mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
0be56a0abf
@ -7,15 +7,15 @@ The docker images are build locally on the developer machine:
|
||||
```sh
|
||||
cd .circleci/docker/
|
||||
|
||||
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 .
|
||||
docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
|
||||
docker build -t ethereum/solidity-buildpack-deps:ubuntu2004-<revision> -f Dockerfile.ubuntu2004 .
|
||||
docker push ethereum/solidity-buildpack-deps:ubuntu2004-<revision>
|
||||
```
|
||||
|
||||
The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `<image-desc>-docker-image-rev` (e.g., `ubuntu-1904-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ.
|
||||
The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `<image-desc>-docker-image-rev` (e.g., `ubuntu-2004-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ.
|
||||
|
||||
Once the docker image has been built and pushed to Dockerhub, you can find it at:
|
||||
|
||||
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
|
||||
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu2004-<revision>
|
||||
|
||||
where the image tag reflects the target OS and revision to build Solidity and run its tests on.
|
||||
|
||||
@ -24,7 +24,7 @@ where the image tag reflects the target OS and revision to build Solidity and ru
|
||||
```sh
|
||||
cd solidity
|
||||
# Mounts your local solidity directory in docker container for testing
|
||||
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904-<revision> /bin/bash
|
||||
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu2004-<revision> /bin/bash
|
||||
cd /src/solidity
|
||||
<commands_to_test_build_with_new_docker_image>
|
||||
```
|
||||
|
@ -10,12 +10,12 @@ parameters:
|
||||
ubuntu-1804-docker-image-rev:
|
||||
type: string
|
||||
default: "4"
|
||||
ubuntu-1904-docker-image-rev:
|
||||
ubuntu-2004-docker-image-rev:
|
||||
type: string
|
||||
default: "4"
|
||||
ubuntu-1904-clang-docker-image-rev:
|
||||
default: "1"
|
||||
ubuntu-2004-clang-docker-image-rev:
|
||||
type: string
|
||||
default: "5"
|
||||
default: "1"
|
||||
ubuntu-1604-clang-ossfuzz-docker-image-rev:
|
||||
type: string
|
||||
default: "2"
|
||||
@ -50,6 +50,7 @@ defaults:
|
||||
cd build
|
||||
protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz
|
||||
protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz
|
||||
protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz
|
||||
cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS
|
||||
make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4
|
||||
|
||||
@ -97,6 +98,7 @@ defaults:
|
||||
- test/tools/ossfuzz/strictasm_opt_ossfuzz
|
||||
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
|
||||
- test/tools/ossfuzz/yul_proto_ossfuzz
|
||||
- test/tools/ossfuzz/sol_proto_ossfuzz
|
||||
|
||||
# test result output directory
|
||||
- artifacts_test_results: &artifacts_test_results
|
||||
@ -137,9 +139,9 @@ defaults:
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
- test_ubuntu1904_clang: &test_ubuntu1904_clang
|
||||
- test_ubuntu2004_clang: &test_ubuntu2004_clang
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >>
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >>
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -148,9 +150,9 @@ defaults:
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
- test_ubuntu1904: &test_ubuntu1904
|
||||
- test_ubuntu2004: &test_ubuntu2004
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -160,7 +162,7 @@ defaults:
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
- test_asan: &test_asan
|
||||
<<: *test_ubuntu1904
|
||||
<<: *test_ubuntu2004
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -179,7 +181,7 @@ defaults:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
||||
- workflow_ubuntu1904: &workflow_ubuntu1904
|
||||
- workflow_ubuntu2004: &workflow_ubuntu2004
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_ubu
|
||||
@ -189,17 +191,17 @@ defaults:
|
||||
requires:
|
||||
- b_ubu_ossfuzz
|
||||
|
||||
- workflow_ubuntu1904_clang: &workflow_ubuntu1904_clang
|
||||
- workflow_ubuntu2004_clang: &workflow_ubuntu2004_clang
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_ubu_clang
|
||||
|
||||
- workflow_ubuntu1904_release: &workflow_ubuntu1904_release
|
||||
- workflow_ubuntu2004_release: &workflow_ubuntu2004_release
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_ubu_release
|
||||
|
||||
- workflow_ubuntu1904_codecov: &workflow_ubuntu1904_codecov
|
||||
- workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_ubu_codecov
|
||||
@ -209,7 +211,7 @@ defaults:
|
||||
requires:
|
||||
- b_osx
|
||||
|
||||
- workflow_ubuntu1904_asan: &workflow_ubuntu1904_asan
|
||||
- workflow_ubuntu2004_asan: &workflow_ubuntu2004_asan
|
||||
<<: *workflow_trigger_on_tags
|
||||
requires:
|
||||
- b_ubu_asan
|
||||
@ -359,16 +361,16 @@ jobs:
|
||||
|
||||
chk_docs_pragma_min_version:
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_docs_pragma_min_version
|
||||
|
||||
b_ubu_clang: &build_ubuntu1904_clang
|
||||
b_ubu_clang: &build_ubuntu2004_clang
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >>
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >>
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
@ -378,9 +380,9 @@ jobs:
|
||||
- store_artifacts: *artifacts_solc
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
|
||||
b_ubu: &build_ubuntu1904
|
||||
b_ubu: &build_ubuntu2004
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
@ -388,8 +390,8 @@ jobs:
|
||||
- store_artifacts: *artifacts_tools
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
|
||||
b_ubu_release: &build_ubuntu1904_release
|
||||
<<: *build_ubuntu1904
|
||||
b_ubu_release: &build_ubuntu2004_release
|
||||
<<: *build_ubuntu2004
|
||||
environment:
|
||||
FORCE_RELEASE: ON
|
||||
|
||||
@ -406,7 +408,7 @@ jobs:
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
|
||||
b_ubu_codecov:
|
||||
<<: *build_ubuntu1904
|
||||
<<: *build_ubuntu2004
|
||||
environment:
|
||||
COVERAGE: ON
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
@ -416,7 +418,7 @@ jobs:
|
||||
- persist_to_workspace: *artifacts_build_dir
|
||||
|
||||
t_ubu_codecov:
|
||||
<<: *test_ubuntu1904
|
||||
<<: *test_ubuntu2004
|
||||
environment:
|
||||
EVM: constantinople
|
||||
OPTIMIZE: 1
|
||||
@ -439,7 +441,7 @@ jobs:
|
||||
# Builds in C++20 mode and uses debug build in order to speed up.
|
||||
# Do *NOT* store any artifacts or workspace as we don't run tests on this build.
|
||||
b_ubu_cxx20:
|
||||
<<: *build_ubuntu1904
|
||||
<<: *build_ubuntu2004
|
||||
environment:
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF
|
||||
@ -591,7 +593,7 @@ jobs:
|
||||
|
||||
# x64 ASAN build, for testing for memory related bugs
|
||||
b_ubu_asan: &b_ubu_asan
|
||||
<<: *build_ubuntu1904
|
||||
<<: *build_ubuntu2004
|
||||
environment:
|
||||
CMAKE_OPTIONS: -DSANITIZE=address
|
||||
CMAKE_BUILD_TYPE: Release
|
||||
@ -603,7 +605,7 @@ jobs:
|
||||
|
||||
b_docs:
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||
steps:
|
||||
- checkout
|
||||
- run: *setup_prerelease_commit_hash
|
||||
@ -615,10 +617,27 @@ jobs:
|
||||
destination: docs-html
|
||||
|
||||
t_ubu_soltest: &t_ubu_soltest
|
||||
<<: *test_ubuntu1904
|
||||
<<: *test_ubuntu2004
|
||||
|
||||
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||
environment:
|
||||
EVM: constantinople
|
||||
SOLTEST_FLAGS: --enforce-via-yul
|
||||
OPTIMIZE: 0
|
||||
TERM: xterm
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: build
|
||||
- run: *run_soltest
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
|
||||
t_ubu_clang_soltest: &t_ubu_clang_soltest
|
||||
<<: *test_ubuntu1904_clang
|
||||
<<: *test_ubuntu2004_clang
|
||||
environment:
|
||||
EVM: constantinople
|
||||
OPTIMIZE: 0
|
||||
@ -628,7 +647,7 @@ jobs:
|
||||
|
||||
t_ubu_cli: &t_ubu_cli
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
@ -816,20 +835,21 @@ workflows:
|
||||
# Ubuntu build and tests
|
||||
- b_ubu: *workflow_trigger_on_tags
|
||||
- b_ubu18: *workflow_trigger_on_tags
|
||||
- t_ubu_cli: *workflow_ubuntu1904
|
||||
- t_ubu_soltest: *workflow_ubuntu1904
|
||||
- t_ubu_cli: *workflow_ubuntu2004
|
||||
- t_ubu_soltest: *workflow_ubuntu2004
|
||||
- t_ubu_soltest_enforce_yul: *workflow_ubuntu2004
|
||||
- b_ubu_clang: *workflow_trigger_on_tags
|
||||
- t_ubu_clang_soltest: *workflow_ubuntu1904_clang
|
||||
- t_ubu_clang_soltest: *workflow_ubuntu2004_clang
|
||||
|
||||
# Ubuntu fake release build and tests
|
||||
- b_ubu_release: *workflow_trigger_on_tags
|
||||
- t_ubu_release_cli: *workflow_ubuntu1904_release
|
||||
- t_ubu_release_soltest: *workflow_ubuntu1904_release
|
||||
- t_ubu_release_cli: *workflow_ubuntu2004_release
|
||||
- t_ubu_release_soltest: *workflow_ubuntu2004_release
|
||||
|
||||
# ASan build and tests
|
||||
- b_ubu_asan: *workflow_trigger_on_tags
|
||||
- t_ubu_asan_constantinople: *workflow_ubuntu1904_asan
|
||||
- t_ubu_asan_cli: *workflow_ubuntu1904_asan
|
||||
- t_ubu_asan_constantinople: *workflow_ubuntu2004_asan
|
||||
- t_ubu_asan_cli: *workflow_ubuntu2004_asan
|
||||
|
||||
# Emscripten build and selected tests
|
||||
- b_ems: *workflow_trigger_on_tags
|
||||
@ -856,4 +876,4 @@ workflows:
|
||||
|
||||
# Code Coverage enabled build and tests
|
||||
- b_ubu_codecov: *workflow_trigger_on_tags
|
||||
- t_ubu_codecov: *workflow_ubuntu1904_codecov
|
||||
- t_ubu_codecov: *workflow_ubuntu2004_codecov
|
||||
|
@ -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
|
@ -21,59 +21,26 @@
|
||||
#
|
||||
# (c) 2016-2019 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
FROM buildpack-deps:disco AS base
|
||||
FROM buildpack-deps:focal AS base
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN set -ex; \
|
||||
dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \
|
||||
echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \
|
||||
apt-get update; \
|
||||
apt-get install -qqy --no-install-recommends \
|
||||
build-essential \
|
||||
software-properties-common \
|
||||
cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \
|
||||
cmake ninja-build \
|
||||
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
|
||||
libboost-program-options-dev \
|
||||
libjsoncpp-dev \
|
||||
llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \
|
||||
libcvc4-dev z3 libz3-dev \
|
||||
; \
|
||||
apt-get install -qy python-pip python-sphinx; \
|
||||
update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \
|
||||
pip install codecov; \
|
||||
apt-get install -qy python3-pip python3-sphinx; \
|
||||
pip3 install codecov; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
FROM base AS libraries
|
||||
|
||||
# OSSFUZZ: libprotobuf-mutator
|
||||
RUN set -ex; \
|
||||
git clone https://github.com/google/libprotobuf-mutator.git \
|
||||
/usr/src/libprotobuf-mutator; \
|
||||
cd /usr/src/libprotobuf-mutator; \
|
||||
git checkout d1fe8a7d8ae18f3d454f055eba5213c291986f21; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \
|
||||
-DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr"; \
|
||||
ninja; \
|
||||
cp -vpr external.protobuf/bin/* /usr/bin/; \
|
||||
cp -vpr external.protobuf/include/* /usr/include/; \
|
||||
cp -vpr external.protobuf/lib/* /usr/lib/; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/libprotobuf-mutator
|
||||
|
||||
# OSSFUZZ: libfuzzer
|
||||
RUN set -ex; \
|
||||
cd /var/tmp; \
|
||||
svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer libfuzzer; \
|
||||
mkdir -p build-libfuzzer; \
|
||||
cd build-libfuzzer; \
|
||||
clang++-8 -O1 -stdlib=libstdc++ -std=c++11 -O2 -fPIC -c ../libfuzzer/*.cpp -I../libfuzzer; \
|
||||
ar r /usr/lib/libFuzzingEngine.a *.o; \
|
||||
rm -rf /var/lib/libfuzzer
|
||||
|
||||
# EVMONE
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
@ -81,7 +48,6 @@ RUN set -ex; \
|
||||
cd evmone; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
# isoltest links against the evmone shared library
|
||||
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
61
.circleci/docker/Dockerfile.ubuntu2004.clang
Normal file
61
.circleci/docker/Dockerfile.ubuntu2004.clang
Normal 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
|
@ -57,7 +57,7 @@ get_logfile_basename() {
|
||||
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml"
|
||||
SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
|
||||
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
|
||||
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul"
|
||||
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2"
|
||||
|
||||
echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS} -- ${SOLTEST_ARGS}"
|
||||
|
||||
|
14
Changelog.md
14
Changelog.md
@ -16,17 +16,25 @@ Bugfixes:
|
||||
|
||||
### 0.6.8 (unreleased)
|
||||
|
||||
Language Features:
|
||||
Important Bugfixes:
|
||||
* Add missing callvalue check to the creation code of a contract that does not define a constructor but has a base that does define a constructor.
|
||||
|
||||
|
||||
Language Features:
|
||||
* Implemented ``type(X).min`` and ``type(X).max`` for every integer type ``X`` that returns the smallest and largest value representable by the type.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode.
|
||||
|
||||
|
||||
|
||||
Bugfixes:
|
||||
|
||||
|
||||
* ABI: Skip ``private`` or ``internal`` constructors.
|
||||
* Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables.
|
||||
* Fixed an "Assembly Exception in Bytecode" error where requested functions were generated twice.
|
||||
* Natspec: Fixed a bug that ignored ``@return`` tag when no other developer-documentation tags were present.
|
||||
* Yul assembler: Fix source location of variable declarations without value.
|
||||
|
||||
|
||||
### 0.6.7 (2020-05-04)
|
||||
|
@ -1,4 +1,12 @@
|
||||
[
|
||||
{
|
||||
"name": "ImplicitConstructorCallvalueCheck",
|
||||
"summary": "The creation code of a contract that does not define a constructor but has a base that does define a constructor did not revert for calls with non-zero value.",
|
||||
"description": "Starting from Solidity 0.4.5 the creation code of contracts without explicit payable constructor is supposed to contain a callvalue check that results in contract creation reverting, if non-zero value is passed. However, this check was missing in case no explicit constructor was defined in a contract at all, but the contract has a base that does define a constructor. In these cases it is possible to send value in a contract creation transaction or using inline assembly without revert, even though the creation code is supposed to be non-payable.",
|
||||
"introduced": "0.4.5",
|
||||
"fixed": "0.6.8",
|
||||
"severity": "very low"
|
||||
},
|
||||
{
|
||||
"name": "TupleAssignmentMultiStackSlotComponents",
|
||||
"summary": "Tuple assignments with components that occupy several stack slots, i.e. nested tuples, pointers to external functions or references to dynamically sized calldata arrays, can result in invalid values.",
|
||||
|
@ -415,6 +415,7 @@
|
||||
},
|
||||
"0.4.10": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -433,6 +434,7 @@
|
||||
},
|
||||
"0.4.11": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -450,6 +452,7 @@
|
||||
},
|
||||
"0.4.12": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -466,6 +469,7 @@
|
||||
},
|
||||
"0.4.13": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -482,6 +486,7 @@
|
||||
},
|
||||
"0.4.14": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -497,6 +502,7 @@
|
||||
},
|
||||
"0.4.15": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -511,6 +517,7 @@
|
||||
},
|
||||
"0.4.16": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -527,6 +534,7 @@
|
||||
},
|
||||
"0.4.17": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -544,6 +552,7 @@
|
||||
},
|
||||
"0.4.18": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -560,6 +569,7 @@
|
||||
},
|
||||
"0.4.19": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -596,6 +606,7 @@
|
||||
},
|
||||
"0.4.20": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -613,6 +624,7 @@
|
||||
},
|
||||
"0.4.21": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -630,6 +642,7 @@
|
||||
},
|
||||
"0.4.22": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -647,6 +660,7 @@
|
||||
},
|
||||
"0.4.23": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -663,6 +677,7 @@
|
||||
},
|
||||
"0.4.24": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -679,6 +694,7 @@
|
||||
},
|
||||
"0.4.25": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -693,6 +709,7 @@
|
||||
},
|
||||
"0.4.26": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -739,6 +756,7 @@
|
||||
},
|
||||
"0.4.5": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -758,6 +776,7 @@
|
||||
},
|
||||
"0.4.6": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -776,6 +795,7 @@
|
||||
},
|
||||
"0.4.7": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -794,6 +814,7 @@
|
||||
},
|
||||
"0.4.8": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -812,6 +833,7 @@
|
||||
},
|
||||
"0.4.9": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -830,6 +852,7 @@
|
||||
},
|
||||
"0.5.0": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -844,6 +867,7 @@
|
||||
},
|
||||
"0.5.1": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -858,6 +882,7 @@
|
||||
},
|
||||
"0.5.10": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -868,6 +893,7 @@
|
||||
},
|
||||
"0.5.11": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -877,6 +903,7 @@
|
||||
},
|
||||
"0.5.12": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -886,6 +913,7 @@
|
||||
},
|
||||
"0.5.13": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -895,6 +923,7 @@
|
||||
},
|
||||
"0.5.14": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -905,6 +934,7 @@
|
||||
},
|
||||
"0.5.15": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -914,6 +944,7 @@
|
||||
},
|
||||
"0.5.16": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden"
|
||||
@ -922,6 +953,7 @@
|
||||
},
|
||||
"0.5.17": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow"
|
||||
],
|
||||
@ -929,6 +961,7 @@
|
||||
},
|
||||
"0.5.2": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -943,6 +976,7 @@
|
||||
},
|
||||
"0.5.3": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -957,6 +991,7 @@
|
||||
},
|
||||
"0.5.4": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -971,6 +1006,7 @@
|
||||
},
|
||||
"0.5.5": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -987,6 +1023,7 @@
|
||||
},
|
||||
"0.5.6": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -1003,6 +1040,7 @@
|
||||
},
|
||||
"0.5.7": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -1017,6 +1055,7 @@
|
||||
},
|
||||
"0.5.8": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -1030,6 +1069,7 @@
|
||||
},
|
||||
"0.5.9": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"privateCanBeOverridden",
|
||||
@ -1042,6 +1082,7 @@
|
||||
},
|
||||
"0.6.0": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow",
|
||||
"YulOptimizerRedundantAssignmentBreakContinue"
|
||||
@ -1050,6 +1091,7 @@
|
||||
},
|
||||
"0.6.1": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow"
|
||||
],
|
||||
@ -1057,6 +1099,7 @@
|
||||
},
|
||||
"0.6.2": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow"
|
||||
],
|
||||
@ -1064,6 +1107,7 @@
|
||||
},
|
||||
"0.6.3": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow"
|
||||
],
|
||||
@ -1071,6 +1115,7 @@
|
||||
},
|
||||
"0.6.4": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents",
|
||||
"MemoryArrayCreationOverflow"
|
||||
],
|
||||
@ -1078,16 +1123,21 @@
|
||||
},
|
||||
"0.6.5": {
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck",
|
||||
"TupleAssignmentMultiStackSlotComponents"
|
||||
],
|
||||
"released": "2020-04-06"
|
||||
},
|
||||
"0.6.6": {
|
||||
"bugs": [],
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck"
|
||||
],
|
||||
"released": "2020-04-09"
|
||||
},
|
||||
"0.6.7": {
|
||||
"bugs": [],
|
||||
"bugs": [
|
||||
"ImplicitConstructorCallvalueCheck"
|
||||
],
|
||||
"released": "2020-05-04"
|
||||
}
|
||||
}
|
@ -121,6 +121,8 @@ Global Variables
|
||||
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||
- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||
|
||||
.. note::
|
||||
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
|
||||
|
@ -40,6 +40,9 @@ Operators:
|
||||
* Shift operators: ``<<`` (left shift), ``>>`` (right shift)
|
||||
* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation)
|
||||
|
||||
For an integer type ``X``, you can use ``type(X).min`` and ``type(X).max`` to
|
||||
access the minimum and maximum value representable by the type.
|
||||
|
||||
.. warning::
|
||||
|
||||
Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``.
|
||||
|
@ -296,10 +296,11 @@ Furthermore, all functions of the current contract are callable directly includi
|
||||
Type Information
|
||||
----------------
|
||||
|
||||
The expression ``type(X)`` can be used to retrieve information about the
|
||||
type ``X``. Currently, there is limited support for this feature, but
|
||||
it might be expanded in the future. The following properties are
|
||||
available for a contract type ``C``:
|
||||
The expression ``type(X)`` can be used to retrieve information about the type
|
||||
``X``. Currently, there is limited support for this feature (``X`` can be either
|
||||
a contract or an integer type) but it might be expanded in the future.
|
||||
|
||||
The following properties are available for a contract type ``C``:
|
||||
|
||||
``type(C).name``
|
||||
The name of the contract.
|
||||
@ -329,3 +330,10 @@ for an interface type ``I``:
|
||||
A ``bytes4`` value containing the `EIP-165 <https://eips.ethereum.org/EIPS/eip-165>`_
|
||||
interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all
|
||||
function selectors defined within the interface itself - excluding all inherited functions.
|
||||
The following properties are available for an integer type ``T``:
|
||||
|
||||
``type(T).min``
|
||||
The smallest value representable by type ``T``.
|
||||
|
||||
``type(T).max``
|
||||
The largest value representable by type ``T``.
|
||||
|
263
docs/yul.rst
263
docs/yul.rst
@ -104,6 +104,8 @@ less-than comparison.
|
||||
}
|
||||
}
|
||||
|
||||
At the :ref:`end of the section <erc20yul>`, a complete implementation of
|
||||
the ERC-20 standard can be found.
|
||||
|
||||
|
||||
|
||||
@ -1039,14 +1041,16 @@ Optimization step sequence
|
||||
--------------------------
|
||||
|
||||
By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly.
|
||||
You can override this sequence and supply your own using the `--yul-optimizations` option when compiling
|
||||
in Solidity mode:
|
||||
You can override this sequence and supply your own using the ``--yul-optimizations`` option:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
|
||||
|
||||
By enclosing part of the sequence in square brackets (`[]`) you tell the optimizer to repeatedly
|
||||
The order of steps is significant and affects the quality of the output.
|
||||
Moreover, applying a step may uncover new optimization opportunities for others that were already
|
||||
applied so repeating steps is often beneficial.
|
||||
By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly
|
||||
apply that part until it no longer improves the size of the resulting assembly.
|
||||
You can use brackets multiple times in a single sequence but they cannot be nested.
|
||||
|
||||
@ -1055,35 +1059,230 @@ The following optimization steps are available:
|
||||
============ ===============================
|
||||
Abbreviation Full name
|
||||
============ ===============================
|
||||
f `BlockFlattener`
|
||||
l `CircularReferencesPruner`
|
||||
c `CommonSubexpressionEliminator`
|
||||
C `ConditionalSimplifier`
|
||||
U `ConditionalUnsimplifier`
|
||||
n `ControlFlowSimplifier`
|
||||
D `DeadCodeEliminator`
|
||||
v `EquivalentFunctionCombiner`
|
||||
e `ExpressionInliner`
|
||||
j `ExpressionJoiner`
|
||||
s `ExpressionSimplifier`
|
||||
x `ExpressionSplitter`
|
||||
I `ForLoopConditionIntoBody`
|
||||
O `ForLoopConditionOutOfBody`
|
||||
o `ForLoopInitRewriter`
|
||||
i `FullInliner`
|
||||
g `FunctionGrouper`
|
||||
h `FunctionHoister`
|
||||
T `LiteralRematerialiser`
|
||||
L `LoadResolver`
|
||||
M `LoopInvariantCodeMotion`
|
||||
r `RedundantAssignEliminator`
|
||||
m `Rematerialiser`
|
||||
V `SSAReverser`
|
||||
a `SSATransform`
|
||||
t `StructuralSimplifier`
|
||||
u `UnusedPruner`
|
||||
d `VarDeclInitializer`
|
||||
``f`` ``BlockFlattener``
|
||||
``l`` ``CircularReferencesPruner``
|
||||
``c`` ``CommonSubexpressionEliminator``
|
||||
``C`` ``ConditionalSimplifier``
|
||||
``U`` ``ConditionalUnsimplifier``
|
||||
``n`` ``ControlFlowSimplifier``
|
||||
``D`` ``DeadCodeEliminator``
|
||||
``v`` ``EquivalentFunctionCombiner``
|
||||
``e`` ``ExpressionInliner``
|
||||
``j`` ``ExpressionJoiner``
|
||||
``s`` ``ExpressionSimplifier``
|
||||
``x`` ``ExpressionSplitter``
|
||||
``I`` ``ForLoopConditionIntoBody``
|
||||
``O`` ``ForLoopConditionOutOfBody``
|
||||
``o`` ``ForLoopInitRewriter``
|
||||
``i`` ``FullInliner``
|
||||
``g`` ``FunctionGrouper``
|
||||
``h`` ``FunctionHoister``
|
||||
``T`` ``LiteralRematerialiser``
|
||||
``L`` ``LoadResolver``
|
||||
``M`` ``LoopInvariantCodeMotion``
|
||||
``r`` ``RedundantAssignEliminator``
|
||||
``m`` ``Rematerialiser``
|
||||
``V`` ``SSAReverser``
|
||||
``a`` ``SSATransform``
|
||||
``t`` ``StructuralSimplifier``
|
||||
``u`` ``UnusedPruner``
|
||||
``d`` ``VarDeclInitializer``
|
||||
============ ===============================
|
||||
|
||||
Some steps depend on properties ensured by `BlockFlattener`, `FunctionGrouper`, `ForLoopInitRewriter`.
|
||||
Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``.
|
||||
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
|
||||
|
||||
|
||||
.. _erc20yul:
|
||||
|
||||
Complete ERC20 Example
|
||||
======================
|
||||
|
||||
.. code-block:: yul
|
||||
|
||||
object "Token" {
|
||||
code {
|
||||
// Store the creator in slot zero.
|
||||
sstore(0, caller())
|
||||
|
||||
// Deploy the contract
|
||||
datacopy(0, dataoffset("runtime"), datasize("runtime"))
|
||||
return(0, datasize("runtime"))
|
||||
}
|
||||
object "runtime" {
|
||||
code {
|
||||
// Protection against sending Ether
|
||||
require(iszero(callvalue()))
|
||||
|
||||
// Dispatcher
|
||||
switch selector()
|
||||
case 0x70a08231 /* "balanceOf(address)" */ {
|
||||
returnUint(balanceOf(decodeAsAddress(0)))
|
||||
}
|
||||
case 0x18160ddd /* "totalSupply()" */ {
|
||||
returnUint(totalSupply())
|
||||
}
|
||||
case 0xa9059cbb /* "transfer(address,uint256)" */ {
|
||||
transfer(decodeAsAddress(0), decodeAsUint(1))
|
||||
returnTrue()
|
||||
}
|
||||
case 0x23b872dd /* "transferFrom(address,address,uint256)" */ {
|
||||
transferFrom(decodeAsAddress(0), decodeAsAddress(1), decodeAsUint(2))
|
||||
returnTrue()
|
||||
}
|
||||
case 0x095ea7b3 /* "approve(address,uint256)" */ {
|
||||
approve(decodeAsAddress(0), decodeAsUint(1))
|
||||
returnTrue()
|
||||
}
|
||||
case 0xdd62ed3e /* "allowance(address,address)" */ {
|
||||
returnUint(allowance(decodeAsAddress(0), decodeAsAddress(1)))
|
||||
}
|
||||
case 0x40c10f19 /* "mint(address,uint256)" */ {
|
||||
mint(decodeAsAddress(0), decodeAsUint(1))
|
||||
returnTrue()
|
||||
}
|
||||
default {
|
||||
revert(0, 0)
|
||||
}
|
||||
|
||||
function mint(account, amount) {
|
||||
require(calledByOwner())
|
||||
|
||||
mintTokens(amount)
|
||||
addToBalance(account, amount)
|
||||
emitTransfer(0, account, amount)
|
||||
}
|
||||
function transfer(to, amount) {
|
||||
executeTransfer(caller(), to, amount)
|
||||
}
|
||||
function approve(spender, amount) {
|
||||
revertIfZeroAddress(spender)
|
||||
setAllowance(caller(), spender, amount)
|
||||
emitApproval(caller(), spender, amount)
|
||||
}
|
||||
function transferFrom(from, to, amount) {
|
||||
decreaseAllowanceBy(from, caller(), amount)
|
||||
executeTransfer(from, to, amount)
|
||||
}
|
||||
|
||||
function executeTransfer(from, to, amount) {
|
||||
revertIfZeroAddress(to)
|
||||
deductFromBalance(from, amount)
|
||||
addToBalance(to, amount)
|
||||
emitTransfer(from, to, amount)
|
||||
}
|
||||
|
||||
|
||||
/* ---------- calldata decoding functions ----------- */
|
||||
function selector() -> s {
|
||||
s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000)
|
||||
}
|
||||
|
||||
function decodeAsAddress(offset) -> v {
|
||||
v := decodeAsUint(offset)
|
||||
if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) {
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
function decodeAsUint(offset) -> v {
|
||||
let pos := add(4, mul(offset, 0x20))
|
||||
if lt(calldatasize(), add(pos, 0x20)) {
|
||||
revert(0, 0)
|
||||
}
|
||||
v := calldataload(pos)
|
||||
}
|
||||
/* ---------- calldata encoding functions ---------- */
|
||||
function returnUint(v) {
|
||||
mstore(0, v)
|
||||
return(0, 0x20)
|
||||
}
|
||||
function returnTrue() {
|
||||
returnUint(1)
|
||||
}
|
||||
|
||||
/* -------- events ---------- */
|
||||
function emitTransfer(from, to, amount) {
|
||||
let signatureHash := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
|
||||
emitEvent(signatureHash, from, to, amount)
|
||||
}
|
||||
function emitApproval(from, spender, amount) {
|
||||
let signatureHash := 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
|
||||
emitEvent(signatureHash, from, spender, amount)
|
||||
}
|
||||
function emitEvent(signatureHash, indexed1, indexed2, nonIndexed) {
|
||||
mstore(0, nonIndexed)
|
||||
log3(0, 0x20, signatureHash, indexed1, indexed2)
|
||||
}
|
||||
|
||||
/* -------- storage layout ---------- */
|
||||
function ownerPos() -> p { p := 0 }
|
||||
function totalSupplyPos() -> p { p := 1 }
|
||||
function accountToStorageOffset(account) -> offset {
|
||||
offset := add(0x1000, account)
|
||||
}
|
||||
function allowanceStorageOffset(account, spender) -> offset {
|
||||
offset := accountToStorageOffset(account)
|
||||
mstore(0, offset)
|
||||
mstore(0x20, spender)
|
||||
offset := keccak256(0, 0x40)
|
||||
}
|
||||
|
||||
/* -------- storage access ---------- */
|
||||
function owner() -> o {
|
||||
o := sload(ownerPos())
|
||||
}
|
||||
function totalSupply() -> supply {
|
||||
supply := sload(totalSupplyPos())
|
||||
}
|
||||
function mintTokens(amount) {
|
||||
sstore(totalSupplyPos(), safeAdd(totalSupply(), amount))
|
||||
}
|
||||
function balanceOf(account) -> bal {
|
||||
bal := sload(accountToStorageOffset(account))
|
||||
}
|
||||
function addToBalance(account, amount) {
|
||||
let offset := accountToStorageOffset(account)
|
||||
sstore(offset, safeAdd(sload(offset), amount))
|
||||
}
|
||||
function deductFromBalance(account, amount) {
|
||||
let offset := accountToStorageOffset(account)
|
||||
let bal := sload(offset)
|
||||
require(lte(amount, bal))
|
||||
sstore(offset, sub(bal, amount))
|
||||
}
|
||||
function allowance(account, spender) -> amount {
|
||||
amount := sload(allowanceStorageOffset(account, spender))
|
||||
}
|
||||
function setAllowance(account, spender, amount) {
|
||||
sstore(allowanceStorageOffset(account, spender), amount)
|
||||
}
|
||||
function decreaseAllowanceBy(account, spender, amount) {
|
||||
let offset := allowanceStorageOffset(account, spender)
|
||||
let currentAllowance := sload(offset)
|
||||
require(lte(amount, currentAllowance))
|
||||
sstore(offset, sub(currentAllowance, amount))
|
||||
}
|
||||
|
||||
/* ---------- utility functions ---------- */
|
||||
function lte(a, b) -> r {
|
||||
r := iszero(gt(a, b))
|
||||
}
|
||||
function gte(a, b) -> r {
|
||||
r := iszero(lt(a, b))
|
||||
}
|
||||
function safeAdd(a, b) -> r {
|
||||
r := add(a, b)
|
||||
if or(lt(r, a), lt(r, b)) { revert(0, 0) }
|
||||
}
|
||||
function calledByOwner() -> cbo {
|
||||
cbo := eq(owner(), caller())
|
||||
}
|
||||
function revertIfZeroAddress(addr) {
|
||||
require(addr)
|
||||
}
|
||||
function require(condition) {
|
||||
if iszero(condition) { revert(0, 0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,9 +26,6 @@
|
||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||
#include <libevmasm/SimplificationRules.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
@ -29,9 +29,6 @@
|
||||
#include <libevmasm/RuleList.h>
|
||||
#include <libsolutil/Assertions.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
|
||||
|
@ -28,6 +28,8 @@ using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
ErrorId solidity::langutil::operator"" _error(unsigned long long _error) { return ErrorId{ _error }; }
|
||||
|
||||
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
|
||||
{
|
||||
if (&_errorReporter == this)
|
||||
@ -36,30 +38,31 @@ ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void ErrorReporter::warning(string const& _description)
|
||||
void ErrorReporter::warning(ErrorId _error, string const& _description)
|
||||
{
|
||||
error(Error::Type::Warning, SourceLocation(), _description);
|
||||
error(_error, Error::Type::Warning, SourceLocation(), _description);
|
||||
}
|
||||
|
||||
void ErrorReporter::warning(
|
||||
ErrorId _error,
|
||||
SourceLocation const& _location,
|
||||
string const& _description
|
||||
)
|
||||
{
|
||||
error(Error::Type::Warning, _location, _description);
|
||||
error(_error, Error::Type::Warning, _location, _description);
|
||||
}
|
||||
|
||||
void ErrorReporter::warning(
|
||||
ErrorId _error,
|
||||
SourceLocation const& _location,
|
||||
string const& _description,
|
||||
SecondarySourceLocation const& _secondaryLocation
|
||||
)
|
||||
{
|
||||
error(Error::Type::Warning, _location, _secondaryLocation, _description);
|
||||
error(_error, Error::Type::Warning, _location, _secondaryLocation, _description);
|
||||
}
|
||||
|
||||
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
if (checkForExcessiveErrors(_type))
|
||||
return;
|
||||
@ -72,7 +75,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st
|
||||
m_errorList.push_back(err);
|
||||
}
|
||||
|
||||
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
{
|
||||
if (checkForExcessiveErrors(_type))
|
||||
return;
|
||||
@ -123,15 +126,15 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
{
|
||||
error(_type, _location, _secondaryLocation, _description);
|
||||
error(_error, _type, _location, _secondaryLocation, _description);
|
||||
BOOST_THROW_EXCEPTION(FatalError());
|
||||
}
|
||||
|
||||
void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
error(_type, _location, _description);
|
||||
error(_error, _type, _location, _description);
|
||||
BOOST_THROW_EXCEPTION(FatalError());
|
||||
}
|
||||
|
||||
@ -145,9 +148,10 @@ void ErrorReporter::clear()
|
||||
m_errorList.clear();
|
||||
}
|
||||
|
||||
void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::DeclarationError,
|
||||
_location,
|
||||
_secondaryLocation,
|
||||
@ -155,53 +159,59 @@ void ErrorReporter::declarationError(SourceLocation const& _location, SecondaryS
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::declarationError(SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::DeclarationError,
|
||||
_location,
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description)
|
||||
void ErrorReporter::fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description)
|
||||
{
|
||||
fatalError(
|
||||
_error,
|
||||
Error::Type::DeclarationError,
|
||||
_location,
|
||||
_description);
|
||||
}
|
||||
|
||||
void ErrorReporter::parserError(SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::ParserError,
|
||||
_location,
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
fatalError(
|
||||
_error,
|
||||
Error::Type::ParserError,
|
||||
_location,
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::syntaxError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::SyntaxError,
|
||||
_location,
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::TypeError,
|
||||
_location,
|
||||
_secondaryLocation,
|
||||
@ -209,18 +219,21 @@ void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLo
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::typeError(SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::TypeError,
|
||||
_location,
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
|
||||
void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||
{
|
||||
fatalError(
|
||||
_error,
|
||||
Error::Type::TypeError,
|
||||
_location,
|
||||
_secondaryLocation,
|
||||
@ -228,26 +241,30 @@ void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySou
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
fatalError(Error::Type::TypeError,
|
||||
fatalError(
|
||||
_error,
|
||||
Error::Type::TypeError,
|
||||
_location,
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::docstringParsingError(string const& _description)
|
||||
void ErrorReporter::docstringParsingError(ErrorId _error, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::DocstringParsingError,
|
||||
SourceLocation(),
|
||||
_description
|
||||
);
|
||||
}
|
||||
|
||||
void ErrorReporter::docstringParsingError(SourceLocation const& _location, string const& _description)
|
||||
void ErrorReporter::docstringParsingError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
error(
|
||||
_error,
|
||||
Error::Type::DocstringParsingError,
|
||||
_location,
|
||||
_description
|
||||
|
@ -33,6 +33,17 @@
|
||||
namespace solidity::langutil
|
||||
{
|
||||
|
||||
/**
|
||||
* Unique identifiers are used to tag and track individual error cases.
|
||||
* They are passed as the first parameter of error reporting functions.
|
||||
* Suffix _error helps to find them in the sources.
|
||||
* The struct ErrorId prevents incidental calls like typeError(3141) instead of typeError(3141_error).
|
||||
* To create a new ID, one can add 0000_error and then run "python ./scripts/correct_error_ids.py"
|
||||
* from the root of the repo.
|
||||
*/
|
||||
struct ErrorId { unsigned long long error = 0; };
|
||||
ErrorId operator"" _error(unsigned long long error);
|
||||
|
||||
class ErrorReporter
|
||||
{
|
||||
public:
|
||||
@ -50,64 +61,68 @@ public:
|
||||
m_errorList += _errorList;
|
||||
}
|
||||
|
||||
void warning(std::string const& _description);
|
||||
void warning(ErrorId _error, std::string const& _description);
|
||||
|
||||
void warning(SourceLocation const& _location, std::string const& _description);
|
||||
void warning(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void warning(
|
||||
ErrorId _error,
|
||||
SourceLocation const& _location,
|
||||
std::string const& _description,
|
||||
SecondarySourceLocation const& _secondaryLocation
|
||||
);
|
||||
|
||||
void error(
|
||||
ErrorId _error,
|
||||
Error::Type _type,
|
||||
SourceLocation const& _location,
|
||||
std::string const& _description
|
||||
);
|
||||
|
||||
void declarationError(
|
||||
ErrorId _error,
|
||||
SourceLocation const& _location,
|
||||
SecondarySourceLocation const& _secondaryLocation,
|
||||
std::string const& _description
|
||||
);
|
||||
|
||||
void declarationError(SourceLocation const& _location, std::string const& _description);
|
||||
void declarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
|
||||
void fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void parserError(SourceLocation const& _location, std::string const& _description);
|
||||
void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void fatalParserError(SourceLocation const& _location, std::string const& _description);
|
||||
void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void syntaxError(SourceLocation const& _location, std::string const& _description);
|
||||
void syntaxError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
void typeError(
|
||||
ErrorId _error,
|
||||
SourceLocation const& _location,
|
||||
SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
|
||||
std::string const& _description = std::string()
|
||||
);
|
||||
|
||||
void typeError(SourceLocation const& _location, std::string const& _description);
|
||||
void typeError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
template <typename... Strings>
|
||||
void typeErrorConcatenateDescriptions(SourceLocation const& _location, Strings const&... _descriptions)
|
||||
void typeErrorConcatenateDescriptions(ErrorId _error, SourceLocation const& _location, Strings const&... _descriptions)
|
||||
{
|
||||
std::initializer_list<std::string> const descs = {_descriptions...};
|
||||
std::initializer_list<std::string> const descs = { _descriptions... };
|
||||
solAssert(descs.size() > 0, "Need error descriptions!");
|
||||
|
||||
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
|
||||
|
||||
std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " ");
|
||||
|
||||
error(Error::Type::TypeError, _location, errorStr);
|
||||
error(_error, Error::Type::TypeError, _location, errorStr);
|
||||
}
|
||||
|
||||
void fatalTypeError(SourceLocation const& _location, std::string const& _description);
|
||||
void fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description);
|
||||
void fatalTypeError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
void fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description);
|
||||
|
||||
void docstringParsingError(std::string const& _description);
|
||||
void docstringParsingError(SourceLocation const& _location, std::string const& _description);
|
||||
void docstringParsingError(ErrorId _error, std::string const& _description);
|
||||
void docstringParsingError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
ErrorList const& errors() const;
|
||||
|
||||
@ -124,18 +139,21 @@ public:
|
||||
|
||||
private:
|
||||
void error(
|
||||
ErrorId _error,
|
||||
Error::Type _type,
|
||||
SourceLocation const& _location,
|
||||
SecondarySourceLocation const& _secondaryLocation,
|
||||
std::string const& _description = std::string());
|
||||
|
||||
void fatalError(
|
||||
ErrorId _error,
|
||||
Error::Type _type,
|
||||
SourceLocation const& _location,
|
||||
SecondarySourceLocation const& _secondaryLocation,
|
||||
std::string const& _description = std::string());
|
||||
|
||||
void fatalError(
|
||||
ErrorId _error,
|
||||
Error::Type _type,
|
||||
SourceLocation const& _location = SourceLocation(),
|
||||
std::string const& _description = std::string());
|
||||
|
@ -77,9 +77,9 @@ void ParserBase::expectToken(Token _value, bool _advance)
|
||||
{
|
||||
string const expectedToken = ParserBase::tokenName(_value);
|
||||
if (m_parserErrorRecovery)
|
||||
parserError("Expected " + expectedToken + " but got " + tokenName(tok));
|
||||
parserError(6635_error, "Expected " + expectedToken + " but got " + tokenName(tok));
|
||||
else
|
||||
fatalParserError("Expected " + expectedToken + " but got " + tokenName(tok));
|
||||
fatalParserError(2314_error, "Expected " + expectedToken + " but got " + tokenName(tok));
|
||||
// Do not advance so that recovery can sync or make use of the current token.
|
||||
// This is especially useful if the expected token
|
||||
// is the only one that is missing and is at the end of a construct.
|
||||
@ -108,21 +108,21 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN
|
||||
// rollback to where the token started, and raise exception to be caught at a higher level.
|
||||
m_scanner->setPosition(startPosition);
|
||||
m_inParserRecovery = true;
|
||||
fatalParserError(errorLoc, msg);
|
||||
fatalParserError(1957_error, errorLoc, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_inParserRecovery)
|
||||
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
||||
parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
||||
else
|
||||
parserError(errorLoc, msg + "Recovered at next " + expectedToken);
|
||||
parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken);
|
||||
m_inParserRecovery = false;
|
||||
}
|
||||
}
|
||||
else if (m_inParserRecovery)
|
||||
{
|
||||
string expectedToken = ParserBase::tokenName(_value);
|
||||
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
||||
parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
||||
m_inParserRecovery = false;
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ void ParserBase::increaseRecursionDepth()
|
||||
{
|
||||
m_recursionDepth++;
|
||||
if (m_recursionDepth >= 1200)
|
||||
fatalParserError("Maximum recursion depth reached during parsing.");
|
||||
fatalParserError(7319_error, "Maximum recursion depth reached during parsing.");
|
||||
}
|
||||
|
||||
void ParserBase::decreaseRecursionDepth()
|
||||
@ -143,27 +143,27 @@ void ParserBase::decreaseRecursionDepth()
|
||||
m_recursionDepth--;
|
||||
}
|
||||
|
||||
void ParserBase::parserWarning(string const& _description)
|
||||
void ParserBase::parserWarning(ErrorId _error, string const& _description)
|
||||
{
|
||||
m_errorReporter.warning(currentLocation(), _description);
|
||||
m_errorReporter.warning(_error, currentLocation(), _description);
|
||||
}
|
||||
|
||||
void ParserBase::parserError(SourceLocation const& _location, string const& _description)
|
||||
void ParserBase::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.parserError(_location, _description);
|
||||
m_errorReporter.parserError(_error, _location, _description);
|
||||
}
|
||||
|
||||
void ParserBase::parserError(string const& _description)
|
||||
void ParserBase::parserError(ErrorId _error, string const& _description)
|
||||
{
|
||||
parserError(currentLocation(), _description);
|
||||
parserError(_error, currentLocation(), _description);
|
||||
}
|
||||
|
||||
void ParserBase::fatalParserError(string const& _description)
|
||||
void ParserBase::fatalParserError(ErrorId _error, string const& _description)
|
||||
{
|
||||
fatalParserError(currentLocation(), _description);
|
||||
fatalParserError(_error, currentLocation(), _description);
|
||||
}
|
||||
|
||||
void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description)
|
||||
void ParserBase::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.fatalParserError(_location, _description);
|
||||
m_errorReporter.fatalParserError(_error, _location, _description);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ namespace solidity::langutil
|
||||
|
||||
class ErrorReporter;
|
||||
class Scanner;
|
||||
struct ErrorId;
|
||||
|
||||
class ParserBase
|
||||
{
|
||||
@ -88,17 +89,17 @@ protected:
|
||||
|
||||
/// Creates a @ref ParserError and annotates it with the current position and the
|
||||
/// given @a _description.
|
||||
void parserError(std::string const& _description);
|
||||
void parserError(SourceLocation const& _location, std::string const& _description);
|
||||
void parserError(ErrorId _error, std::string const& _description);
|
||||
void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
/// Creates a @ref ParserWarning and annotates it with the current position and the
|
||||
/// given @a _description.
|
||||
void parserWarning(std::string const& _description);
|
||||
void parserWarning(ErrorId _error, std::string const& _description);
|
||||
|
||||
/// Creates a @ref ParserError and annotates it with the current position and the
|
||||
/// given @a _description. Throws the FatalError.
|
||||
void fatalParserError(std::string const& _description);
|
||||
void fatalParserError(SourceLocation const& _location, std::string const& _description);
|
||||
void fatalParserError(ErrorId _error, std::string const& _description);
|
||||
void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||
|
||||
std::shared_ptr<Scanner> m_scanner;
|
||||
/// The reference to the list of errors and warning to add errors/warnings during parsing
|
||||
|
@ -166,7 +166,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
|
||||
endif()
|
||||
|
||||
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
||||
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::filesystem Boost::system)
|
||||
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost)
|
||||
|
||||
if (${Z3_FOUND})
|
||||
target_link_libraries(solidity PUBLIC z3::libz3)
|
||||
|
@ -29,6 +29,7 @@
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
|
||||
{
|
||||
@ -46,6 +47,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
||||
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
||||
if (!commonType)
|
||||
m_errorReporter.fatalTypeError(
|
||||
6020_error,
|
||||
_operation.location(),
|
||||
"Operator " +
|
||||
string(TokenTraits::toString(_operation.getOperator())) +
|
||||
@ -82,7 +84,7 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier)
|
||||
else if (!m_types->count(value.get()))
|
||||
{
|
||||
if (m_depth > 32)
|
||||
m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
|
||||
m_errorReporter.fatalTypeError(5210_error, _identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
|
||||
ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <libsolidity/analysis/TypeChecker.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
using namespace std;
|
||||
@ -78,6 +77,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
{
|
||||
if (constructor)
|
||||
m_errorReporter.declarationError(
|
||||
7997_error,
|
||||
function->location(),
|
||||
SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
|
||||
"More than one constructor defined."
|
||||
@ -88,6 +88,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
{
|
||||
if (fallback)
|
||||
m_errorReporter.declarationError(
|
||||
7301_error,
|
||||
function->location(),
|
||||
SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
|
||||
"Only one fallback function is allowed."
|
||||
@ -98,6 +99,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
{
|
||||
if (receive)
|
||||
m_errorReporter.declarationError(
|
||||
4046_error,
|
||||
function->location(),
|
||||
SecondarySourceLocation().append("Another declaration is here:", receive->location()),
|
||||
"Only one receive function is allowed."
|
||||
@ -110,7 +112,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
functions[function->name()].push_back(function);
|
||||
}
|
||||
|
||||
findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
|
||||
findDuplicateDefinitions(functions);
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
|
||||
@ -121,11 +123,11 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr
|
||||
for (EventDefinition const* event: _contract.events())
|
||||
events[event->name()].push_back(event);
|
||||
|
||||
findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
|
||||
findDuplicateDefinitions(events);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
|
||||
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions)
|
||||
{
|
||||
for (auto const& it: _definitions)
|
||||
{
|
||||
@ -144,12 +146,27 @@ void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const
|
||||
|
||||
if (ssl.infos.size() > 0)
|
||||
{
|
||||
ssl.limitSize(_message);
|
||||
ErrorId error;
|
||||
string message;
|
||||
if constexpr (is_same_v<T, FunctionDefinition const*>)
|
||||
{
|
||||
error = 1686_error;
|
||||
message = "Function with same name and arguments defined twice.";
|
||||
}
|
||||
else
|
||||
{
|
||||
static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\"");
|
||||
error = 5883_error;
|
||||
message = "Event with same name and arguments defined twice.";
|
||||
}
|
||||
|
||||
ssl.limitSize(message);
|
||||
|
||||
m_errorReporter.declarationError(
|
||||
error,
|
||||
overloads[i]->location(),
|
||||
ssl,
|
||||
_message
|
||||
message
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -197,9 +214,9 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
|
||||
if (_contract.abstract())
|
||||
{
|
||||
if (_contract.contractKind() == ContractKind::Interface)
|
||||
m_errorReporter.typeError(_contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
|
||||
m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
|
||||
else if (_contract.contractKind() == ContractKind::Library)
|
||||
m_errorReporter.typeError(_contract.location(), "Libraries cannot be abstract.");
|
||||
m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract.");
|
||||
else
|
||||
solAssert(_contract.contractKind() == ContractKind::Contract, "");
|
||||
}
|
||||
@ -215,10 +232,12 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
|
||||
SecondarySourceLocation ssl;
|
||||
for (auto declaration: _contract.annotation().unimplementedDeclarations)
|
||||
ssl.append("Missing implementation: ", declaration->location());
|
||||
m_errorReporter.typeError(_contract.location(), ssl,
|
||||
"Contract \"" + _contract.annotation().canonicalName
|
||||
+ "\" should be marked as abstract.");
|
||||
|
||||
m_errorReporter.typeError(
|
||||
3656_error,
|
||||
_contract.location(),
|
||||
ssl,
|
||||
"Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,6 +262,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons
|
||||
}
|
||||
else
|
||||
m_errorReporter.declarationError(
|
||||
1563_error,
|
||||
modifier->location(),
|
||||
"Modifier-style base constructor call without arguments."
|
||||
);
|
||||
@ -304,6 +324,7 @@ void ContractLevelChecker::annotateBaseConstructorArguments(
|
||||
}
|
||||
|
||||
m_errorReporter.declarationError(
|
||||
3364_error,
|
||||
*mainLocation,
|
||||
ssl,
|
||||
"Base constructor arguments given twice."
|
||||
@ -343,6 +364,7 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
||||
for (size_t j = i + 1; j < it.second.size(); ++j)
|
||||
if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
|
||||
m_errorReporter.typeError(
|
||||
9914_error,
|
||||
it.second[j].first->location(),
|
||||
"Function overload clash during conversion to external types for arguments."
|
||||
);
|
||||
@ -356,6 +378,7 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
|
||||
util::FixedHash<4> const& hash = it.first;
|
||||
if (hashes.count(hash))
|
||||
m_errorReporter.typeError(
|
||||
1860_error,
|
||||
_contract.location(),
|
||||
string("Function signature hash collision for ") + it.second->externalSignature()
|
||||
);
|
||||
@ -369,11 +392,11 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c
|
||||
return;
|
||||
|
||||
if (!_contract.baseContracts().empty())
|
||||
m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
|
||||
m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit.");
|
||||
|
||||
for (auto const& var: _contract.stateVariables())
|
||||
if (!var->isConstant())
|
||||
m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
|
||||
m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables");
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
|
||||
@ -412,6 +435,7 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
|
||||
|
||||
if (!errors.infos.empty())
|
||||
m_errorReporter.fatalTypeError(
|
||||
6594_error,
|
||||
_contract.location(),
|
||||
errors,
|
||||
std::string("Contract \"") +
|
||||
@ -428,6 +452,7 @@ void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition
|
||||
if (auto const* fallback = _contract.fallbackFunction())
|
||||
if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction())
|
||||
m_errorReporter.warning(
|
||||
3628_error,
|
||||
_contract.location(),
|
||||
"This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.",
|
||||
SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location())
|
||||
|
@ -60,7 +60,7 @@ private:
|
||||
void checkDuplicateFunctions(ContractDefinition const& _contract);
|
||||
void checkDuplicateEvents(ContractDefinition const& _contract);
|
||||
template <class T>
|
||||
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
|
||||
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions);
|
||||
/// Checks for unimplemented functions and modifiers.
|
||||
void checkAbstractDefinitions(ContractDefinition const& _contract);
|
||||
/// Checks that the base constructor arguments are properly provided.
|
||||
|
@ -136,6 +136,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
|
||||
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
|
||||
|
||||
m_errorReporter.typeError(
|
||||
3464_error,
|
||||
variableOccurrence->occurrence() ?
|
||||
*variableOccurrence->occurrence() :
|
||||
variableOccurrence->declaration().location(),
|
||||
@ -176,6 +177,6 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const*
|
||||
// Extend the location, as long as the next location overlaps (unreachable is sorted).
|
||||
for (; it != unreachable.end() && it->start <= location.end; ++it)
|
||||
location.end = std::max(location.end, it->end);
|
||||
m_errorReporter.warning(location, "Unreachable code.");
|
||||
m_errorReporter.warning(5740_error, location, "Unreachable code.");
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
@ -188,12 +188,14 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
|
||||
{
|
||||
if (contractType->contractDefinition().isLibrary())
|
||||
m_errorReporter.fatalTypeError(
|
||||
1665_error,
|
||||
typeName->location(),
|
||||
"Library types cannot be used as mapping keys."
|
||||
);
|
||||
}
|
||||
else if (typeName->annotation().type->category() != Type::Category::Enum)
|
||||
m_errorReporter.fatalTypeError(
|
||||
7804_error,
|
||||
typeName->location(),
|
||||
"Only elementary types, contract types or enums are allowed as mapping keys."
|
||||
);
|
||||
@ -253,9 +255,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
return;
|
||||
|
||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||
m_errorReporter.declarationError(1788_error, _variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||
if (_variable.immutable() && !_variable.isStateVariable())
|
||||
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||
m_errorReporter.declarationError(8297_error, _variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||
|
||||
if (!_variable.typeName())
|
||||
{
|
||||
@ -360,19 +362,19 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
||||
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.typeError(_location, _description);
|
||||
m_errorReporter.typeError(2311_error, _location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalTypeError(_location, _description);
|
||||
m_errorReporter.fatalTypeError(5651_error, _location, _description);
|
||||
}
|
||||
|
||||
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
||||
m_errorReporter.fatalDeclarationError(2046_error, _location, _description);
|
||||
}
|
||||
|
||||
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
||||
|
@ -173,5 +173,5 @@ void DocStringAnalyser::parseDocStrings(
|
||||
void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccured = true;
|
||||
m_errorReporter.docstringParsingError(_location, _description);
|
||||
m_errorReporter.docstringParsingError(7816_error, _location, _description);
|
||||
}
|
||||
|
@ -100,11 +100,13 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
||||
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
||||
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
||||
// Accepts a MagicType that can be any contract type or an Integer type and returns a
|
||||
// MagicType. The TypeChecker handles the correctness of the input and output types.
|
||||
magicVarDecl("type", TypeProvider::function(
|
||||
strings{"address"} /* accepts any contract type, handled by the type checker */,
|
||||
strings{} /* returns a MagicType, handled by the type checker */,
|
||||
strings{},
|
||||
strings{},
|
||||
FunctionType::Kind::MetaType,
|
||||
false,
|
||||
true,
|
||||
StateMutability::Pure
|
||||
)),
|
||||
};
|
||||
@ -123,7 +125,7 @@ vector<Declaration const*> GlobalContext::declarations() const
|
||||
{
|
||||
vector<Declaration const*> declarations;
|
||||
declarations.reserve(m_magicVariables.size());
|
||||
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
|
||||
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
|
||||
declarations.push_back(variable.get());
|
||||
return declarations;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
void ImmutableValidator::analyze()
|
||||
{
|
||||
@ -42,7 +43,7 @@ void ImmutableValidator::analyze()
|
||||
visitCallableIfNew(*contract->constructor());
|
||||
|
||||
for (ContractDefinition const* contract: linearizedContracts)
|
||||
for (std::shared_ptr<InheritanceSpecifier> const inheritSpec: contract->baseContracts())
|
||||
for (std::shared_ptr<InheritanceSpecifier> const& inheritSpec: contract->baseContracts())
|
||||
if (auto args = inheritSpec->arguments())
|
||||
ASTNode::listAccept(*args, *this);
|
||||
|
||||
@ -165,33 +166,39 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
|
||||
{
|
||||
if (!m_currentConstructor)
|
||||
m_errorReporter.typeError(
|
||||
1581_error,
|
||||
_expression.location(),
|
||||
"Immutable variables can only be initialized inline or assigned directly in the constructor."
|
||||
);
|
||||
else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id())
|
||||
m_errorReporter.typeError(
|
||||
7484_error,
|
||||
_expression.location(),
|
||||
"Immutable variables must be initialized in the constructor of the contract they are defined in."
|
||||
);
|
||||
else if (m_inLoop)
|
||||
m_errorReporter.typeError(
|
||||
6672_error,
|
||||
_expression.location(),
|
||||
"Immutable variables can only be initialized once, not in a while statement."
|
||||
);
|
||||
else if (m_inBranch)
|
||||
m_errorReporter.typeError(
|
||||
4599_error,
|
||||
_expression.location(),
|
||||
"Immutable variables must be initialized unconditionally, not in an if statement."
|
||||
);
|
||||
|
||||
if (!m_initializedStateVariables.emplace(&_variableReference).second)
|
||||
m_errorReporter.typeError(
|
||||
1574_error,
|
||||
_expression.location(),
|
||||
"Immutable state variable already initialized."
|
||||
);
|
||||
}
|
||||
else if (m_inConstructionContext)
|
||||
m_errorReporter.typeError(
|
||||
7733_error,
|
||||
_expression.location(),
|
||||
"Immutable variables cannot be read during contract creation time, which means "
|
||||
"they cannot be read in the constructor or any function or modifier called from it."
|
||||
@ -205,6 +212,7 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
|
||||
if (varDecl->immutable())
|
||||
if (!util::contains(m_initializedStateVariables, varDecl))
|
||||
m_errorReporter.typeError(
|
||||
2658_error,
|
||||
_location,
|
||||
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
||||
"Construction control flow ends without initializing all immutable state variables."
|
||||
|
@ -80,6 +80,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
if (!_sourceUnits.count(path))
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
5073_error,
|
||||
imp->location(),
|
||||
"Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
|
||||
);
|
||||
@ -95,6 +96,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
||||
if (declarations.empty())
|
||||
{
|
||||
m_errorReporter.declarationError(
|
||||
2904_error,
|
||||
imp->location(),
|
||||
"Declaration \"" +
|
||||
alias.symbol->name() +
|
||||
@ -208,6 +210,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
||||
// Don't warn the user for what the user did not.
|
||||
continue;
|
||||
m_errorReporter.warning(
|
||||
8261_error,
|
||||
declaration->location(),
|
||||
"Variable is shadowed in inline assembly by an instruction of the same name"
|
||||
);
|
||||
@ -326,6 +329,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
||||
}
|
||||
|
||||
m_errorReporter.declarationError(
|
||||
9097_error,
|
||||
secondDeclarationLocation,
|
||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
||||
"Identifier already declared."
|
||||
@ -343,19 +347,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
|
||||
UserDefinedTypeName const& baseName = baseSpecifier->name();
|
||||
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
|
||||
if (!base)
|
||||
m_errorReporter.fatalTypeError(baseName.location(), "Contract expected.");
|
||||
m_errorReporter.fatalTypeError(8758_error, baseName.location(), "Contract expected.");
|
||||
// "push_front" has the effect that bases mentioned later can overwrite members of bases
|
||||
// mentioned earlier
|
||||
input.back().push_front(base);
|
||||
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
|
||||
if (basesBases.empty())
|
||||
m_errorReporter.fatalTypeError(baseName.location(), "Definition of base has to precede definition of derived contract");
|
||||
m_errorReporter.fatalTypeError(2449_error, baseName.location(), "Definition of base has to precede definition of derived contract");
|
||||
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
|
||||
}
|
||||
input.back().push_front(&_contract);
|
||||
vector<ContractDefinition const*> result = cThreeMerge(input);
|
||||
if (result.empty())
|
||||
m_errorReporter.fatalTypeError(_contract.location(), "Linearization of inheritance graph impossible");
|
||||
m_errorReporter.fatalTypeError(5005_error, _contract.location(), "Linearization of inheritance graph impossible");
|
||||
_contract.annotation().linearizedBaseContracts = result;
|
||||
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
|
||||
}
|
||||
@ -476,6 +480,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
}
|
||||
|
||||
_errorReporter.declarationError(
|
||||
2333_error,
|
||||
secondDeclarationLocation,
|
||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
||||
"Identifier already declared."
|
||||
@ -486,6 +491,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
{
|
||||
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
|
||||
_errorReporter.warning(
|
||||
2319_error,
|
||||
*_errorLocation,
|
||||
"This declaration shadows a builtin symbol."
|
||||
);
|
||||
@ -493,6 +499,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
{
|
||||
auto shadowedLocation = shadowedDeclaration->location();
|
||||
_errorReporter.warning(
|
||||
2519_error,
|
||||
_declaration.location(),
|
||||
"This declaration shadows an existing declaration.",
|
||||
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
|
||||
@ -461,6 +460,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
||||
{
|
||||
if (contains_if(inheritedFuncs, MatchByName{modifier->name()}))
|
||||
m_errorReporter.typeError(
|
||||
5631_error,
|
||||
modifier->location(),
|
||||
"Override changes function or public state variable to modifier."
|
||||
);
|
||||
@ -474,7 +474,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
||||
continue;
|
||||
|
||||
if (contains_if(inheritedMods, MatchByName{function->name()}))
|
||||
m_errorReporter.typeError(function->location(), "Override changes modifier to function.");
|
||||
m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function.");
|
||||
|
||||
checkOverrideList(OverrideProxy{function}, inheritedFuncs);
|
||||
}
|
||||
@ -484,7 +484,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
||||
continue;
|
||||
|
||||
if (contains_if(inheritedMods, MatchByName{stateVar->name()}))
|
||||
m_errorReporter.typeError(stateVar->location(), "Override changes modifier to public state variable.");
|
||||
m_errorReporter.typeError(1456_error, stateVar->location(), "Override changes modifier to public state variable.");
|
||||
|
||||
checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs);
|
||||
}
|
||||
@ -500,17 +500,19 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
|
||||
if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType())
|
||||
m_errorReporter.typeError(
|
||||
1078_error,
|
||||
_overriding.location(),
|
||||
"Override changes modifier signature."
|
||||
);
|
||||
|
||||
if (!_overriding.overrides())
|
||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
|
||||
overrideError(_overriding, _super, 9456_error, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
|
||||
|
||||
if (_super.isVariable())
|
||||
overrideError(
|
||||
_super,
|
||||
_overriding,
|
||||
1452_error,
|
||||
"Cannot override public state variable.",
|
||||
"Overriding " + _overriding.astNodeName() + " is here:"
|
||||
);
|
||||
@ -518,6 +520,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
overrideError(
|
||||
_super,
|
||||
_overriding,
|
||||
4334_error,
|
||||
"Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?",
|
||||
"Overriding " + _overriding.astNodeName() + " is here:"
|
||||
);
|
||||
@ -525,7 +528,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
if (_overriding.isVariable())
|
||||
{
|
||||
if (_super.visibility() != Visibility::External)
|
||||
overrideError(_overriding, _super, "Public state variables can only override functions with external visibility.");
|
||||
overrideError(_overriding, _super, 5225_error, "Public state variables can only override functions with external visibility.");
|
||||
solAssert(_overriding.visibility() == Visibility::External, "");
|
||||
}
|
||||
else if (_overriding.visibility() != _super.visibility())
|
||||
@ -536,7 +539,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
_super.visibility() == Visibility::External &&
|
||||
_overriding.visibility() == Visibility::Public
|
||||
))
|
||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " visibility differs.");
|
||||
overrideError(_overriding, _super, 9098_error, "Overriding " + _overriding.astNodeName() + " visibility differs.");
|
||||
}
|
||||
|
||||
if (_super.isFunction())
|
||||
@ -547,7 +550,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
|
||||
|
||||
if (!functionType->hasEqualReturnTypes(*superType))
|
||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " return types differ.");
|
||||
overrideError(_overriding, _super, 4822_error, "Overriding " + _overriding.astNodeName() + " return types differ.");
|
||||
|
||||
// This is only relevant for a function overriding a function.
|
||||
if (_overriding.isFunction())
|
||||
@ -556,6 +559,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
overrideError(
|
||||
_overriding,
|
||||
_super,
|
||||
2837_error,
|
||||
"Overriding function changes state mutability from \"" +
|
||||
stateMutabilityToString(_super.stateMutability()) +
|
||||
"\" to \"" +
|
||||
@ -567,6 +571,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
overrideError(
|
||||
_overriding,
|
||||
_super,
|
||||
4593_error,
|
||||
"Overriding an implemented function with an unimplemented function is not allowed."
|
||||
);
|
||||
}
|
||||
@ -576,6 +581,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
void OverrideChecker::overrideListError(
|
||||
OverrideProxy const& _item,
|
||||
set<ContractDefinition const*, CompareByID> _secondary,
|
||||
ErrorId _error,
|
||||
string const& _message1,
|
||||
string const& _message2
|
||||
)
|
||||
@ -593,6 +599,7 @@ void OverrideChecker::overrideListError(
|
||||
contractSingularPlural = "contracts ";
|
||||
|
||||
m_errorReporter.typeError(
|
||||
_error,
|
||||
_item.overrides() ? _item.overrides()->location() : _item.location(),
|
||||
ssl,
|
||||
_message1 +
|
||||
@ -603,9 +610,10 @@ void OverrideChecker::overrideListError(
|
||||
);
|
||||
}
|
||||
|
||||
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, string const& _message, string const& _secondaryMsg)
|
||||
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_error,
|
||||
_overriding.location(),
|
||||
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
||||
_message
|
||||
@ -613,9 +621,10 @@ void OverrideChecker::overrideError(Declaration const& _overriding, Declaration
|
||||
}
|
||||
|
||||
|
||||
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, string const& _message, string const& _secondaryMsg)
|
||||
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
_error,
|
||||
_overriding.location(),
|
||||
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
||||
_message
|
||||
@ -718,7 +727,7 @@ void OverrideChecker::checkAmbiguousOverridesInternal(set<OverrideProxy> _baseCa
|
||||
" Since one of the bases defines a public state variable which cannot be overridden, "
|
||||
"you have to change the inheritance layout or the names of the functions.";
|
||||
|
||||
m_errorReporter.typeError(_location, ssl, message);
|
||||
m_errorReporter.typeError(6480_error, _location, ssl, message);
|
||||
}
|
||||
|
||||
set<ContractDefinition const*, OverrideChecker::CompareByID> OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const
|
||||
@ -766,13 +775,14 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
SecondarySourceLocation ssl;
|
||||
ssl.append("First occurrence here: ", list[i-1]->location());
|
||||
m_errorReporter.typeError(
|
||||
4520_error,
|
||||
list[i]->location(),
|
||||
ssl,
|
||||
"Duplicate contract \"" +
|
||||
joinHumanReadable(list[i]->namePath(), ".") +
|
||||
"\" found in override list of \"" +
|
||||
_item.name() +
|
||||
"\"."
|
||||
"Duplicate contract \"" +
|
||||
joinHumanReadable(list[i]->namePath(), ".") +
|
||||
"\" found in override list of \"" +
|
||||
_item.name() +
|
||||
"\"."
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -791,6 +801,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
|
||||
if (_item.overrides() && expectedContracts.empty())
|
||||
m_errorReporter.typeError(
|
||||
7792_error,
|
||||
_item.overrides()->location(),
|
||||
_item.astNodeNameCapitalized() + " has override specified but does not override anything."
|
||||
);
|
||||
@ -804,6 +815,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
overrideListError(
|
||||
_item,
|
||||
missingContracts,
|
||||
4327_error,
|
||||
_item.astNodeNameCapitalized() + " needs to specify overridden ",
|
||||
""
|
||||
);
|
||||
@ -813,6 +825,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
||||
overrideListError(
|
||||
_item,
|
||||
surplusContracts,
|
||||
2353_error,
|
||||
"Invalid ",
|
||||
"specified in override list: "
|
||||
);
|
||||
|
@ -32,6 +32,7 @@
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
struct ErrorId;
|
||||
struct SourceLocation;
|
||||
}
|
||||
|
||||
@ -160,18 +161,21 @@ private:
|
||||
void overrideListError(
|
||||
OverrideProxy const& _item,
|
||||
std::set<ContractDefinition const*, CompareByID> _secondary,
|
||||
langutil::ErrorId _error,
|
||||
std::string const& _message1,
|
||||
std::string const& _message2
|
||||
);
|
||||
void overrideError(
|
||||
Declaration const& _overriding,
|
||||
Declaration const& _super,
|
||||
langutil::ErrorId _error,
|
||||
std::string const& _message,
|
||||
std::string const& _secondaryMsg = "Overridden function is here:"
|
||||
);
|
||||
void overrideError(
|
||||
OverrideProxy const& _overriding,
|
||||
OverrideProxy const& _super,
|
||||
langutil::ErrorId _error,
|
||||
std::string const& _message,
|
||||
std::string const& _secondaryMsg = "Overridden function is here:"
|
||||
);
|
||||
|
@ -122,6 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
||||
for (auto declaration: m_constVariables)
|
||||
if (auto identifier = findCycle(*declaration))
|
||||
m_errorReporter.typeError(
|
||||
6161_error,
|
||||
declaration->location(),
|
||||
"The value of the constant " + declaration->name() +
|
||||
" has a cyclic dependency via " + identifier->name() + "."
|
||||
@ -165,7 +166,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
||||
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
|
||||
{
|
||||
if (_depth >= 256)
|
||||
m_errorReporter.fatalDeclarationError(_variable.location(), "Variable definition exhausting cyclic dependency validator.");
|
||||
m_errorReporter.fatalDeclarationError(7380_error, _variable.location(), "Variable definition exhausting cyclic dependency validator.");
|
||||
|
||||
// Iterating through the dependencies needs to be deterministic and thus cannot
|
||||
// depend on the memory layout.
|
||||
@ -209,6 +210,7 @@ struct OverrideSpecifierChecker: public PostTypeChecker::Checker
|
||||
TypeType const* actualTypeType = dynamic_cast<TypeType const*>(decl->type());
|
||||
|
||||
m_errorReporter.typeError(
|
||||
9301_error,
|
||||
override->location(),
|
||||
"Expected contract but got " +
|
||||
actualTypeType->actualType()->toString(true) +
|
||||
@ -243,6 +245,7 @@ struct ModifierContextChecker: public PostTypeChecker::Checker
|
||||
if (ModifierType const* type = dynamic_cast<decltype(type)>(_identifier.annotation().type))
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
3112_error,
|
||||
_identifier.location(),
|
||||
"Modifier can only be referenced in function headers."
|
||||
);
|
||||
@ -280,6 +283,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker
|
||||
// Check for event outside of emit statement
|
||||
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
|
||||
m_errorReporter.typeError(
|
||||
3132_error,
|
||||
_functionCall.location(),
|
||||
"Event invocations have to be prefixed by \"emit\"."
|
||||
);
|
||||
@ -308,7 +312,7 @@ struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker
|
||||
&& !_variable.isCallableOrCatchParameter()
|
||||
&& !m_insideStruct
|
||||
)
|
||||
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
|
||||
m_errorReporter.typeError(8274_error, _variable.location(), "Variables cannot be declared in interfaces.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -268,19 +268,19 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
||||
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.declarationError(_location, _description);
|
||||
m_errorReporter.declarationError(8532_error, _location, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.declarationError(_location, _ssl, _description);
|
||||
m_errorReporter.declarationError(3881_error, _location, _ssl, _description);
|
||||
}
|
||||
|
||||
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorOccurred = true;
|
||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
||||
m_errorReporter.fatalDeclarationError(6546_error, _location, _description);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -120,13 +120,14 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
||||
{
|
||||
if (var.first.second->isCallableOrCatchParameter())
|
||||
m_errorReporter.warning(
|
||||
5667_error,
|
||||
var.first.second->location(),
|
||||
"Unused " +
|
||||
string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") +
|
||||
" parameter. Remove or comment out the variable name to silence this warning."
|
||||
);
|
||||
else
|
||||
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
||||
m_errorReporter.warning(2072_error, var.first.second->location(), "Unused local variable.");
|
||||
}
|
||||
m_localVarUseCount.clear();
|
||||
m_constructor = false;
|
||||
@ -159,6 +160,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
|
||||
set<StructDefinition const*> structsSeen;
|
||||
if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
|
||||
m_errorReporter.warning(
|
||||
3408_error,
|
||||
_variable.location(),
|
||||
"Variable covers a large part of storage and thus makes collisions likely. "
|
||||
"Either use mappings or dynamic arrays and allow their size to be increased only "
|
||||
@ -183,6 +185,7 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
|
||||
{
|
||||
if (_statement.expression().annotation().isPure)
|
||||
m_errorReporter.warning(
|
||||
6133_error,
|
||||
_statement.location(),
|
||||
"Statement has no effect."
|
||||
);
|
||||
@ -196,11 +199,13 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
|
||||
m_errorReporter.typeError(
|
||||
1400_error,
|
||||
_memberAccess.location(),
|
||||
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
|
||||
);
|
||||
else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash")
|
||||
m_errorReporter.typeError(
|
||||
8113_error,
|
||||
_memberAccess.location(),
|
||||
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
||||
);
|
||||
@ -211,6 +216,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
|
||||
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
|
||||
m_errorReporter.warning(
|
||||
6417_error,
|
||||
_memberAccess.location(),
|
||||
"The constructor of the contract (or its base) uses inline assembly. "
|
||||
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
|
||||
@ -222,6 +228,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||
if (type->kind() == FunctionType::Kind::BareCallCode)
|
||||
m_errorReporter.typeError(
|
||||
2256_error,
|
||||
_memberAccess.location(),
|
||||
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
|
||||
);
|
||||
@ -235,6 +242,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
if (id->name() == "this")
|
||||
m_errorReporter.warning(
|
||||
5805_error,
|
||||
id->location(),
|
||||
"\"this\" used in constructor. "
|
||||
"Note that external functions of a contract "
|
||||
@ -285,6 +293,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation)
|
||||
))
|
||||
if (rhs->isZero())
|
||||
m_errorReporter.typeError(
|
||||
1211_error,
|
||||
_operation.location(),
|
||||
(_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero."
|
||||
);
|
||||
@ -307,6 +316,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
||||
))
|
||||
if (lastArg->isZero())
|
||||
m_errorReporter.typeError(
|
||||
4195_error,
|
||||
_functionCall.location(),
|
||||
"Arithmetic modulo zero."
|
||||
);
|
||||
@ -317,6 +327,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
||||
functionType->declaration().scope() == m_currentContract
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
6700_error,
|
||||
_functionCall.location(),
|
||||
SecondarySourceLocation().append(
|
||||
"The function declaration is here:",
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <memory>
|
||||
@ -69,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
|
||||
string(";\"");
|
||||
|
||||
// when reporting the warning, print the source name only
|
||||
m_errorReporter.warning({-1, -1, _sourceUnit.location().source}, errorString);
|
||||
m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString);
|
||||
}
|
||||
m_sourceUnit = nullptr;
|
||||
}
|
||||
@ -79,18 +78,20 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
solAssert(!_pragma.tokens().empty(), "");
|
||||
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
|
||||
if (_pragma.tokens()[0] != Token::Identifier)
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
|
||||
m_errorReporter.syntaxError(5226_error, _pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
|
||||
else if (_pragma.literals()[0] == "experimental")
|
||||
{
|
||||
solAssert(m_sourceUnit, "");
|
||||
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
||||
if (literals.empty())
|
||||
m_errorReporter.syntaxError(
|
||||
9679_error,
|
||||
_pragma.location(),
|
||||
"Experimental feature name is missing."
|
||||
);
|
||||
else if (literals.size() > 1)
|
||||
m_errorReporter.syntaxError(
|
||||
6022_error,
|
||||
_pragma.location(),
|
||||
"Stray arguments."
|
||||
);
|
||||
@ -98,17 +99,17 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
{
|
||||
string const literal = literals[0];
|
||||
if (literal.empty())
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid.");
|
||||
m_errorReporter.syntaxError(3250_error, _pragma.location(), "Empty experimental feature name is invalid.");
|
||||
else if (!ExperimentalFeatureNames.count(literal))
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name.");
|
||||
m_errorReporter.syntaxError(8491_error, _pragma.location(), "Unsupported experimental feature name.");
|
||||
else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal)))
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name.");
|
||||
m_errorReporter.syntaxError(1231_error, _pragma.location(), "Duplicate experimental feature name.");
|
||||
else
|
||||
{
|
||||
auto feature = ExperimentalFeatureNames.at(literal);
|
||||
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
|
||||
if (!ExperimentalFeatureWithoutWarning.count(feature))
|
||||
m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
||||
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,15 +122,16 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
||||
static SemVerVersion const currentVersion{string(VersionString)};
|
||||
if (!matchExpression.matches(currentVersion))
|
||||
m_errorReporter.syntaxError(
|
||||
3997_error,
|
||||
_pragma.location(),
|
||||
"Source file requires different compiler version (current compiler is " +
|
||||
string(VersionString) + " - note that nightly builds are considered to be "
|
||||
string(VersionString) + ") - note that nightly builds are considered to be "
|
||||
"strictly less than the released version"
|
||||
);
|
||||
m_versionPragmaFound = true;
|
||||
}
|
||||
else
|
||||
m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -142,7 +144,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&)
|
||||
void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
|
||||
{
|
||||
if (_modifier.isImplemented() && !m_placeholderFound)
|
||||
m_errorReporter.syntaxError(_modifier.body().location(), "Modifier body does not contain '_'.");
|
||||
m_errorReporter.syntaxError(2883_error, _modifier.body().location(), "Modifier body does not contain '_'.");
|
||||
m_placeholderFound = false;
|
||||
}
|
||||
|
||||
@ -150,7 +152,7 @@ void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _stat
|
||||
{
|
||||
auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement);
|
||||
if (varDecl)
|
||||
m_errorReporter.syntaxError(_statement.location(), "Variable declarations can only be used inside blocks.");
|
||||
m_errorReporter.syntaxError(9079_error, _statement.location(), "Variable declarations can only be used inside blocks.");
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(IfStatement const& _ifStatement)
|
||||
@ -189,7 +191,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement)
|
||||
{
|
||||
if (m_inLoopDepth <= 0)
|
||||
// we're not in a for/while loop, report syntax error
|
||||
m_errorReporter.syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
|
||||
m_errorReporter.syntaxError(4123_error, _continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -197,13 +199,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
|
||||
{
|
||||
if (m_inLoopDepth <= 0)
|
||||
// we're not in a for/while loop, report syntax error
|
||||
m_errorReporter.syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
|
||||
m_errorReporter.syntaxError(6102_error, _breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(Throw const& _throwStatement)
|
||||
{
|
||||
m_errorReporter.syntaxError(
|
||||
4538_error,
|
||||
_throwStatement.location(),
|
||||
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
|
||||
);
|
||||
@ -222,29 +225,29 @@ bool SyntaxChecker::visit(Literal const& _literal)
|
||||
// Generic checks no matter what base this number literal is of:
|
||||
if (value.back() == '_')
|
||||
{
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
|
||||
m_errorReporter.syntaxError(2090_error, _literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (value.find("__") != ASTString::npos)
|
||||
{
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
|
||||
m_errorReporter.syntaxError(2990_error, _literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_literal.isHexNumber()) // decimal literal
|
||||
{
|
||||
if (value.find("._") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
m_errorReporter.syntaxError(3891_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
|
||||
if (value.find("_.") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
m_errorReporter.syntaxError(1023_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||
|
||||
if (value.find("_e") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
|
||||
m_errorReporter.syntaxError(6415_error, _literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
|
||||
|
||||
if (value.find("e_") != ASTString::npos)
|
||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
|
||||
m_errorReporter.syntaxError(6165_error, _literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -253,7 +256,7 @@ bool SyntaxChecker::visit(Literal const& _literal)
|
||||
bool SyntaxChecker::visit(UnaryOperation const& _operation)
|
||||
{
|
||||
if (_operation.getOperator() == Token::Add)
|
||||
m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed.");
|
||||
m_errorReporter.syntaxError(9636_error, _operation.location(), "Use of unary + is disallowed.");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -265,6 +268,7 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
|
||||
if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations()))
|
||||
m_errorReporter.syntaxError(
|
||||
6553_error,
|
||||
_inlineAssembly.location(),
|
||||
"The msize instruction cannot be used when the Yul optimizer is activated because "
|
||||
"it can change its semantics. Either disable the Yul optimizer or do not use the instruction."
|
||||
@ -285,7 +289,9 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
|
||||
ASTString const& contractName = _contract.name();
|
||||
for (FunctionDefinition const* function: _contract.definedFunctions())
|
||||
if (function->name() == contractName)
|
||||
m_errorReporter.syntaxError(function->location(),
|
||||
m_errorReporter.syntaxError(
|
||||
5796_error,
|
||||
function->location(),
|
||||
"Functions are not allowed to have the same name as the contract. "
|
||||
"If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
|
||||
);
|
||||
@ -298,15 +304,16 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
|
||||
m_errorReporter.syntaxError(
|
||||
4937_error,
|
||||
_function.location(),
|
||||
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
|
||||
);
|
||||
}
|
||||
|
||||
if (m_isInterface && !_function.modifiers().empty())
|
||||
m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers.");
|
||||
m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers.");
|
||||
else if (!_function.isImplemented() && !_function.modifiers().empty())
|
||||
m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers.");
|
||||
m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers.");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -315,11 +322,11 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
|
||||
{
|
||||
for (auto const& decl: _node.parameterTypeList()->parameters())
|
||||
if (!decl->name().empty())
|
||||
m_errorReporter.warning(decl->location(), "Naming function type parameters is deprecated.");
|
||||
m_errorReporter.warning(6162_error, decl->location(), "Naming function type parameters is deprecated.");
|
||||
|
||||
for (auto const& decl: _node.returnParameterTypeList()->parameters())
|
||||
if (!decl->name().empty())
|
||||
m_errorReporter.syntaxError(decl->location(), "Return parameters in function types may not be named.");
|
||||
m_errorReporter.syntaxError(7304_error, decl->location(), "Return parameters in function types may not be named.");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -327,8 +334,13 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
|
||||
bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
{
|
||||
// Report if none of the variable components in the tuple have a name (only possible via deprecated "var")
|
||||
if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr))
|
||||
if (std::all_of(
|
||||
_statement.declarations().begin(),
|
||||
_statement.declarations().end(),
|
||||
[](ASTPointer<VariableDeclaration> const& declaration) { return declaration == nullptr; }
|
||||
))
|
||||
m_errorReporter.syntaxError(
|
||||
3299_error,
|
||||
_statement.location(),
|
||||
"The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty."
|
||||
);
|
||||
@ -339,7 +351,7 @@ bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
bool SyntaxChecker::visit(StructDefinition const& _struct)
|
||||
{
|
||||
if (_struct.members().empty())
|
||||
m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed.");
|
||||
m_errorReporter.syntaxError(5306_error, _struct.location(), "Defining empty structs is disallowed.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -171,6 +171,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
||||
!_funDef.overrides()
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
2018_error,
|
||||
_funDef.location(),
|
||||
"Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability)
|
||||
);
|
||||
@ -229,7 +230,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
AssemblyViewPureChecker{
|
||||
_inlineAssembly.dialect(),
|
||||
[=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
||||
[&](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
||||
}(_inlineAssembly.operations());
|
||||
}
|
||||
|
||||
@ -252,6 +253,7 @@ void ViewPureChecker::reportMutability(
|
||||
))
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
2527_error,
|
||||
_location,
|
||||
"Function declared as pure, but this expression (potentially) reads from the "
|
||||
"environment or state and thus requires \"view\"."
|
||||
@ -261,6 +263,7 @@ void ViewPureChecker::reportMutability(
|
||||
else if (_mutability == StateMutability::NonPayable)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
8961_error,
|
||||
_location,
|
||||
"Function declared as " +
|
||||
stateMutabilityToString(m_currentFunction->stateMutability()) +
|
||||
@ -277,12 +280,14 @@ void ViewPureChecker::reportMutability(
|
||||
{
|
||||
if (_nestedLocation)
|
||||
m_errorReporter.typeError(
|
||||
4006_error,
|
||||
_location,
|
||||
SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation),
|
||||
"This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal."
|
||||
);
|
||||
else
|
||||
m_errorReporter.typeError(
|
||||
5887_error,
|
||||
_location,
|
||||
"\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function "
|
||||
"\"payable\" or use an internal function to avoid this error."
|
||||
@ -357,6 +362,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
||||
{MagicType::Kind::MetaType, "runtimeCode"},
|
||||
{MagicType::Kind::MetaType, "name"},
|
||||
{MagicType::Kind::MetaType, "interfaceId"},
|
||||
{MagicType::Kind::MetaType, "min"},
|
||||
{MagicType::Kind::MetaType, "max"},
|
||||
};
|
||||
set<MagicMember> static const payableMembers{
|
||||
{MagicType::Kind::Message, "value"}
|
||||
|
@ -556,7 +556,13 @@ MagicType const* TypeProvider::magic(MagicType::Kind _kind)
|
||||
|
||||
MagicType const* TypeProvider::meta(Type const* _type)
|
||||
{
|
||||
solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
|
||||
solAssert(
|
||||
_type && (
|
||||
_type->category() == Type::Category::Contract ||
|
||||
_type->category() == Type::Category::Integer
|
||||
),
|
||||
"Only contracts or integer types supported for now."
|
||||
);
|
||||
return createAndGet<MagicType>(_type);
|
||||
}
|
||||
|
||||
|
@ -556,6 +556,22 @@ string IntegerType::toString(bool) const
|
||||
return prefix + util::toString(m_bits);
|
||||
}
|
||||
|
||||
u256 IntegerType::min() const
|
||||
{
|
||||
if (isSigned())
|
||||
return s2u(s256(minValue()));
|
||||
else
|
||||
return u256(minValue());
|
||||
}
|
||||
|
||||
u256 IntegerType::max() const
|
||||
{
|
||||
if (isSigned())
|
||||
return s2u(s256(maxValue()));
|
||||
else
|
||||
return u256(maxValue());
|
||||
}
|
||||
|
||||
bigint IntegerType::minValue() const
|
||||
{
|
||||
if (isSigned())
|
||||
@ -3763,20 +3779,35 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
||||
case Kind::MetaType:
|
||||
{
|
||||
solAssert(
|
||||
m_typeArgument && m_typeArgument->category() == Type::Category::Contract,
|
||||
"Only contracts supported for now"
|
||||
m_typeArgument && (
|
||||
m_typeArgument->category() == Type::Category::Contract ||
|
||||
m_typeArgument->category() == Type::Category::Integer
|
||||
),
|
||||
"Only contracts or integer types supported for now"
|
||||
);
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
||||
if (contract.canBeDeployed())
|
||||
|
||||
if (m_typeArgument->category() == Type::Category::Contract)
|
||||
{
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
||||
if (contract.canBeDeployed())
|
||||
return MemberList::MemberMap({
|
||||
{"creationCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"name", TypeProvider::stringMemory()},
|
||||
});
|
||||
else
|
||||
return MemberList::MemberMap({
|
||||
{"interfaceId", TypeProvider::fixedBytes(4)},
|
||||
});
|
||||
}
|
||||
else if (m_typeArgument->category() == Type::Category::Integer)
|
||||
{
|
||||
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
|
||||
return MemberList::MemberMap({
|
||||
{"creationCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
|
||||
{"name", TypeProvider::stringMemory()},
|
||||
});
|
||||
else
|
||||
return MemberList::MemberMap({
|
||||
{"interfaceId", TypeProvider::fixedBytes(4)},
|
||||
{"min", integerTypePointer},
|
||||
{"max", integerTypePointer},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
solAssert(false, "Unknown kind of magic.");
|
||||
|
@ -452,6 +452,9 @@ public:
|
||||
unsigned numBits() const { return m_bits; }
|
||||
bool isSigned() const { return m_modifier == Modifier::Signed; }
|
||||
|
||||
u256 min() const;
|
||||
u256 max() const;
|
||||
|
||||
bigint minValue() const;
|
||||
bigint maxValue() const;
|
||||
|
||||
|
@ -28,7 +28,6 @@
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -38,7 +37,8 @@ using namespace solidity::frontend;
|
||||
string ABIFunctions::tupleEncoder(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _encodeAsLibraryTypes
|
||||
bool _encodeAsLibraryTypes,
|
||||
bool _reversed
|
||||
)
|
||||
{
|
||||
EncodingOptions options;
|
||||
@ -54,6 +54,8 @@ string ABIFunctions::tupleEncoder(
|
||||
for (auto const& t: _targetTypes)
|
||||
functionName += t->identifier() + "_";
|
||||
functionName += options.toFunctionNameSuffix();
|
||||
if (_reversed)
|
||||
functionName += "_reversed";
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
// Note that the values are in reverse due to the difference in calling semantics.
|
||||
@ -94,7 +96,10 @@ string ABIFunctions::tupleEncoder(
|
||||
stackPos += sizeOnStack;
|
||||
}
|
||||
solAssert(headPos == headSize_, "");
|
||||
string valueParams = suffixedVariableNameList("value", stackPos, 0);
|
||||
string valueParams =
|
||||
_reversed ?
|
||||
suffixedVariableNameList("value", stackPos, 0) :
|
||||
suffixedVariableNameList("value", 0, stackPos);
|
||||
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
||||
templ("encodeElements", encodeElements);
|
||||
|
||||
@ -104,7 +109,8 @@ string ABIFunctions::tupleEncoder(
|
||||
|
||||
string ABIFunctions::tupleEncoderPacked(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes
|
||||
TypePointers const& _targetTypes,
|
||||
bool _reversed
|
||||
)
|
||||
{
|
||||
EncodingOptions options;
|
||||
@ -120,6 +126,8 @@ string ABIFunctions::tupleEncoderPacked(
|
||||
for (auto const& t: _targetTypes)
|
||||
functionName += t->identifier() + "_";
|
||||
functionName += options.toFunctionNameSuffix();
|
||||
if (_reversed)
|
||||
functionName += "_reversed";
|
||||
|
||||
return createFunction(functionName, [&]() {
|
||||
solAssert(!_givenTypes.empty(), "");
|
||||
@ -158,7 +166,10 @@ string ABIFunctions::tupleEncoderPacked(
|
||||
encodeElements += elementTempl.render();
|
||||
stackPos += sizeOnStack;
|
||||
}
|
||||
string valueParams = suffixedVariableNameList("value", stackPos, 0);
|
||||
string valueParams =
|
||||
_reversed ?
|
||||
suffixedVariableNameList("value", stackPos, 0) :
|
||||
suffixedVariableNameList("value", 0, stackPos);
|
||||
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
||||
templ("encodeElements", encodeElements);
|
||||
|
||||
|
@ -67,31 +67,53 @@ public:
|
||||
|
||||
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
|
||||
/// into memory, converting the types to @a _targetTypes on the fly.
|
||||
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e.
|
||||
/// the layout on the stack is <value_1> ... <value_n> <headStart> with
|
||||
/// Parameters are: <headStart> <value_1> ... <value_n>, i.e.
|
||||
/// the layout on the stack is <value_n> ... <value_1> <headStart> with
|
||||
/// the top of the stack on the right.
|
||||
/// The values represent stack slots. If a type occupies more or less than one
|
||||
/// stack slot, it takes exactly that number of values.
|
||||
/// Returns a pointer to the end of the area written in memory.
|
||||
/// Does not allocate memory (does not change the free memory pointer), but writes
|
||||
/// to memory starting at $headStart and an unrestricted amount after that.
|
||||
/// If @reversed is true, the order of the variables after <headStart> is reversed.
|
||||
std::string tupleEncoder(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _encodeAsLibraryTypes = false
|
||||
bool _encodeAsLibraryTypes = false,
|
||||
bool _reversed = false
|
||||
);
|
||||
|
||||
/// Specialization of tupleEncoder to _reversed = true
|
||||
std::string tupleEncoderReversed(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _encodeAsLibraryTypes = false
|
||||
) {
|
||||
return tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes, true);
|
||||
}
|
||||
|
||||
/// @returns name of an assembly function to encode values of @a _givenTypes
|
||||
/// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
|
||||
/// Parameters are: <memPos> <value_n> ... <value_1>, i.e.
|
||||
/// the layout on the stack is <value_1> ... <value_n> <memPos> with
|
||||
/// Parameters are: <memPos> <value_1> ... <value_n>, i.e.
|
||||
/// the layout on the stack is <value_n> ... <value_1> <memPos> with
|
||||
/// the top of the stack on the right.
|
||||
/// The values represent stack slots. If a type occupies more or less than one
|
||||
/// stack slot, it takes exactly that number of values.
|
||||
/// Returns a pointer to the end of the area written in memory.
|
||||
/// Does not allocate memory (does not change the free memory pointer), but writes
|
||||
/// to memory starting at memPos and an unrestricted amount after that.
|
||||
std::string tupleEncoderPacked(TypePointers const& _givenTypes, TypePointers const& _targetTypes);
|
||||
/// If @reversed is true, the order of the variables after <headStart> is reversed.
|
||||
std::string tupleEncoderPacked(
|
||||
TypePointers const& _givenTypes,
|
||||
TypePointers const& _targetTypes,
|
||||
bool _reversed = false
|
||||
);
|
||||
|
||||
/// Specialization of tupleEncoderPacked to _reversed = true
|
||||
std::string tupleEncoderPackedReversed(TypePointers const& _givenTypes, TypePointers const& _targetTypes)
|
||||
{
|
||||
return tupleEncoderPacked(_givenTypes, _targetTypes, true);
|
||||
}
|
||||
|
||||
/// @returns name of an assembly function to ABI-decode values of @a _types
|
||||
/// into memory. If @a _fromMemory is true, decodes from memory instead of
|
||||
|
@ -49,6 +49,9 @@ void Compiler::compileContract(
|
||||
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
|
||||
|
||||
m_context.optimise(m_optimiserSettings);
|
||||
|
||||
solAssert(m_context.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
|
||||
solAssert(m_runtimeContext.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
|
||||
}
|
||||
|
||||
std::shared_ptr<evmasm::Assembly> Compiler::runtimeAssemblyPtr() const
|
||||
|
@ -192,6 +192,9 @@ void CompilerContext::appendMissingLowLevelFunctions()
|
||||
|
||||
pair<string, set<string>> CompilerContext::requestedYulFunctions()
|
||||
{
|
||||
solAssert(!m_requestedYulFunctionsRan, "requestedYulFunctions called more than once.");
|
||||
m_requestedYulFunctionsRan = true;
|
||||
|
||||
set<string> empty;
|
||||
swap(empty, m_externallyUsedYulFunctions);
|
||||
return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)};
|
||||
|
@ -167,6 +167,7 @@ public:
|
||||
/// Clears the internal list, i.e. calling it again will result in an
|
||||
/// empty return value.
|
||||
std::pair<std::string, std::set<std::string>> requestedYulFunctions();
|
||||
bool requestedYulFunctionsRan() const { return m_requestedYulFunctionsRan; }
|
||||
|
||||
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
||||
unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
|
||||
@ -389,6 +390,8 @@ private:
|
||||
YulUtilFunctions m_yulUtilFunctions;
|
||||
/// The queue of low-level functions to generate.
|
||||
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
||||
/// Flag to check that requestedYulFunctions() was called exactly once
|
||||
bool m_requestedYulFunctionsRan = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -559,8 +559,8 @@ void CompilerUtils::abiEncodeV2(
|
||||
|
||||
string encoderName =
|
||||
_padToWordBoundaries ?
|
||||
m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
|
||||
m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes);
|
||||
m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
|
||||
m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes);
|
||||
m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1);
|
||||
}
|
||||
|
||||
|
@ -103,8 +103,6 @@ void ContractCompiler::compileContract(
|
||||
// and adds the function to the compilation queue. Additionally internal functions,
|
||||
// which are referenced directly or indirectly will be added.
|
||||
appendFunctionSelector(_contract);
|
||||
// This processes the above populated queue until it is empty.
|
||||
appendMissingFunctions();
|
||||
}
|
||||
|
||||
size_t ContractCompiler::compileConstructor(
|
||||
@ -159,10 +157,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
|
||||
|
||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||
appendConstructor(*constructor);
|
||||
else if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
|
||||
appendBaseConstructor(*c);
|
||||
else
|
||||
{
|
||||
// Implicit constructors are always non-payable.
|
||||
appendCallValueCheck();
|
||||
if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
|
||||
appendBaseConstructor(*c);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
|
||||
@ -215,6 +216,9 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
|
||||
solAssert(!!m_runtimeCompiler, "");
|
||||
solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
|
||||
|
||||
appendMissingFunctions();
|
||||
m_runtimeCompiler->appendMissingFunctions();
|
||||
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||
|
||||
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
|
||||
@ -586,13 +590,13 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
if (!_function.isConstructor())
|
||||
// adding 1 for return address.
|
||||
m_context.adjustStackOffset(parametersSize + 1);
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters())
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters())
|
||||
{
|
||||
m_context.addVariable(*variable, parametersSize);
|
||||
parametersSize -= variable->annotation().type->sizeOnStack();
|
||||
}
|
||||
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _function.returnParameters())
|
||||
appendStackVariableInitialisation(*variable);
|
||||
|
||||
if (_function.isConstructor())
|
||||
@ -649,7 +653,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
if (stackLayout[i] != i)
|
||||
solAssert(false, "Invalid stack layout on cleanup.");
|
||||
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters())
|
||||
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters() + _function.returnParameters())
|
||||
m_context.removeVariable(*variable);
|
||||
|
||||
m_context.adjustStackOffset(-(int)c_returnValuesSize);
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include <libsolutil/Whiskers.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
@ -1591,6 +1590,16 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||
m_context << (u256{result} << (256 - 32));
|
||||
}
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||
|
||||
if (member == "min")
|
||||
m_context << integerType->min();
|
||||
else
|
||||
m_context << integerType->max();
|
||||
}
|
||||
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
||||
{
|
||||
// no-op
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
|
@ -28,9 +28,6 @@
|
||||
#include <libsolutil/Whiskers.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
@ -299,9 +296,6 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
||||
|
||||
string YulUtilFunctions::shiftRightFunctionDynamic()
|
||||
{
|
||||
// Note that if this is extended with signed shifts,
|
||||
// the opcodes SAR and SDIV behave differently with regards to rounding!
|
||||
|
||||
string const functionName = "shift_right_unsigned_dynamic";
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
@ -321,6 +315,86 @@ string YulUtilFunctions::shiftRightFunctionDynamic()
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::shiftRightSignedFunctionDynamic()
|
||||
{
|
||||
string const functionName = "shift_right_signed_dynamic";
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(bits, value) -> result {
|
||||
<?hasShifts>
|
||||
result := sar(bits, value)
|
||||
<!hasShifts>
|
||||
let divisor := exp(2, bits)
|
||||
let xor_mask := sub(0, slt(value, 0))
|
||||
result := xor(div(xor(value, xor_mask), divisor), xor_mask)
|
||||
// combined version of
|
||||
// switch slt(value, 0)
|
||||
// case 0 { result := div(value, divisor) }
|
||||
// default { result := not(div(not(value), divisor)) }
|
||||
</hasShifts>
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("hasShifts", m_evmVersion.hasBitwiseShifting())
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType)
|
||||
{
|
||||
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
|
||||
solAssert(_amountType.category() == Type::Category::Integer, "");
|
||||
string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(value, bits) -> result {
|
||||
bits := <cleanAmount>(bits)
|
||||
<?amountSigned>
|
||||
if slt(bits, 0) { invalid() }
|
||||
</amountSigned>
|
||||
result := <cleanup>(<shift>(bits, value))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
|
||||
("cleanAmount", cleanupFunction(_amountType))
|
||||
("shift", shiftLeftFunctionDynamic())
|
||||
("cleanup", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType)
|
||||
{
|
||||
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
|
||||
solAssert(_amountType.category() == Type::Category::Integer, "");
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(&_type);
|
||||
bool valueSigned = integerType && integerType->isSigned();
|
||||
|
||||
string const functionName = "shift_right_" + _type.identifier() + "_" + _amountType.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(value, bits) -> result {
|
||||
bits := <cleanAmount>(bits)
|
||||
<?amountSigned>
|
||||
if slt(bits, 0) { invalid() }
|
||||
</amountSigned>
|
||||
result := <cleanup>(<shift>(bits, <cleanup>(value)))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
|
||||
("cleanAmount", cleanupFunction(_amountType))
|
||||
("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic())
|
||||
("cleanup", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
||||
{
|
||||
solAssert(_numBytes <= 32, "");
|
||||
@ -393,6 +467,8 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> sum {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
<?signed>
|
||||
// overflow, if x >= 0 and y > (maxValue - x)
|
||||
if and(iszero(slt(x, 0)), sgt(y, sub(<maxValue>, x))) { revert(0, 0) }
|
||||
@ -409,6 +485,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
||||
("signed", _type.isSigned())
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -421,6 +498,8 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
||||
// Multiplication by zero could be treated separately and directly return zero.
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> product {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
<?signed>
|
||||
// overflow, if x > 0, y > 0 and x > (maxValue / y)
|
||||
if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(<maxValue>, y))) { revert(0, 0) }
|
||||
@ -441,6 +520,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
||||
("signed", _type.isSigned())
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -452,6 +532,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> r {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
if iszero(y) { revert(0, 0) }
|
||||
<?signed>
|
||||
// overflow for minVal / -1
|
||||
@ -466,6 +548,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
("functionName", functionName)
|
||||
("signed", _type.isSigned())
|
||||
("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -477,12 +560,15 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> r {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
if iszero(y) { revert(0, 0) }
|
||||
r := <?signed>s</signed>mod(x, y)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("signed", _type.isSigned())
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -494,6 +580,8 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> diff {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
<?signed>
|
||||
// underflow, if y >= 0 and x < (minValue + y)
|
||||
if and(iszero(slt(y, 0)), slt(x, add(<minValue>, y))) { revert(0, 0) }
|
||||
@ -509,6 +597,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
||||
("signed", _type.isSigned())
|
||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
@ -1033,13 +1122,12 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
if (_mappingType.keyType()->isDynamicallySized())
|
||||
return Whiskers(R"(
|
||||
function <functionName>(slot <comma> <key>) -> dataSlot {
|
||||
dataSlot := <hash>(slot <comma> <key>)
|
||||
function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
|
||||
dataSlot := <hash>(<key> <?+key>,</+key> slot)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("key", _keyType.sizeOnStack() > 0 ? "key" : "")
|
||||
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
|
||||
("hash", packedHashFunction(
|
||||
{&_keyType, TypeProvider::uint256()},
|
||||
{_mappingType.keyType(), TypeProvider::uint256()}
|
||||
@ -1764,8 +1852,18 @@ string YulUtilFunctions::cleanupFunction(Type const& _type)
|
||||
solUnimplemented("Fixed point types not implemented.");
|
||||
break;
|
||||
case Type::Category::Function:
|
||||
solAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External, "");
|
||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||
switch (dynamic_cast<FunctionType const&>(_type).kind())
|
||||
{
|
||||
case FunctionType::Kind::External:
|
||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||
break;
|
||||
case FunctionType::Kind::Internal:
|
||||
templ("body", "cleaned := value");
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Type::Category::Array:
|
||||
case Type::Category::Struct:
|
||||
@ -1938,14 +2036,16 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(value) -> ret {
|
||||
value := <cleanupFunction>(value)
|
||||
if <lt>(value, <minval>) { revert(0,0) }
|
||||
ret := sub(value, 1)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("lt", type.isSigned() ? "slt" : "lt")
|
||||
.render();
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("lt", type.isSigned() ? "slt" : "lt")
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
@ -1966,14 +2066,16 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(value) -> ret {
|
||||
value := <cleanupFunction>(value)
|
||||
if <gt>(value, <maxval>) { revert(0,0) }
|
||||
ret := add(value, 1)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("maxval", toCompactHexWithPrefix(maxintval))
|
||||
("gt", type.isSigned() ? "sgt" : "gt")
|
||||
.render();
|
||||
("functionName", functionName)
|
||||
("maxval", toCompactHexWithPrefix(maxintval))
|
||||
("gt", type.isSigned() ? "sgt" : "gt")
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
@ -1988,15 +2090,17 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(_value) -> ret {
|
||||
if slt(_value, <minval>) { revert(0,0) }
|
||||
ret := sub(0, _value)
|
||||
function <functionName>(value) -> ret {
|
||||
value := <cleanupFunction>(value)
|
||||
if slt(value, <minval>) { revert(0,0) }
|
||||
ret := sub(0, value)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
.render();
|
||||
});
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
|
||||
@ -2214,7 +2318,6 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
||||
}
|
||||
|
||||
solAssert(_type.isValueType(), "");
|
||||
|
||||
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||
if (funType->kind() == FunctionType::Kind::External)
|
||||
return Whiskers(R"(
|
||||
@ -2228,18 +2331,23 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
||||
("splitFunction", splitExternalFunctionIdFunction())
|
||||
.render();
|
||||
|
||||
|
||||
return Whiskers(R"(
|
||||
function <functionName>(memPtr) -> value {
|
||||
value := <load>(memPtr)
|
||||
<?needsValidation>
|
||||
function <functionName>(ptr) -> value {
|
||||
<?fromCalldata>
|
||||
value := calldataload(ptr)
|
||||
<validate>(value)
|
||||
</needsValidation>
|
||||
<!fromCalldata>
|
||||
value := <cleanup>(mload(ptr))
|
||||
</fromCalldata>
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("load", _fromCalldata ? "calldataload" : "mload")
|
||||
("needsValidation", _fromCalldata)
|
||||
("validate", _fromCalldata ? validatorFunction(_type) : "")
|
||||
("fromCalldata", _fromCalldata)
|
||||
("validate", validatorFunction(_type))
|
||||
// Byte array elements generally need cleanup.
|
||||
// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
|
||||
("cleanup", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
@ -81,6 +81,14 @@ public:
|
||||
std::string shiftLeftFunctionDynamic();
|
||||
std::string shiftRightFunction(size_t _numBits);
|
||||
std::string shiftRightFunctionDynamic();
|
||||
std::string shiftRightSignedFunctionDynamic();
|
||||
|
||||
/// @returns the name of a function that performs a left shift and subsequent cleanup
|
||||
/// and, if needed, prior cleanup.
|
||||
/// If the amount to shift by is signed, a check for negativeness is performed.
|
||||
/// signature: (value, amountToShift) -> result
|
||||
std::string typedShiftLeftFunction(Type const& _type, Type const& _amountType);
|
||||
std::string typedShiftRightFunction(Type const& _type, Type const& _amountType);
|
||||
|
||||
/// @returns the name of a function which replaces the
|
||||
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
|
||||
@ -204,7 +212,7 @@ public:
|
||||
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||
std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes);
|
||||
|
||||
/// @returns a function that reads a value type from memory.
|
||||
/// @returns a function that reads a value type from memory. Performs cleanup.
|
||||
/// signature: (addr) -> value
|
||||
std::string readFromMemory(Type const& _type);
|
||||
/// @returns a function that reads a value type from calldata.
|
||||
|
@ -38,9 +38,6 @@
|
||||
|
||||
#include <liblangutil/SourceReferenceFormatter.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
@ -172,24 +169,24 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||
string functionName = m_context.functionName(_function);
|
||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||
Whiskers t(R"(
|
||||
function <functionName>(<params>) <returns> {
|
||||
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
||||
<initReturnVariables>
|
||||
<body>
|
||||
}
|
||||
)");
|
||||
t("functionName", functionName);
|
||||
string params;
|
||||
vector<string> params;
|
||||
for (auto const& varDecl: _function.parameters())
|
||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||
t("params", params);
|
||||
string retParams;
|
||||
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||
t("params", joinHumanReadable(params));
|
||||
vector<string> retParams;
|
||||
string retInit;
|
||||
for (auto const& varDecl: _function.returnParameters())
|
||||
{
|
||||
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||
retParams += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||
retInit += generateInitialAssignment(*varDecl);
|
||||
}
|
||||
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
||||
t("retParams", joinHumanReadable(retParams));
|
||||
t("initReturnVariables", retInit);
|
||||
t("body", generate(_function.body()));
|
||||
return t.render();
|
||||
@ -202,11 +199,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
||||
|
||||
Type const* type = _varDecl.annotation().type;
|
||||
|
||||
solAssert(!_varDecl.isConstant(), "");
|
||||
solAssert(_varDecl.isStateVariable(), "");
|
||||
|
||||
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
|
||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||
solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), "");
|
||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
||||
solAssert(slot_offset.second == 0, "");
|
||||
FunctionType funType(_varDecl);
|
||||
@ -268,6 +265,16 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
||||
("id", to_string(_varDecl.id()))
|
||||
.render();
|
||||
}
|
||||
else if (_varDecl.isConstant())
|
||||
return Whiskers(R"(
|
||||
function <functionName>() -> <ret> {
|
||||
<ret> := <constantValueFunction>()
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
|
||||
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
|
||||
.render();
|
||||
else
|
||||
{
|
||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
||||
@ -293,31 +300,35 @@ string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDec
|
||||
return generator.code();
|
||||
}
|
||||
|
||||
pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstructorArguments(
|
||||
pair<string, map<ContractDefinition const*, vector<string>>> IRGenerator::evaluateConstructorArguments(
|
||||
ContractDefinition const& _contract
|
||||
)
|
||||
{
|
||||
map<ContractDefinition const*, string> constructorParams;
|
||||
map<ContractDefinition const*, vector<string>> constructorParams;
|
||||
vector<pair<ContractDefinition const*, std::vector<ASTPointer<Expression>>const *>> baseConstructorArguments;
|
||||
|
||||
for (ASTPointer<InheritanceSpecifier> const& base: _contract.baseContracts())
|
||||
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
|
||||
base->name().annotation().referencedDeclaration
|
||||
)->constructor(); baseConstructor && base->arguments())
|
||||
base->name().annotation().referencedDeclaration
|
||||
)->constructor(); baseConstructor && base->arguments())
|
||||
baseConstructorArguments.emplace_back(
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
base->arguments()
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
base->arguments()
|
||||
);
|
||||
|
||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||
for (auto const& modifier: constructor->modifiers())
|
||||
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
|
||||
for (ASTPointer<ModifierInvocation> const& modifier: constructor->modifiers())
|
||||
if (auto const* baseContract = dynamic_cast<ContractDefinition const*>(
|
||||
modifier->name()->annotation().referencedDeclaration
|
||||
)->constructor(); baseConstructor && modifier->arguments())
|
||||
baseConstructorArguments.emplace_back(
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
modifier->arguments()
|
||||
);
|
||||
))
|
||||
if (
|
||||
FunctionDefinition const* baseConstructor = baseContract->constructor();
|
||||
baseConstructor && modifier->arguments()
|
||||
)
|
||||
baseConstructorArguments.emplace_back(
|
||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||
modifier->arguments()
|
||||
);
|
||||
|
||||
IRGeneratorForStatements generator{m_context, m_utils};
|
||||
for (auto&& [baseContract, arguments]: baseConstructorArguments)
|
||||
@ -327,11 +338,11 @@ pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstr
|
||||
{
|
||||
vector<string> params;
|
||||
for (size_t i = 0; i < arguments->size(); ++i)
|
||||
params.emplace_back(generator.evaluateExpression(
|
||||
*(arguments->at(i)),
|
||||
*(baseContract->constructor()->parameters()[i]->type())
|
||||
).commaSeparatedList());
|
||||
constructorParams[baseContract] = joinHumanReadable(params);
|
||||
params += generator.evaluateExpression(
|
||||
*(arguments->at(i)),
|
||||
*(baseContract->constructor()->parameters()[i]->type())
|
||||
).stackSlots();
|
||||
constructorParams[baseContract] = std::move(params);
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,16 +363,16 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
|
||||
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
|
||||
{
|
||||
auto listAllParams = [&](
|
||||
map<ContractDefinition const*, string> const& baseParams) -> string
|
||||
map<ContractDefinition const*, vector<string>> const& baseParams) -> vector<string>
|
||||
{
|
||||
vector<string> params;
|
||||
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
||||
if (baseParams.count(contract))
|
||||
params.emplace_back(baseParams.at(contract));
|
||||
return joinHumanReadable(params);
|
||||
params += baseParams.at(contract);
|
||||
return params;
|
||||
};
|
||||
|
||||
map<ContractDefinition const*, string> baseConstructorParams;
|
||||
map<ContractDefinition const*, vector<string>> baseConstructorParams;
|
||||
for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i)
|
||||
{
|
||||
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
|
||||
@ -376,16 +387,16 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
||||
<userDefinedConstructorBody>
|
||||
}
|
||||
)");
|
||||
string params;
|
||||
vector<string> params;
|
||||
if (contract->constructor())
|
||||
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
|
||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
||||
t("params", params);
|
||||
string baseParamsString = listAllParams(baseConstructorParams);
|
||||
t("baseParams", baseParamsString);
|
||||
t("comma", !params.empty() && !baseParamsString.empty() ? ", " : "");
|
||||
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||
t("params", joinHumanReadable(params));
|
||||
vector<string> baseParams = listAllParams(baseConstructorParams);
|
||||
t("baseParams", joinHumanReadable(baseParams));
|
||||
t("comma", !params.empty() && !baseParams.empty() ? ", " : "");
|
||||
t("functionName", implicitConstructorName(*contract));
|
||||
pair<string, map<ContractDefinition const*, string>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
||||
pair<string, map<ContractDefinition const*, vector<string>>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
||||
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
|
||||
t("evalBaseArguments", evaluatedArgs.first);
|
||||
if (i < _contract.annotation().linearizedBaseContracts.size() - 1)
|
||||
@ -393,7 +404,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
||||
t("hasNextConstructor", true);
|
||||
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
|
||||
t("nextConstructor", implicitConstructorName(*nextContract));
|
||||
t("nextParams", listAllParams(baseConstructorParams));
|
||||
t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams)));
|
||||
}
|
||||
else
|
||||
t("hasNextConstructor", false);
|
||||
@ -468,10 +479,10 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
{
|
||||
// <functionName>
|
||||
<callValueCheck>
|
||||
<assignToParams> <abiDecode>(4, calldatasize())
|
||||
<assignToRetParams> <function>(<params>)
|
||||
<?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
|
||||
<?+retParams>let <retParams> := </+retParams> <function>(<params>)
|
||||
let memPos := <allocate>(0)
|
||||
let memEnd := <abiEncode>(memPos <comma> <retParams>)
|
||||
let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
|
||||
return(memPos, sub(memEnd, memPos))
|
||||
}
|
||||
</cases>
|
||||
@ -493,13 +504,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
|
||||
unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack();
|
||||
unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack();
|
||||
templ["assignToParams"] = paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := ";
|
||||
templ["assignToRetParams"] = retVars == 0 ? "" : "let " + suffixedVariableNameList("ret_", 0, retVars) + " := ";
|
||||
|
||||
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
|
||||
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
|
||||
templ["params"] = suffixedVariableNameList("param_", 0, paramVars);
|
||||
templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0);
|
||||
templ["retParams"] = suffixedVariableNameList("ret_", 0, retVars);
|
||||
|
||||
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
|
||||
templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
|
||||
@ -510,7 +519,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
|
||||
templ["allocate"] = m_utils.allocationFunction();
|
||||
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
|
||||
templ["comma"] = retVars == 0 ? "" : ", ";
|
||||
}
|
||||
t("cases", functions);
|
||||
if (FunctionDefinition const* fallback = _contract.fallbackFunction())
|
||||
|
@ -81,9 +81,8 @@ private:
|
||||
/// Evaluates constructor's arguments for all base contracts (listed in inheritance specifiers) of
|
||||
/// @a _contract
|
||||
/// @returns Pair of expressions needed to evaluate params and list of parameters in a map contract -> params
|
||||
std::pair<std::string, std::map<ContractDefinition const*, std::string>> evaluateConstructorArguments(
|
||||
ContractDefinition const& _contract
|
||||
);
|
||||
std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::string>>>
|
||||
evaluateConstructorArguments(ContractDefinition const& _contract);
|
||||
|
||||
/// Initializes state variables of
|
||||
/// @a _contract
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include <libsolutil/Keccak256.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
|
||||
using namespace std;
|
||||
@ -117,10 +116,12 @@ struct CopyTranslate: public yul::ASTCopier
|
||||
reference.isOffset == false && reference.isSlot == false,
|
||||
"Should not be called for offset/slot"
|
||||
);
|
||||
auto const& var = m_context.localVariable(*varDecl);
|
||||
solAssert(var.type().sizeOnStack() == 1, "");
|
||||
|
||||
return yul::Identifier{
|
||||
_identifier.location,
|
||||
yul::YulString{m_context.localVariable(*varDecl).name()}
|
||||
yul::YulString{var.commaSeparatedList()}
|
||||
};
|
||||
}
|
||||
|
||||
@ -178,6 +179,28 @@ IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expre
|
||||
return variable;
|
||||
}
|
||||
|
||||
string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
|
||||
{
|
||||
string functionName = "constant_" + _constant.name() + "_" + to_string(_constant.id());
|
||||
return m_context.functionCollector().createFunction(functionName, [&] {
|
||||
Whiskers templ(R"(
|
||||
function <functionName>() -> <ret> {
|
||||
<code>
|
||||
<ret> := <value>
|
||||
}
|
||||
)");
|
||||
templ("functionName", functionName);
|
||||
IRGeneratorForStatements generator(m_context, m_utils);
|
||||
solAssert(_constant.value(), "");
|
||||
Type const& constantType = *_constant.type();
|
||||
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
|
||||
templ("code", generator.code());
|
||||
templ("ret", IRVariable("ret", constantType).commaSeparatedList());
|
||||
|
||||
return templ.render();
|
||||
});
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
|
||||
{
|
||||
if (Expression const* expression = _varDeclStatement.initialValue())
|
||||
@ -230,29 +253,53 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
|
||||
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||
{
|
||||
_assignment.rightHandSide().accept(*this);
|
||||
Type const* intermediateType = type(_assignment.rightHandSide()).closestTemporaryType(
|
||||
&type(_assignment.leftHandSide())
|
||||
);
|
||||
IRVariable value = convert(_assignment.rightHandSide(), *intermediateType);
|
||||
|
||||
Token assignmentOperator = _assignment.assignmentOperator();
|
||||
Token binaryOperator =
|
||||
assignmentOperator == Token::Assign ?
|
||||
assignmentOperator :
|
||||
TokenTraits::AssignmentToBinaryOp(assignmentOperator);
|
||||
|
||||
Type const* rightIntermediateType =
|
||||
TokenTraits::isShiftOp(binaryOperator) ?
|
||||
type(_assignment.rightHandSide()).mobileType() :
|
||||
type(_assignment.rightHandSide()).closestTemporaryType(
|
||||
&type(_assignment.leftHandSide())
|
||||
);
|
||||
solAssert(rightIntermediateType, "");
|
||||
IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType);
|
||||
_assignment.leftHandSide().accept(*this);
|
||||
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||
|
||||
if (_assignment.assignmentOperator() != Token::Assign)
|
||||
if (assignmentOperator != Token::Assign)
|
||||
{
|
||||
solAssert(type(_assignment.leftHandSide()) == *intermediateType, "");
|
||||
solAssert(intermediateType->isValueType(), "Compound operators only available for value types.");
|
||||
|
||||
solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
|
||||
solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types.");
|
||||
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
|
||||
m_code << value.name() << " := " << binaryOperation(
|
||||
TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()),
|
||||
*intermediateType,
|
||||
leftIntermediate.name(),
|
||||
value.name()
|
||||
);
|
||||
if (TokenTraits::isShiftOp(binaryOperator))
|
||||
{
|
||||
solAssert(type(_assignment) == leftIntermediate.type(), "");
|
||||
solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
|
||||
define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value);
|
||||
|
||||
writeToLValue(*m_currentLValue, IRVariable(_assignment));
|
||||
m_currentLValue.reset();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(type(_assignment.leftHandSide()) == *rightIntermediateType, "");
|
||||
m_code << value.name() << " := " << binaryOperation(
|
||||
binaryOperator,
|
||||
*rightIntermediateType,
|
||||
leftIntermediate.name(),
|
||||
value.name()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
writeToLValue(*m_currentLValue, value);
|
||||
|
||||
m_currentLValue.reset();
|
||||
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
|
||||
define(_assignment, value);
|
||||
@ -477,12 +524,16 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (commonType->category() == Type::Category::RationalNumber)
|
||||
{
|
||||
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
|
||||
return false; // skip sub-expressions
|
||||
}
|
||||
|
||||
_binOp.leftExpression().accept(*this);
|
||||
_binOp.rightExpression().accept(*this);
|
||||
|
||||
if (commonType->category() == Type::Category::RationalNumber)
|
||||
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
|
||||
else if (TokenTraits::isCompareOp(op))
|
||||
if (TokenTraits::isCompareOp(op))
|
||||
{
|
||||
if (auto type = dynamic_cast<FunctionType const*>(commonType))
|
||||
{
|
||||
@ -496,9 +547,9 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
isSigned = type->isSigned();
|
||||
|
||||
string args =
|
||||
expressionAsType(_binOp.leftExpression(), *commonType) +
|
||||
expressionAsType(_binOp.leftExpression(), *commonType, true) +
|
||||
", " +
|
||||
expressionAsType(_binOp.rightExpression(), *commonType);
|
||||
expressionAsType(_binOp.rightExpression(), *commonType, true);
|
||||
|
||||
string expr;
|
||||
if (op == Token::Equal)
|
||||
@ -517,6 +568,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
solAssert(false, "Unknown comparison operator.");
|
||||
define(_binOp) << expr << "\n";
|
||||
}
|
||||
else if (TokenTraits::isShiftOp(op))
|
||||
{
|
||||
IRVariable left = convert(_binOp.leftExpression(), *commonType);
|
||||
IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType());
|
||||
define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
string left = expressionAsType(_binOp.leftExpression(), *commonType);
|
||||
@ -584,13 +641,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
break;
|
||||
case FunctionType::Kind::Internal:
|
||||
{
|
||||
vector<string> args;
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
if (functionType->takesArbitraryParameters())
|
||||
args.emplace_back(IRVariable(*arguments[i]).commaSeparatedList());
|
||||
else
|
||||
args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList());
|
||||
|
||||
optional<FunctionDefinition const*> functionDef;
|
||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
||||
{
|
||||
@ -628,6 +678,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(functionDef.has_value(), "");
|
||||
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
|
||||
|
||||
vector<string> args;
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
if (functionType->takesArbitraryParameters())
|
||||
args += IRVariable(*arguments[i]).stackSlots();
|
||||
else
|
||||
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
|
||||
|
||||
if (functionDef.value() != nullptr)
|
||||
define(_functionCall) <<
|
||||
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
|
||||
@ -649,10 +706,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
case FunctionType::Kind::External:
|
||||
case FunctionType::Kind::DelegateCall:
|
||||
appendExternalFunctionCall(_functionCall, arguments);
|
||||
break;
|
||||
case FunctionType::Kind::BareCall:
|
||||
case FunctionType::Kind::BareDelegateCall:
|
||||
case FunctionType::Kind::BareStaticCall:
|
||||
appendExternalFunctionCall(_functionCall, arguments);
|
||||
appendBareCall(_functionCall, arguments);
|
||||
break;
|
||||
case FunctionType::Kind::BareCallCode:
|
||||
solAssert(false, "Callcode has been removed.");
|
||||
@ -663,7 +722,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||
|
||||
vector<IRVariable> indexedArgs;
|
||||
string nonIndexedArgs;
|
||||
vector<string> nonIndexedArgs;
|
||||
TypePointers nonIndexedArgTypes;
|
||||
TypePointers nonIndexedParamTypes;
|
||||
if (!event.isAnonymous())
|
||||
@ -686,10 +745,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
}
|
||||
else
|
||||
{
|
||||
string vars = IRVariable(arg).commaSeparatedList();
|
||||
if (!vars.empty())
|
||||
// In reverse because abi_encode expects it like that.
|
||||
nonIndexedArgs = ", " + move(vars) + nonIndexedArgs;
|
||||
nonIndexedArgs += IRVariable(arg).stackSlots();
|
||||
nonIndexedArgTypes.push_back(arg.annotation().type);
|
||||
nonIndexedParamTypes.push_back(paramTypes[i]);
|
||||
}
|
||||
@ -704,7 +760,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("freeMemory", freeMemory());
|
||||
templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
|
||||
templ("nonIndexedArgs", nonIndexedArgs);
|
||||
templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs));
|
||||
templ("log", "log" + to_string(indexedArgs.size()));
|
||||
templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) {
|
||||
return _arg.commaSeparatedList();
|
||||
@ -731,13 +787,134 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::ABIEncode:
|
||||
case FunctionType::Kind::ABIEncodePacked:
|
||||
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||
{
|
||||
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||
solAssert(functionType->padArguments() != isPacked, "");
|
||||
bool const hasSelectorOrSignature =
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature;
|
||||
|
||||
TypePointers argumentTypes;
|
||||
TypePointers targetTypes;
|
||||
vector<string> argumentVars;
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
// ignore selector
|
||||
if (hasSelectorOrSignature && i == 0)
|
||||
continue;
|
||||
argumentTypes.emplace_back(&type(*arguments[i]));
|
||||
targetTypes.emplace_back(type(*arguments[i]).fullEncodingType(false, true, isPacked));
|
||||
argumentVars += IRVariable(*arguments[i]).stackSlots();
|
||||
}
|
||||
|
||||
string selector;
|
||||
if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||
{
|
||||
// hash the signature
|
||||
Type const& selectorType = type(*arguments.front());
|
||||
if (auto const* stringType = dynamic_cast<StringLiteralType const*>(&selectorType))
|
||||
{
|
||||
FixedHash<4> hash(keccak256(stringType->value()));
|
||||
selector = formatNumber(u256(FixedHash<4>::Arith(hash)) << (256 - 32));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Used to reset the free memory pointer later.
|
||||
string freeMemoryPre = m_context.newYulVariable();
|
||||
m_code << "let " << freeMemoryPre << " := " << freeMemory() << "\n";
|
||||
IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory());
|
||||
IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32));
|
||||
|
||||
define(hashVariable) <<
|
||||
"keccak256(" <<
|
||||
m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()) <<
|
||||
"(" <<
|
||||
array.commaSeparatedList() <<
|
||||
"), " <<
|
||||
m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) <<
|
||||
"(" <<
|
||||
array.commaSeparatedList() <<
|
||||
"))\n";
|
||||
IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4));
|
||||
define(selectorVariable, hashVariable);
|
||||
m_code << "mstore(" << to_string(CompilerUtils::freeMemoryPointer) << ", " << freeMemoryPre << ")\n";
|
||||
}
|
||||
}
|
||||
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector)
|
||||
selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name();
|
||||
|
||||
Whiskers templ(R"(
|
||||
let <data> := <allocateTemporary>()
|
||||
let <mpos> := add(<data>, 0x20)
|
||||
<?+selector>
|
||||
mstore(<mpos>, <selector>)
|
||||
<mpos> := add(<mpos>, 4)
|
||||
</+selector>
|
||||
let <mend> := <encode>(<mpos><arguments>)
|
||||
mstore(<data>, sub(<mend>, add(<data>, 0x20)))
|
||||
mstore(<freeMemPtr>, <roundUp>(<mend>))
|
||||
)");
|
||||
templ("data", IRVariable(_functionCall).part("mpos").name());
|
||||
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
|
||||
templ("mpos", m_context.newYulVariable());
|
||||
templ("mend", m_context.newYulVariable());
|
||||
templ("selector", selector);
|
||||
templ("encode",
|
||||
isPacked ?
|
||||
m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) :
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false)
|
||||
);
|
||||
templ("arguments", joinHumanReadablePrefixed(argumentVars));
|
||||
templ("freeMemPtr", to_string(CompilerUtils::freeMemoryPointer));
|
||||
templ("roundUp", m_utils.roundUpFunction());
|
||||
|
||||
m_code << templ.render();
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::Revert:
|
||||
{
|
||||
solAssert(arguments.size() == parameterTypes.size(), "");
|
||||
if (arguments.empty())
|
||||
m_code << "revert(0, 0)\n";
|
||||
else
|
||||
solUnimplementedAssert(false, "");
|
||||
{
|
||||
solAssert(arguments.size() == 1, "");
|
||||
|
||||
if (m_context.revertStrings() == RevertStrings::Strip)
|
||||
m_code << "revert(0, 0)\n";
|
||||
else
|
||||
{
|
||||
solAssert(type(*arguments.front()).isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),"");
|
||||
|
||||
Whiskers templ(R"({
|
||||
let <pos> := <allocateTemporary>()
|
||||
mstore(<pos>, <hash>)
|
||||
let <end> := <encode>(add(<pos>, 4) <argumentVars>)
|
||||
revert(<pos>, sub(<end>, <pos>))
|
||||
})");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ(
|
||||
"hash",
|
||||
(u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str()
|
||||
);
|
||||
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
|
||||
templ(
|
||||
"argumentVars",
|
||||
joinHumanReadablePrefixed(IRVariable{*arguments.front()}.stackSlots())
|
||||
);
|
||||
templ("encode", m_context.abiFunctions().tupleEncoder(
|
||||
{&type(*arguments.front())},
|
||||
{TypeProvider::stringMemory()}
|
||||
));
|
||||
|
||||
m_code << templ.render();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@ -760,6 +937,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
solAssert(arguments.size() == 1, "");
|
||||
|
||||
ArrayType const* arrayType = TypeProvider::bytesMemory();
|
||||
|
||||
auto array = convert(*arguments[0], *arrayType);
|
||||
|
||||
define(_functionCall) <<
|
||||
@ -905,11 +1083,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
);
|
||||
|
||||
TypePointers argumentTypes;
|
||||
string constructorParams;
|
||||
vector<string> constructorParams;
|
||||
for (ASTPointer<Expression const> const& arg: arguments)
|
||||
{
|
||||
argumentTypes.push_back(arg->annotation().type);
|
||||
constructorParams += ", " + IRVariable{*arg}.commaSeparatedList();
|
||||
constructorParams += IRVariable{*arg}.stackSlots();
|
||||
}
|
||||
|
||||
ContractDefinition const* contract =
|
||||
@ -935,9 +1113,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
||||
t("object", m_context.creationObjectName(*contract));
|
||||
t("abiEncode",
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false)
|
||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
|
||||
);
|
||||
t("constructorParams", constructorParams);
|
||||
t("constructorParams", joinHumanReadablePrefixed(constructorParams));
|
||||
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
t("saltSet", functionType->saltSet());
|
||||
if (functionType->saltSet())
|
||||
@ -1126,7 +1304,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
solAssert(false, "Blockhash has been removed.");
|
||||
else if (member == "creationCode" || member == "runtimeCode")
|
||||
{
|
||||
solUnimplementedAssert(false, "");
|
||||
solUnimplementedAssert(member != "runtimeCode", "");
|
||||
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||
m_context.subObjectsCreated().insert(&contract);
|
||||
m_code << Whiskers(R"(
|
||||
let <size> := datasize("<objectName>")
|
||||
let <result> := <allocationFunction>(add(<size>, 32))
|
||||
mstore(<result>, <size>)
|
||||
datacopy(add(<result>, 32), dataoffset("<objectName>"), <size>)
|
||||
)")
|
||||
("allocationFunction", m_utils.allocationFunction())
|
||||
("size", m_context.newYulVariable())
|
||||
("objectName", m_context.creationObjectName(contract))
|
||||
("result", IRVariable(_memberAccess).commaSeparatedList()).render();
|
||||
}
|
||||
else if (member == "name")
|
||||
{
|
||||
@ -1141,6 +1332,16 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n";
|
||||
}
|
||||
else if (member == "min" || member == "max")
|
||||
{
|
||||
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||
|
||||
if (member == "min")
|
||||
define(_memberAccess) << formatNumber(integerType->min()) << "\n";
|
||||
else
|
||||
define(_memberAccess) << formatNumber(integerType->max()) << "\n";
|
||||
}
|
||||
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
||||
{
|
||||
// no-op
|
||||
@ -1312,14 +1513,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
||||
solAssert(keyType.sizeOnStack() <= 1, "");
|
||||
|
||||
string slot = m_context.newYulVariable();
|
||||
Whiskers templ("let <slot> := <indexAccess>(<base> <key>)\n");
|
||||
Whiskers templ("let <slot> := <indexAccess>(<base><?+key>,<key></+key>)\n");
|
||||
templ("slot", slot);
|
||||
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
|
||||
templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
|
||||
if (keyType.sizeOnStack() == 0)
|
||||
templ("key", "");
|
||||
else
|
||||
templ("key", ", " + IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
|
||||
templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
|
||||
m_code << templ.render();
|
||||
setLValue(_indexAccess, IRLValue{
|
||||
*_indexAccess.annotation().type,
|
||||
@ -1542,11 +1740,9 @@ void IRGeneratorForStatements::handleVariableReference(
|
||||
Expression const& _referencingExpression
|
||||
)
|
||||
{
|
||||
// TODO for the constant case, we have to be careful:
|
||||
// If the value is visited twice, `defineExpression` is called twice on
|
||||
// the same expression.
|
||||
solUnimplementedAssert(!_variable.isConstant(), "");
|
||||
if (_variable.isStateVariable() && _variable.immutable())
|
||||
if (_variable.isStateVariable() && _variable.isConstant())
|
||||
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
|
||||
else if (_variable.isStateVariable() && _variable.immutable())
|
||||
setLValue(_referencingExpression, IRLValue{
|
||||
*_variable.annotation().type,
|
||||
IRLValue::Immutable{&_variable}
|
||||
@ -1574,18 +1770,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
)
|
||||
{
|
||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||
solAssert(
|
||||
funType.takesArbitraryParameters() ||
|
||||
_arguments.size() == funType.parameterTypes().size(), ""
|
||||
);
|
||||
solUnimplementedAssert(!funType.bound(), "");
|
||||
solAssert(!funType.takesArbitraryParameters(), "");
|
||||
solAssert(_arguments.size() == funType.parameterTypes().size(), "");
|
||||
solAssert(!funType.isBareCall(), "");
|
||||
FunctionType::Kind const funKind = funType.kind();
|
||||
|
||||
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
||||
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
||||
solAssert(
|
||||
funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall,
|
||||
"Can only be used for regular external calls."
|
||||
);
|
||||
|
||||
bool const isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
||||
bool const useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
|
||||
solUnimplementedAssert(!funType.bound(), "");
|
||||
|
||||
bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
|
||||
|
||||
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
|
||||
|
||||
@ -1594,12 +1792,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
for (auto const& arg: _arguments)
|
||||
{
|
||||
argumentTypes.emplace_back(&type(*arg));
|
||||
if (IRVariable(*arg).type().sizeOnStack() > 0)
|
||||
argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList());
|
||||
argumentStrings += IRVariable(*arg).stackSlots();
|
||||
}
|
||||
string argumentString = argumentStrings.empty() ? ""s : (", " + joinHumanReadable(argumentStrings));
|
||||
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
|
||||
if (!m_context.evmVersion().canOverchargeGasForCall())
|
||||
{
|
||||
@ -1611,33 +1806,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
|
||||
}
|
||||
|
||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||
|
||||
Whiskers templ(R"(
|
||||
<?checkExistence>
|
||||
if iszero(extcodesize(<address>)) { revert(0, 0) }
|
||||
</checkExistence>
|
||||
if iszero(extcodesize(<address>)) { revert(0, 0) }
|
||||
|
||||
// storage for arguments and returned data
|
||||
let <pos> := <freeMemory>
|
||||
<?bareCall>
|
||||
<!bareCall>
|
||||
mstore(<pos>, <shl28>(<funId>))
|
||||
</bareCall>
|
||||
let <end> := <encodeArgs>(
|
||||
<?bareCall>
|
||||
<pos>
|
||||
<!bareCall>
|
||||
add(<pos>, 4)
|
||||
</bareCall>
|
||||
<argumentString>
|
||||
)
|
||||
mstore(<pos>, <shl28>(<funId>))
|
||||
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
|
||||
|
||||
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
|
||||
<?noTryCall>
|
||||
if iszero(<success>) { <forwardingRevert>() }
|
||||
</noTryCall>
|
||||
<?hasRetVars> let <retVars> </hasRetVars>
|
||||
<?+retVars> let <retVars> </+retVars>
|
||||
if <success> {
|
||||
<?dynamicReturnSize>
|
||||
// copy dynamic return data out
|
||||
@ -1648,12 +1829,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
|
||||
|
||||
// decode return parameters from external try-call into retVars
|
||||
<?hasRetVars> <retVars> := </hasRetVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
||||
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
||||
}
|
||||
)");
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
templ("bareCall", funType.isBareCall());
|
||||
if (_functionCall.annotation().tryCall)
|
||||
templ("success", m_context.trySuccessConditionVariable(_functionCall));
|
||||
else
|
||||
@ -1661,17 +1841,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
templ("freeMemory", freeMemory());
|
||||
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
|
||||
|
||||
if (!funType.isBareCall())
|
||||
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
|
||||
|
||||
if (funKind == FunctionType::Kind::ECRecover)
|
||||
templ("address", "1");
|
||||
else if (funKind == FunctionType::Kind::SHA256)
|
||||
templ("address", "2");
|
||||
else if (funKind == FunctionType::Kind::RIPEMD160)
|
||||
templ("address", "3");
|
||||
else
|
||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
|
||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||
|
||||
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
|
||||
// This ensures it can catch badly formatted input from external calls.
|
||||
@ -1684,39 +1855,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
|
||||
string const retVars = IRVariable(_functionCall).commaSeparatedList();
|
||||
templ("retVars", retVars);
|
||||
templ("hasRetVars", !retVars.empty());
|
||||
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
|
||||
|
||||
templ("roundUp", m_utils.roundUpFunction());
|
||||
templ("abiDecode", abi.tupleDecoder(returnInfo.returnTypes, true));
|
||||
templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
|
||||
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
|
||||
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||
|
||||
templ("noTryCall", !_functionCall.annotation().tryCall);
|
||||
|
||||
// If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place.
|
||||
// Move arguments to memory, will not update the free memory pointer (but will update the memory
|
||||
// pointer on the stack).
|
||||
bool encodeInPlace = funType.takesArbitraryParameters() || funType.isBareCall();
|
||||
if (funType.kind() == FunctionType::Kind::ECRecover)
|
||||
// This would be the only combination of padding and in-place encoding,
|
||||
// but all parameters of ecrecover are value types anyway.
|
||||
encodeInPlace = false;
|
||||
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
||||
|
||||
solUnimplementedAssert(encodeInPlace == !funType.padArguments(), "");
|
||||
if (encodeInPlace)
|
||||
{
|
||||
solUnimplementedAssert(!encodeForLibraryCall, "");
|
||||
templ("encodeArgs", abi.tupleEncoderPacked(argumentTypes, funType.parameterTypes()));
|
||||
}
|
||||
else
|
||||
templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
||||
templ("argumentString", argumentString);
|
||||
|
||||
// Output data will replace input data, unless we have ECRecover (then, output
|
||||
// area will be 32 bytes just before input area).
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
solAssert(funType.padArguments(), "");
|
||||
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
||||
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
||||
|
||||
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
|
||||
solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
|
||||
@ -1724,10 +1876,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
templ("hasValue", !isDelegateCall && !useStaticCall);
|
||||
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
|
||||
// Check that the target contract exists (has code) for non-low-level calls.
|
||||
bool checkExistence = (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall);
|
||||
templ("checkExistence", checkExistence);
|
||||
|
||||
if (funType.gasSet())
|
||||
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
|
||||
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||
@ -1740,8 +1888,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||
if (funType.valueSet())
|
||||
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
||||
if (!checkExistence)
|
||||
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
|
||||
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||
}
|
||||
// Order is important here, STATICCALL might overlap with DELEGATECALL.
|
||||
@ -1754,8 +1900,103 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
|
||||
templ("forwardingRevert", m_utils.forwardingRevertFunction());
|
||||
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, "");
|
||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
||||
m_code << templ.render();
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::appendBareCall(
|
||||
FunctionCall const& _functionCall,
|
||||
vector<ASTPointer<Expression const>> const& _arguments
|
||||
)
|
||||
{
|
||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||
solAssert(
|
||||
!funType.bound() &&
|
||||
!funType.takesArbitraryParameters() &&
|
||||
_arguments.size() == 1 &&
|
||||
funType.parameterTypes().size() == 1, ""
|
||||
);
|
||||
FunctionType::Kind const funKind = funType.kind();
|
||||
|
||||
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
||||
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
||||
solAssert(
|
||||
funKind == FunctionType::Kind::BareCall ||
|
||||
funKind == FunctionType::Kind::BareDelegateCall ||
|
||||
funKind == FunctionType::Kind::BareStaticCall, ""
|
||||
);
|
||||
|
||||
solAssert(!_functionCall.annotation().tryCall, "");
|
||||
Whiskers templ(R"(
|
||||
<?needsEncoding>
|
||||
let <pos> := mload(<freeMemoryPointer>)
|
||||
let <length> := sub(<encode>(<pos> <?+arg>,</+arg> <arg>), <pos>)
|
||||
<!needsEncoding>
|
||||
let <pos> := add(<arg>, 0x20)
|
||||
let <length> := mload(<arg>)
|
||||
</needsEncoding>
|
||||
|
||||
let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0)
|
||||
<?+returndataVar>
|
||||
let <returndataVar> := <extractReturndataFunction>()
|
||||
</+returndataVar>
|
||||
)");
|
||||
|
||||
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("length", m_context.newYulVariable());
|
||||
|
||||
templ("arg", IRVariable(*_arguments.front()).commaSeparatedList());
|
||||
Type const& argType = type(*_arguments.front());
|
||||
if (argType == *TypeProvider::bytesMemory() || argType == *TypeProvider::stringMemory())
|
||||
templ("needsEncoding", false);
|
||||
else
|
||||
{
|
||||
templ("needsEncoding", true);
|
||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||
templ("encode", abi.tupleEncoderPacked({&argType}, {TypeProvider::bytesMemory()}));
|
||||
}
|
||||
|
||||
templ("success", IRVariable(_functionCall).tupleComponent(0).name());
|
||||
if (IRVariable(_functionCall).tupleComponent(1).type().category() == Type::Category::InaccessibleDynamic)
|
||||
templ("returndataVar", "");
|
||||
else
|
||||
{
|
||||
templ("returndataVar", IRVariable(_functionCall).tupleComponent(1).part("mpos").name());
|
||||
templ("extractReturndataFunction", m_utils.extractReturndataFunction());
|
||||
}
|
||||
|
||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||
|
||||
if (funKind == FunctionType::Kind::BareCall)
|
||||
{
|
||||
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||
templ("call", "call");
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall.");
|
||||
templ("value", "");
|
||||
if (funKind == FunctionType::Kind::BareStaticCall)
|
||||
templ("call", "staticcall");
|
||||
else
|
||||
templ("call", "delegatecall");
|
||||
}
|
||||
|
||||
if (funType.gasSet())
|
||||
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
|
||||
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||
// Send all gas (requires tangerine whistle EVM)
|
||||
templ("gas", "gas()");
|
||||
else
|
||||
{
|
||||
// send all gas except the amount needed to execute "SUB" and "CALL"
|
||||
// @todo this retains too much gas for now, needs to be fine-tuned.
|
||||
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||
if (funType.valueSet())
|
||||
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
||||
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
|
||||
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||
}
|
||||
|
||||
m_code << templ.render();
|
||||
}
|
||||
@ -1777,11 +2018,16 @@ IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const
|
||||
}
|
||||
}
|
||||
|
||||
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to)
|
||||
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup)
|
||||
{
|
||||
IRVariable from(_expression);
|
||||
if (from.type() == _to)
|
||||
return from.commaSeparatedList();
|
||||
{
|
||||
if (_forceCleanup)
|
||||
return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")";
|
||||
else
|
||||
return from.commaSeparatedList();
|
||||
}
|
||||
else
|
||||
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
||||
}
|
||||
@ -1860,6 +2106,10 @@ string IRGeneratorForStatements::binaryOperation(
|
||||
string const& _right
|
||||
)
|
||||
{
|
||||
solAssert(
|
||||
!TokenTraits::isShiftOp(_operator),
|
||||
"Have to use specific shift operation function for shifts."
|
||||
);
|
||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
||||
{
|
||||
string fun;
|
||||
@ -1903,6 +2153,31 @@ string IRGeneratorForStatements::binaryOperation(
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string IRGeneratorForStatements::shiftOperation(
|
||||
langutil::Token _operator,
|
||||
IRVariable const& _value,
|
||||
IRVariable const& _amountToShift
|
||||
)
|
||||
{
|
||||
IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type());
|
||||
solAssert(amountType, "");
|
||||
|
||||
solAssert(_operator == Token::SHL || _operator == Token::SAR, "");
|
||||
|
||||
return
|
||||
Whiskers(R"(
|
||||
<shift>(<value>, <amount>)
|
||||
)")
|
||||
("shift",
|
||||
_operator == Token::SHL ?
|
||||
m_utils.typedShiftLeftFunction(_value.type(), *amountType) :
|
||||
m_utils.typedShiftRightFunction(_value.type(), *amountType)
|
||||
)
|
||||
("value", _value.name())
|
||||
("amount", _amountToShift.name())
|
||||
.render();
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
|
||||
{
|
||||
langutil::Token const op = _binOp.getOperator();
|
||||
@ -2021,13 +2296,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
||||
")\n";
|
||||
},
|
||||
[&](IRLValue::Memory const& _memory) {
|
||||
if (_memory.byteArrayElement)
|
||||
define(result) <<
|
||||
m_utils.cleanupFunction(_lvalue.type) <<
|
||||
"(mload(" <<
|
||||
_memory.address <<
|
||||
"))\n";
|
||||
else if (_lvalue.type.isValueType())
|
||||
if (_lvalue.type.isValueType())
|
||||
define(result) <<
|
||||
m_utils.readFromMemory(_lvalue.type) <<
|
||||
"(" <<
|
||||
|
@ -54,6 +54,10 @@ public:
|
||||
/// Calculates expression's value and returns variable where it was stored
|
||||
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
|
||||
|
||||
/// @returns the name of a function that computes the value of the given constant
|
||||
/// and also generates the function.
|
||||
std::string constantValueFunction(VariableDeclaration const& _constant);
|
||||
|
||||
void endVisit(VariableDeclarationStatement const& _variableDeclaration) override;
|
||||
bool visit(Conditional const& _conditional) override;
|
||||
bool visit(Assignment const& _assignment) override;
|
||||
@ -102,6 +106,13 @@ private:
|
||||
std::vector<ASTPointer<Expression const>> const& _arguments
|
||||
);
|
||||
|
||||
/// Appends code for .call / .delegatecall / .staticcall.
|
||||
/// All involved expressions have already been visited.
|
||||
void appendBareCall(
|
||||
FunctionCall const& _functionCall,
|
||||
std::vector<ASTPointer<Expression const>> const& _arguments
|
||||
);
|
||||
|
||||
/// @returns code that evaluates to the first unused memory slot (which does not have to
|
||||
/// be empty).
|
||||
static std::string freeMemory();
|
||||
@ -112,7 +123,8 @@ private:
|
||||
|
||||
/// @returns a Yul expression representing the current value of @a _expression,
|
||||
/// converted to type @a _to if it does not yet have that type.
|
||||
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
||||
/// If @a _forceCleanup is set to true, it also cleans the value, in case it already has type @a _to.
|
||||
std::string expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup = false);
|
||||
|
||||
/// @returns an output stream that can be used to define @a _var using a function call or
|
||||
/// single stack slot expression.
|
||||
@ -142,6 +154,11 @@ private:
|
||||
std::string const& _right
|
||||
);
|
||||
|
||||
/// @returns code to perform the given shift operation.
|
||||
/// The operation itself will be performed in the type of the value,
|
||||
/// while the amount to shift can have its own type.
|
||||
std::string shiftOperation(langutil::Token _op, IRVariable const& _value, IRVariable const& _shiftAmount);
|
||||
|
||||
/// Assigns the value of @a _value to the lvalue @a _lvalue.
|
||||
void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value);
|
||||
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue.
|
||||
|
@ -54,7 +54,7 @@ IRVariable IRVariable::part(string const& _name) const
|
||||
solAssert(itemName.empty() || itemType, "");
|
||||
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type};
|
||||
}
|
||||
solAssert(false, "Invalid stack item name.");
|
||||
solAssert(false, "Invalid stack item name: " + _name);
|
||||
}
|
||||
|
||||
vector<string> IRVariable::stackSlots() const
|
||||
@ -89,7 +89,7 @@ string IRVariable::name() const
|
||||
{
|
||||
solAssert(m_type.sizeOnStack() == 1, "");
|
||||
auto const& [itemName, type] = m_type.stackItems().front();
|
||||
solAssert(!type, "");
|
||||
solAssert(!type, "Expected null type for name " + itemName);
|
||||
return suffixedName(itemName);
|
||||
}
|
||||
|
||||
|
@ -69,10 +69,11 @@ public:
|
||||
/// The returned IRVariable is itself typed with the type of the stack slot as defined
|
||||
/// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
|
||||
IRVariable part(std::string const& _slot) const;
|
||||
private:
|
||||
|
||||
/// @returns a vector containing the names of the stack slots of the variable.
|
||||
std::vector<std::string> stackSlots() const;
|
||||
|
||||
private:
|
||||
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix,
|
||||
/// unless @a _suffix is empty, in which case the base name itself is returned.
|
||||
std::string suffixedName(std::string const& _suffix) const;
|
||||
|
@ -21,8 +21,6 @@
|
||||
#include <libsolidity/formal/SymbolicState.h>
|
||||
#include <libsolidity/formal/SymbolicTypes.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
@ -44,6 +42,7 @@ BMC::BMC(
|
||||
if (_enabledSolvers.some())
|
||||
if (!_smtlib2Responses.empty())
|
||||
m_errorReporter.warning(
|
||||
5622_error,
|
||||
"SMT-LIB2 query responses were given in the auxiliary input, "
|
||||
"but this Solidity binary uses an SMT solver (Z3/CVC4) directly."
|
||||
"These responses will be ignored."
|
||||
@ -74,6 +73,7 @@ void BMC::analyze(SourceUnit const& _source, set<Expression const*> _safeAsserti
|
||||
{
|
||||
m_noSolverWarning = true;
|
||||
m_outerErrorReporter.warning(
|
||||
8084_error,
|
||||
SourceLocation(),
|
||||
"BMC analysis was not possible since no integrated SMT solver (Z3 or CVC4) was found."
|
||||
);
|
||||
@ -467,6 +467,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall)
|
||||
inlineFunctionCall(_funCall);
|
||||
else if (funType.kind() == FunctionType::Kind::Internal)
|
||||
m_errorReporter.warning(
|
||||
5729_error,
|
||||
_funCall.location(),
|
||||
"Assertion checker does not yet implement this type of function call."
|
||||
);
|
||||
@ -591,8 +592,7 @@ void BMC::checkConstantCondition(BMCVerificationTarget& _target)
|
||||
*_target.expression,
|
||||
_target.constraints,
|
||||
_target.value,
|
||||
_target.callStack,
|
||||
"Condition is always $VALUE."
|
||||
_target.callStack
|
||||
);
|
||||
}
|
||||
|
||||
@ -610,6 +610,8 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smt::Expression const&
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
4144_error,
|
||||
8312_error,
|
||||
"Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")",
|
||||
"<result>",
|
||||
&_target.value
|
||||
@ -630,6 +632,8 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smt::Expression const& _
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
2661_error,
|
||||
8065_error,
|
||||
"Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")",
|
||||
"<result>",
|
||||
&_target.value
|
||||
@ -644,6 +648,8 @@ void BMC::checkDivByZero(BMCVerificationTarget& _target)
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
3046_error,
|
||||
5272_error,
|
||||
"Division by zero",
|
||||
"<result>",
|
||||
&_target.value
|
||||
@ -658,6 +664,8 @@ void BMC::checkBalance(BMCVerificationTarget& _target)
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
1236_error,
|
||||
4010_error,
|
||||
"Insufficient funds",
|
||||
"address(this).balance"
|
||||
);
|
||||
@ -672,6 +680,8 @@ void BMC::checkAssert(BMCVerificationTarget& _target)
|
||||
_target.callStack,
|
||||
_target.modelExpressions,
|
||||
_target.expression->location(),
|
||||
4661_error,
|
||||
7812_error,
|
||||
"Assertion violation"
|
||||
);
|
||||
}
|
||||
@ -702,9 +712,11 @@ void BMC::addVerificationTarget(
|
||||
|
||||
void BMC::checkCondition(
|
||||
smt::Expression _condition,
|
||||
vector<SMTEncoder::CallStackEntry> const& callStack,
|
||||
vector<SMTEncoder::CallStackEntry> const& _callStack,
|
||||
pair<vector<smt::Expression>, vector<string>> const& _modelExpressions,
|
||||
SourceLocation const& _location,
|
||||
ErrorId _errorHappens,
|
||||
ErrorId _errorMightHappen,
|
||||
string const& _description,
|
||||
string const& _additionalValueName,
|
||||
smt::Expression const* _additionalValue
|
||||
@ -716,7 +728,7 @@ void BMC::checkCondition(
|
||||
vector<smt::Expression> expressionsToEvaluate;
|
||||
vector<string> expressionNames;
|
||||
tie(expressionsToEvaluate, expressionNames) = _modelExpressions;
|
||||
if (callStack.size())
|
||||
if (_callStack.size())
|
||||
if (_additionalValue)
|
||||
{
|
||||
expressionsToEvaluate.emplace_back(*_additionalValue);
|
||||
@ -747,7 +759,7 @@ void BMC::checkCondition(
|
||||
{
|
||||
std::ostringstream message;
|
||||
message << _description << " happens here";
|
||||
if (callStack.size())
|
||||
if (_callStack.size())
|
||||
{
|
||||
std::ostringstream modelMessage;
|
||||
modelMessage << " for:\n";
|
||||
@ -760,30 +772,31 @@ void BMC::checkCondition(
|
||||
for (auto const& eval: sortedModel)
|
||||
modelMessage << " " << eval.first << " = " << eval.second << "\n";
|
||||
m_errorReporter.warning(
|
||||
_errorHappens,
|
||||
_location,
|
||||
message.str(),
|
||||
SecondarySourceLocation().append(modelMessage.str(), SourceLocation{})
|
||||
.append(SMTEncoder::callStackMessage(callStack))
|
||||
.append(SMTEncoder::callStackMessage(_callStack))
|
||||
.append(move(secondaryLocation))
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
message << ".";
|
||||
m_errorReporter.warning(_location, message.str(), secondaryLocation);
|
||||
m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case smt::CheckResult::UNSATISFIABLE:
|
||||
break;
|
||||
case smt::CheckResult::UNKNOWN:
|
||||
m_errorReporter.warning(_location, _description + " might happen here.", secondaryLocation);
|
||||
m_errorReporter.warning(_errorMightHappen, _location, _description + " might happen here.", secondaryLocation);
|
||||
break;
|
||||
case smt::CheckResult::CONFLICTING:
|
||||
m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
break;
|
||||
case smt::CheckResult::ERROR:
|
||||
m_errorReporter.warning(_location, "Error trying to invoke SMT solver.");
|
||||
m_errorReporter.warning(1823_error, _location, "Error trying to invoke SMT solver.");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -794,8 +807,7 @@ void BMC::checkBooleanNotConstant(
|
||||
Expression const& _condition,
|
||||
smt::Expression const& _constraints,
|
||||
smt::Expression const& _value,
|
||||
vector<SMTEncoder::CallStackEntry> const& _callStack,
|
||||
string const& _description
|
||||
vector<SMTEncoder::CallStackEntry> const& _callStack
|
||||
)
|
||||
{
|
||||
// Do not check for const-ness if this is a constant.
|
||||
@ -813,9 +825,9 @@ void BMC::checkBooleanNotConstant(
|
||||
m_interface->pop();
|
||||
|
||||
if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR)
|
||||
m_errorReporter.warning(_condition.location(), "Error trying to invoke SMT solver.");
|
||||
m_errorReporter.warning(8592_error, _condition.location(), "Error trying to invoke SMT solver.");
|
||||
else if (positiveResult == smt::CheckResult::CONFLICTING || negatedResult == smt::CheckResult::CONFLICTING)
|
||||
m_errorReporter.warning(_condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_errorReporter.warning(3356_error, _condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
else if (positiveResult == smt::CheckResult::SATISFIABLE && negatedResult == smt::CheckResult::SATISFIABLE)
|
||||
{
|
||||
// everything fine.
|
||||
@ -825,24 +837,25 @@ void BMC::checkBooleanNotConstant(
|
||||
// can't do anything.
|
||||
}
|
||||
else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE)
|
||||
m_errorReporter.warning(_condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
|
||||
m_errorReporter.warning(2512_error, _condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
|
||||
else
|
||||
{
|
||||
string value;
|
||||
string description;
|
||||
if (positiveResult == smt::CheckResult::SATISFIABLE)
|
||||
{
|
||||
solAssert(negatedResult == smt::CheckResult::UNSATISFIABLE, "");
|
||||
value = "true";
|
||||
description = "Condition is always true.";
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(positiveResult == smt::CheckResult::UNSATISFIABLE, "");
|
||||
solAssert(negatedResult == smt::CheckResult::SATISFIABLE, "");
|
||||
value = "false";
|
||||
description = "Condition is always false.";
|
||||
}
|
||||
m_errorReporter.warning(
|
||||
6838_error,
|
||||
_condition.location(),
|
||||
boost::algorithm::replace_all_copy(_description, "$VALUE", value),
|
||||
description,
|
||||
SMTEncoder::callStackMessage(_callStack)
|
||||
);
|
||||
}
|
||||
@ -862,7 +875,7 @@ BMC::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _expression
|
||||
string description("Error querying SMT solver");
|
||||
if (_e.comment())
|
||||
description += ": " + *_e.comment();
|
||||
m_errorReporter.warning(description);
|
||||
m_errorReporter.warning(8140_error, description);
|
||||
result = smt::CheckResult::ERROR;
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ using solidity::util::h256;
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
struct ErrorId;
|
||||
struct SourceLocation;
|
||||
}
|
||||
|
||||
@ -144,22 +145,22 @@ private:
|
||||
/// Check that a condition can be satisfied.
|
||||
void checkCondition(
|
||||
smt::Expression _condition,
|
||||
std::vector<CallStackEntry> const& callStack,
|
||||
std::vector<CallStackEntry> const& _callStack,
|
||||
std::pair<std::vector<smt::Expression>, std::vector<std::string>> const& _modelExpressions,
|
||||
langutil::SourceLocation const& _location,
|
||||
langutil::ErrorId _errorHappens,
|
||||
langutil::ErrorId _errorMightHappen,
|
||||
std::string const& _description,
|
||||
std::string const& _additionalValueName = "",
|
||||
smt::Expression const* _additionalValue = nullptr
|
||||
);
|
||||
/// Checks that a boolean condition is not constant. Do not warn if the expression
|
||||
/// is a literal constant.
|
||||
/// @param _description the warning string, $VALUE will be replaced by the constant value.
|
||||
void checkBooleanNotConstant(
|
||||
Expression const& _condition,
|
||||
smt::Expression const& _constraints,
|
||||
smt::Expression const& _value,
|
||||
std::vector<CallStackEntry> const& _callStack,
|
||||
std::string const& _description
|
||||
std::vector<CallStackEntry> const& _callStack
|
||||
);
|
||||
std::pair<smt::CheckResult, std::vector<std::string>>
|
||||
checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
||||
|
@ -965,10 +965,10 @@ pair<smt::CheckResult, vector<string>> CHC::query(smt::Expression const& _query,
|
||||
case smt::CheckResult::UNKNOWN:
|
||||
break;
|
||||
case smt::CheckResult::CONFLICTING:
|
||||
m_outerErrorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||
break;
|
||||
case smt::CheckResult::ERROR:
|
||||
m_outerErrorReporter.warning(_location, "Error trying to invoke SMT solver.");
|
||||
m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver.");
|
||||
break;
|
||||
}
|
||||
return {result, values};
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
@ -266,6 +266,7 @@ void SMTEncoder::endVisit(FunctionDefinition const&)
|
||||
bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
7737_error,
|
||||
_inlineAsm.location(),
|
||||
"Assertion checker does not support inline assembly."
|
||||
);
|
||||
@ -325,6 +326,7 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
7186_error,
|
||||
_varDecl.location(),
|
||||
"Assertion checker does not yet implement such variable declarations."
|
||||
);
|
||||
@ -350,6 +352,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
||||
m_context.newValue(*varDecl);
|
||||
|
||||
m_errorReporter.warning(
|
||||
9149_error,
|
||||
_assignment.location(),
|
||||
"Assertion checker does not yet implement this assignment operator."
|
||||
);
|
||||
@ -409,6 +412,7 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
|
||||
|
||||
if (_tuple.isInlineArray())
|
||||
m_errorReporter.warning(
|
||||
2177_error,
|
||||
_tuple.location(),
|
||||
"Assertion checker does not yet implement inline arrays."
|
||||
);
|
||||
@ -493,6 +497,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
1950_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement such increments / decrements."
|
||||
);
|
||||
@ -522,6 +527,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
arrayIndexAssignment(_op.subExpression(), symbVar->currentValue());
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
2683_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement \"delete\" for this expression."
|
||||
);
|
||||
@ -530,6 +536,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
||||
}
|
||||
default:
|
||||
m_errorReporter.warning(
|
||||
3682_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator."
|
||||
);
|
||||
@ -568,6 +575,7 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
|
||||
compareOperation(_op);
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
3876_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator."
|
||||
);
|
||||
@ -580,6 +588,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
4639_error,
|
||||
_funCall.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@ -639,6 +648,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
||||
}
|
||||
default:
|
||||
m_errorReporter.warning(
|
||||
4588_error,
|
||||
_funCall.location(),
|
||||
"Assertion checker does not yet implement this type of function call."
|
||||
);
|
||||
@ -771,6 +781,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
||||
}
|
||||
|
||||
m_errorReporter.warning(
|
||||
5084_error,
|
||||
_funCall.location(),
|
||||
"Type conversion is not yet fully supported and might yield false positives."
|
||||
);
|
||||
@ -800,6 +811,7 @@ void SMTEncoder::endVisit(Literal const& _literal)
|
||||
else
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
7885_error,
|
||||
_literal.location(),
|
||||
"Assertion checker does not yet support the type of this literal (" +
|
||||
_literal.annotation().type->toString() +
|
||||
@ -850,6 +862,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
||||
accessedName = identifier->name();
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
9551_error,
|
||||
_memberAccess.location(),
|
||||
"Assertion checker does not yet support this expression."
|
||||
);
|
||||
@ -879,6 +892,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
7650_error,
|
||||
_memberAccess.location(),
|
||||
"Assertion checker does not yet support this expression."
|
||||
);
|
||||
@ -903,6 +917,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
||||
if (varDecl->type()->category() == Type::Category::FixedBytes)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
7989_error,
|
||||
_indexAccess.location(),
|
||||
"Assertion checker does not yet support index accessing fixed bytes."
|
||||
);
|
||||
@ -917,6 +932,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
||||
else
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
9118_error,
|
||||
_indexAccess.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@ -940,6 +956,7 @@ void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
||||
{
|
||||
createExpr(_indexRangeAccess);
|
||||
m_errorReporter.warning(
|
||||
2923_error,
|
||||
_indexRangeAccess.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@ -1019,6 +1036,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
|
||||
else
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
9056_error,
|
||||
_expr.location(),
|
||||
"Assertion checker does not yet implement this expression."
|
||||
);
|
||||
@ -1034,6 +1052,7 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex
|
||||
bool abstract = m_context.createGlobalSymbol(_name, _expr);
|
||||
if (abstract)
|
||||
m_errorReporter.warning(
|
||||
1695_error,
|
||||
_expr.location(),
|
||||
"Assertion checker does not yet support this global variable."
|
||||
);
|
||||
@ -1087,6 +1106,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
|
||||
}
|
||||
default:
|
||||
m_errorReporter.warning(
|
||||
5188_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator."
|
||||
);
|
||||
@ -1094,6 +1114,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
9011_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement this operator for type " + type->richIdentifier() + "."
|
||||
);
|
||||
@ -1185,6 +1206,7 @@ void SMTEncoder::compareOperation(BinaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
7229_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons"
|
||||
);
|
||||
@ -1213,6 +1235,7 @@ void SMTEncoder::booleanOperation(BinaryOperation const& _op)
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
3263_error,
|
||||
_op.location(),
|
||||
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations"
|
||||
);
|
||||
@ -1245,6 +1268,7 @@ void SMTEncoder::assignment(
|
||||
m_context.newValue(*varDecl);
|
||||
|
||||
m_errorReporter.warning(
|
||||
6191_error,
|
||||
_location,
|
||||
"Assertion checker does not yet implement type " + _type->toString()
|
||||
);
|
||||
@ -1272,6 +1296,7 @@ void SMTEncoder::assignment(
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
8182_error,
|
||||
_location,
|
||||
"Assertion checker does not yet implement such assignments."
|
||||
);
|
||||
@ -1482,6 +1507,7 @@ bool SMTEncoder::createVariable(VariableDeclaration const& _varDecl)
|
||||
if (abstract)
|
||||
{
|
||||
m_errorReporter.warning(
|
||||
8115_error,
|
||||
_varDecl.location(),
|
||||
"Assertion checker does not yet support the type of this variable."
|
||||
);
|
||||
@ -1494,7 +1520,7 @@ smt::Expression SMTEncoder::expr(Expression const& _e, TypePointer _targetType)
|
||||
{
|
||||
if (!m_context.knownExpression(_e))
|
||||
{
|
||||
m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." );
|
||||
m_errorReporter.warning(6031_error, _e.location(), "Internal error: Expression undefined for SMT solver." );
|
||||
createExpr(_e);
|
||||
}
|
||||
|
||||
@ -1506,6 +1532,7 @@ void SMTEncoder::createExpr(Expression const& _e)
|
||||
bool abstract = m_context.createExpression(_e);
|
||||
if (abstract)
|
||||
m_errorReporter.warning(
|
||||
8364_error,
|
||||
_e.location(),
|
||||
"Assertion checker does not yet implement type " + _e.annotation().type->toString()
|
||||
);
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
|
@ -73,9 +73,10 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
);
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
if (_contractDef.constructor())
|
||||
FunctionDefinition const* constructor = _contractDef.constructor();
|
||||
if (constructor && constructor->visibility() >= Visibility::Public)
|
||||
{
|
||||
FunctionType constrType(*_contractDef.constructor());
|
||||
FunctionType constrType(*constructor);
|
||||
FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
|
||||
solAssert(!!externalFunctionType, "");
|
||||
Json::Value method;
|
||||
|
@ -67,7 +67,7 @@
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
@ -239,7 +239,7 @@ bool CompilerStack::parse()
|
||||
m_errorReporter.clear();
|
||||
|
||||
if (SemVerVersion{string(VersionString)}.isPrerelease())
|
||||
m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production.");
|
||||
m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production.");
|
||||
|
||||
Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery};
|
||||
|
||||
@ -949,6 +949,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
|
||||
else
|
||||
{
|
||||
m_errorReporter.parserError(
|
||||
6275_error,
|
||||
import->location(),
|
||||
string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage)
|
||||
);
|
||||
@ -1110,6 +1111,7 @@ void CompilerStack::compileContract(
|
||||
compiledContract.runtimeObject.bytecode.size() > 0x6000
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
5574_error,
|
||||
_contract.location(),
|
||||
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||
"This contract may not be deployable on mainnet. "
|
||||
|
@ -103,16 +103,14 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
||||
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||
{
|
||||
Json::Value method(devDocumentation(fun->annotation().docTags));
|
||||
// add the function, only if we have any documentation to add
|
||||
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
||||
|
||||
if (!jsonReturn.empty())
|
||||
method["returns"] = jsonReturn;
|
||||
|
||||
if (!method.empty())
|
||||
{
|
||||
// add the function, only if we have any documentation to add
|
||||
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
||||
|
||||
if (!jsonReturn.empty())
|
||||
method["returns"] = jsonReturn;
|
||||
|
||||
methods[it.second->externalSignature()] = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,8 +31,7 @@
|
||||
#include <libsolutil/JSON.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
@ -491,7 +490,7 @@ std::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelect
|
||||
}
|
||||
/// Validates the optimizer settings and returns them in a parsed object.
|
||||
/// On error returns the json-formatted error message.
|
||||
boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
|
||||
std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
|
||||
{
|
||||
if (auto result = checkOptimizerKeys(_jsonInput))
|
||||
return *result;
|
||||
@ -552,7 +551,7 @@ boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Valu
|
||||
|
||||
}
|
||||
|
||||
boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
|
||||
std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
|
||||
{
|
||||
InputsAndSettings ret;
|
||||
|
||||
@ -740,10 +739,10 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
|
||||
if (settings.isMember("optimizer"))
|
||||
{
|
||||
auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]);
|
||||
if (optimiserSettings.type() == typeid(Json::Value))
|
||||
return boost::get<Json::Value>(std::move(optimiserSettings)); // was an error
|
||||
if (std::holds_alternative<Json::Value>(optimiserSettings))
|
||||
return std::get<Json::Value>(std::move(optimiserSettings)); // was an error
|
||||
else
|
||||
ret.optimiserSettings = boost::get<OptimiserSettings>(std::move(optimiserSettings));
|
||||
ret.optimiserSettings = std::get<OptimiserSettings>(std::move(optimiserSettings));
|
||||
}
|
||||
|
||||
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
||||
@ -1155,9 +1154,9 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
|
||||
try
|
||||
{
|
||||
auto parsed = parseInput(_input);
|
||||
if (parsed.type() == typeid(Json::Value))
|
||||
return boost::get<Json::Value>(std::move(parsed));
|
||||
InputsAndSettings settings = boost::get<InputsAndSettings>(std::move(parsed));
|
||||
if (std::holds_alternative<Json::Value>(parsed))
|
||||
return std::get<Json::Value>(std::move(parsed));
|
||||
InputsAndSettings settings = std::get<InputsAndSettings>(std::move(parsed));
|
||||
if (settings.language == "Solidity")
|
||||
return compileSolidity(std::move(settings));
|
||||
else if (settings.language == "Yul")
|
||||
|
@ -24,9 +24,9 @@
|
||||
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
@ -73,7 +73,7 @@ private:
|
||||
|
||||
/// Parses the input json (and potentially invokes the read callback) and either returns
|
||||
/// it in condensed form or an error as a json object.
|
||||
boost::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
|
||||
std::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
|
||||
|
||||
Json::Value compileSolidity(InputsAndSettings _inputsAndSettings);
|
||||
Json::Value compileYul(InputsAndSettings _inputsAndSettings);
|
||||
|
@ -195,5 +195,5 @@ void DocStringParser::newTag(string const& _tagName)
|
||||
void DocStringParser::appendError(string const& _description)
|
||||
{
|
||||
m_errorsOccurred = true;
|
||||
m_errorReporter->docstringParsingError(_description);
|
||||
m_errorReporter->docstringParsingError(9440_error, _description);
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
||||
nodes.push_back(parseEnumDefinition());
|
||||
break;
|
||||
default:
|
||||
fatalParserError(string("Expected pragma, import directive or contract/interface/library/struct/enum definition."));
|
||||
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition.");
|
||||
}
|
||||
}
|
||||
solAssert(m_recursionDepth == 0, "");
|
||||
@ -128,9 +128,10 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> c
|
||||
// so we don't need to report anything here.
|
||||
if (!m_parserErrorRecovery)
|
||||
m_errorReporter.fatalParserError(
|
||||
5333_error,
|
||||
_location,
|
||||
"Source file requires different compiler version (current compiler is " +
|
||||
string(VersionString) + " - note that nightly builds are considered to be "
|
||||
string(VersionString) + ") - note that nightly builds are considered to be "
|
||||
"strictly less than the released version"
|
||||
);
|
||||
}
|
||||
@ -162,7 +163,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
|
||||
{
|
||||
Token token = m_scanner->currentToken();
|
||||
if (token == Token::Illegal)
|
||||
parserError("Token incompatible with Solidity parser as part of pragma directive.");
|
||||
parserError(6281_error, "Token incompatible with Solidity parser as part of pragma directive.");
|
||||
else
|
||||
{
|
||||
string literal = m_scanner->currentLiteral();
|
||||
@ -240,18 +241,18 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
|
||||
unitAlias = expectIdentifierToken();
|
||||
}
|
||||
else
|
||||
fatalParserError("Expected string literal (path), \"*\" or alias list.");
|
||||
fatalParserError(9478_error, "Expected string literal (path), \"*\" or alias list.");
|
||||
// "from" is not a keyword but parsed as an identifier because of backwards
|
||||
// compatibility and because it is a really common word.
|
||||
if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
|
||||
fatalParserError("Expected \"from\".");
|
||||
fatalParserError(8208_error, "Expected \"from\".");
|
||||
m_scanner->next();
|
||||
if (m_scanner->currentToken() != Token::StringLiteral)
|
||||
fatalParserError("Expected import path.");
|
||||
fatalParserError(6845_error, "Expected import path.");
|
||||
path = getLiteralAndAdvance();
|
||||
}
|
||||
if (path->empty())
|
||||
fatalParserError("Import path cannot be empty.");
|
||||
fatalParserError(6326_error, "Import path cannot be empty.");
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::Semicolon);
|
||||
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
|
||||
@ -278,7 +279,7 @@ std::pair<ContractKind, bool> Parser::parseContractKind()
|
||||
kind = ContractKind::Library;
|
||||
break;
|
||||
default:
|
||||
parserError("Expected keyword \"contract\", \"interface\" or \"library\".");
|
||||
parserError(3515_error, "Expected keyword \"contract\", \"interface\" or \"library\".");
|
||||
return std::make_pair(ContractKind::Contract, abstract);
|
||||
}
|
||||
m_scanner->next();
|
||||
@ -343,7 +344,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
||||
else if (currentTokenValue == Token::Using)
|
||||
subNodes.push_back(parseUsingDirective());
|
||||
else
|
||||
fatalParserError(string("Function, variable, struct or modifier declaration expected."));
|
||||
fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected.");
|
||||
}
|
||||
}
|
||||
catch (FatalError const&)
|
||||
@ -462,6 +463,7 @@ StateMutability Parser::parseStateMutability()
|
||||
case Token::Constant:
|
||||
stateMutability = StateMutability::View;
|
||||
parserError(
|
||||
7698_error,
|
||||
"The state mutability modifier \"constant\" was removed in version 0.5.0. "
|
||||
"Use \"view\" or \"pure\" instead."
|
||||
);
|
||||
@ -494,11 +496,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
||||
// Detect this and return early.
|
||||
if (_isStateVariable && (result.visibility == Visibility::External || result.visibility == Visibility::Internal))
|
||||
break;
|
||||
parserError(string(
|
||||
parserError(
|
||||
9439_error,
|
||||
"Visibility already specified as \"" +
|
||||
Declaration::visibilityToString(result.visibility) +
|
||||
"\"."
|
||||
));
|
||||
);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@ -508,11 +511,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
||||
{
|
||||
if (result.stateMutability != StateMutability::NonPayable)
|
||||
{
|
||||
parserError(string(
|
||||
parserError(
|
||||
9680_error,
|
||||
"State mutability already specified as \"" +
|
||||
stateMutabilityToString(result.stateMutability) +
|
||||
"\"."
|
||||
));
|
||||
);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@ -521,14 +525,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
||||
else if (!_isStateVariable && token == Token::Override)
|
||||
{
|
||||
if (result.overrides)
|
||||
parserError("Override already specified.");
|
||||
parserError(1827_error, "Override already specified.");
|
||||
|
||||
result.overrides = parseOverrideSpecifier();
|
||||
}
|
||||
else if (!_isStateVariable && token == Token::Virtual)
|
||||
{
|
||||
if (result.isVirtual)
|
||||
parserError("Virtual already specified.");
|
||||
parserError(6879_error, "Virtual already specified.");
|
||||
|
||||
result.isVirtual = true;
|
||||
m_scanner->next();
|
||||
@ -576,9 +580,9 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
|
||||
"the \"function\" keyword to define it."
|
||||
};
|
||||
if (m_scanner->currentToken() == Token::Constructor)
|
||||
parserError(message);
|
||||
parserError(3323_error, message);
|
||||
else
|
||||
parserWarning(message);
|
||||
parserWarning(3445_error, message);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@ -659,10 +663,10 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
|
||||
break;
|
||||
expectToken(Token::Comma);
|
||||
if (m_scanner->currentToken() != Token::Identifier)
|
||||
fatalParserError(string("Expected identifier after ','"));
|
||||
fatalParserError(1612_error, "Expected identifier after ','");
|
||||
}
|
||||
if (members.empty())
|
||||
parserError({"enum with no members is not allowed."});
|
||||
parserError(3147_error, "enum with no members is not allowed.");
|
||||
|
||||
nodeFactory.markEndPosition();
|
||||
expectToken(Token::RBrace);
|
||||
@ -689,6 +693,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
|
||||
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
||||
fatalParserError(
|
||||
2915_error,
|
||||
"Expected a state variable declaration. If you intended this as a fallback function "
|
||||
"or a function to handle plain ether transactions, use the \"fallback\" keyword "
|
||||
"or the \"receive\" keyword instead."
|
||||
@ -709,11 +714,12 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
nodeFactory.markEndPosition();
|
||||
if (visibility != Visibility::Default)
|
||||
{
|
||||
parserError(string(
|
||||
parserError(
|
||||
4110_error,
|
||||
"Visibility already specified as \"" +
|
||||
Declaration::visibilityToString(visibility) +
|
||||
"\"."
|
||||
));
|
||||
);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
@ -722,7 +728,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
else if (_options.isStateVariable && token == Token::Override)
|
||||
{
|
||||
if (overrides)
|
||||
parserError("Override already specified.");
|
||||
parserError(9125_error, "Override already specified.");
|
||||
|
||||
overrides = parseOverrideSpecifier();
|
||||
}
|
||||
@ -734,6 +740,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
{
|
||||
if (mutability != VariableDeclaration::Mutability::Mutable)
|
||||
parserError(
|
||||
3109_error,
|
||||
string("Mutability already set to ") +
|
||||
(mutability == VariableDeclaration::Mutability::Constant ? "\"constant\"" : "\"immutable\"")
|
||||
);
|
||||
@ -745,9 +752,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
|
||||
{
|
||||
if (location != VariableDeclaration::Location::Unspecified)
|
||||
parserError(string("Location already specified."));
|
||||
parserError(3548_error, "Location already specified.");
|
||||
else if (!type)
|
||||
parserError(string("Location specifier needs explicit type name."));
|
||||
parserError(7439_error, "Location specifier needs explicit type name.");
|
||||
else
|
||||
{
|
||||
switch (token)
|
||||
@ -836,13 +843,13 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
||||
if (m_scanner->currentToken() == Token::Override)
|
||||
{
|
||||
if (overrides)
|
||||
parserError("Override already specified.");
|
||||
parserError(9102_error, "Override already specified.");
|
||||
overrides = parseOverrideSpecifier();
|
||||
}
|
||||
else if (m_scanner->currentToken() == Token::Virtual)
|
||||
{
|
||||
if (isVirtual)
|
||||
parserError("Virtual already specified.");
|
||||
parserError(2662_error, "Virtual already specified.");
|
||||
|
||||
isVirtual = true;
|
||||
m_scanner->next();
|
||||
@ -990,7 +997,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
}
|
||||
else
|
||||
{
|
||||
parserError("State mutability can only be specified for address types.");
|
||||
parserError(9106_error, "State mutability can only be specified for address types.");
|
||||
m_scanner->next();
|
||||
}
|
||||
}
|
||||
@ -999,7 +1006,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
else if (token == Token::Var)
|
||||
{
|
||||
if (!_allowVar)
|
||||
parserError(string("Expected explicit type name."));
|
||||
parserError(7059_error, "Expected explicit type name.");
|
||||
m_scanner->next();
|
||||
}
|
||||
else if (token == Token::Function)
|
||||
@ -1009,7 +1016,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||
else if (token == Token::Identifier)
|
||||
type = parseUserDefinedTypeName();
|
||||
else
|
||||
fatalParserError(string("Expected type name"));
|
||||
fatalParserError(3546_error, "Expected type name");
|
||||
|
||||
if (type)
|
||||
// Parse "[...]" postfixes for arrays.
|
||||
@ -1052,7 +1059,7 @@ ASTPointer<Mapping> Parser::parseMapping()
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
fatalParserError(string("Expected elementary type name or identifier for mapping key type"));
|
||||
fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type");
|
||||
expectToken(Token::Arrow);
|
||||
bool const allowVar = false;
|
||||
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
|
||||
@ -1078,7 +1085,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
|
||||
while (m_scanner->currentToken() != Token::RParen)
|
||||
{
|
||||
if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen)
|
||||
fatalParserError("Unexpected trailing comma in parameter list.");
|
||||
fatalParserError(7591_error, "Unexpected trailing comma in parameter list.");
|
||||
expectToken(Token::Comma);
|
||||
parameters.push_back(parseVariableDeclaration(options));
|
||||
}
|
||||
@ -1213,7 +1220,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
||||
if (m_scanner->currentToken() == Token::StringLiteral)
|
||||
{
|
||||
if (m_scanner->currentLiteral() != "evmasm")
|
||||
fatalParserError("Only \"evmasm\" supported.");
|
||||
fatalParserError(4531_error, "Only \"evmasm\" supported.");
|
||||
// This can be used in the future to set the dialect.
|
||||
m_scanner->next();
|
||||
}
|
||||
@ -1376,7 +1383,7 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
|
||||
ASTNodeFactory eventCallNodeFactory(*this);
|
||||
|
||||
if (m_scanner->currentToken() != Token::Identifier)
|
||||
fatalParserError("Expected event name or path.");
|
||||
fatalParserError(5620_error, "Expected event name or path.");
|
||||
|
||||
IndexAccessedPath iap;
|
||||
while (true)
|
||||
@ -1844,7 +1851,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
nodeFactory.markEndPosition();
|
||||
m_scanner->next();
|
||||
if (m_scanner->currentToken() == Token::Illegal)
|
||||
fatalParserError(to_string(m_scanner->currentError()));
|
||||
fatalParserError(5428_error, to_string(m_scanner->currentError()));
|
||||
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
|
||||
break;
|
||||
}
|
||||
@ -1875,7 +1882,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken)
|
||||
components.push_back(parseExpression());
|
||||
else if (isArray)
|
||||
parserError("Expected expression (inline array elements cannot be omitted).");
|
||||
parserError(4799_error, "Expected expression (inline array elements cannot be omitted).");
|
||||
else
|
||||
components.push_back(ASTPointer<Expression>());
|
||||
|
||||
@ -1890,7 +1897,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
break;
|
||||
}
|
||||
case Token::Illegal:
|
||||
fatalParserError(to_string(m_scanner->currentError()));
|
||||
fatalParserError(8936_error, to_string(m_scanner->currentError()));
|
||||
break;
|
||||
default:
|
||||
if (TokenTraits::isElementaryTypeName(token))
|
||||
@ -1906,7 +1913,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
fatalParserError(string("Expected primary expression."));
|
||||
fatalParserError(6933_error, "Expected primary expression.");
|
||||
break;
|
||||
}
|
||||
return expression;
|
||||
@ -1964,7 +1971,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
|
||||
m_scanner->peekNextToken() == Token::RBrace
|
||||
)
|
||||
{
|
||||
parserError("Unexpected trailing comma.");
|
||||
parserError(2074_error, "Unexpected trailing comma.");
|
||||
m_scanner->next();
|
||||
}
|
||||
|
||||
@ -2083,7 +2090,7 @@ ASTPointer<TypeName> Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces
|
||||
for (auto const& lengthExpression: _iap.indices)
|
||||
{
|
||||
if (lengthExpression.end)
|
||||
parserError(lengthExpression.location, "Expected array length expression.");
|
||||
parserError(5464_error, lengthExpression.location, "Expected array length expression.");
|
||||
nodeFactory.setLocation(lengthExpression.location);
|
||||
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.start);
|
||||
}
|
||||
|
@ -24,7 +24,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
|
@ -527,7 +527,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
||||
// Similarly we assume bitwise shifting and create2 go together.
|
||||
yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
|
||||
|
||||
auto errorForVM = [=](string const& vmKindMessage) {
|
||||
auto errorForVM = [&](string const& vmKindMessage) {
|
||||
typeError(
|
||||
_location,
|
||||
"The \"" +
|
||||
@ -583,6 +583,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
||||
)
|
||||
{
|
||||
m_errorReporter.error(
|
||||
4316_error,
|
||||
Error::Type::SyntaxError,
|
||||
_location,
|
||||
"Jump instructions and labels are low-level EVM features that can lead to "
|
||||
@ -599,13 +600,13 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
||||
|
||||
void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.typeError(_location, _description);
|
||||
m_errorReporter.typeError(7569_error, _location, _description);
|
||||
m_success = false;
|
||||
}
|
||||
|
||||
void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description)
|
||||
{
|
||||
m_errorReporter.declarationError(_location, _description);
|
||||
m_errorReporter.declarationError(9595_error, _location, _description);
|
||||
m_success = false;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
* Converts inline assembly AST to JSON format
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libyul/AsmDataForward.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <json/json.h>
|
||||
|
@ -122,11 +122,11 @@ Statement Parser::parseStatement()
|
||||
if (currentToken() == Token::Default)
|
||||
_switch.cases.emplace_back(parseCase());
|
||||
if (currentToken() == Token::Default)
|
||||
fatalParserError("Only one default case allowed.");
|
||||
fatalParserError(6931_error, "Only one default case allowed.");
|
||||
else if (currentToken() == Token::Case)
|
||||
fatalParserError("Case not allowed after default case.");
|
||||
fatalParserError(4904_error, "Case not allowed after default case.");
|
||||
if (_switch.cases.empty())
|
||||
fatalParserError("Switch statement without any cases.");
|
||||
fatalParserError(2418_error, "Switch statement without any cases.");
|
||||
_switch.location.end = _switch.cases.back().body.location.end;
|
||||
return Statement{move(_switch)};
|
||||
}
|
||||
@ -151,7 +151,7 @@ Statement Parser::parseStatement()
|
||||
{
|
||||
Statement stmt{createWithLocation<Leave>()};
|
||||
if (!m_insideFunction)
|
||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"leave\" can only be used inside a function.");
|
||||
m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function.");
|
||||
m_scanner->next();
|
||||
return stmt;
|
||||
}
|
||||
@ -184,6 +184,7 @@ Statement Parser::parseStatement()
|
||||
auto const token = currentToken() == Token::Comma ? "," : ":=";
|
||||
|
||||
fatalParserError(
|
||||
2856_error,
|
||||
std::string("Variable name must precede \"") +
|
||||
token +
|
||||
"\"" +
|
||||
@ -194,7 +195,7 @@ Statement Parser::parseStatement()
|
||||
auto const& identifier = std::get<Identifier>(elementary);
|
||||
|
||||
if (m_dialect.builtin(identifier.name))
|
||||
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
|
||||
fatalParserError(6272_error, "Cannot assign to builtin function \"" + identifier.name.str() + "\".");
|
||||
|
||||
variableNames.emplace_back(identifier);
|
||||
|
||||
@ -218,7 +219,7 @@ Statement Parser::parseStatement()
|
||||
return Statement{std::move(assignment)};
|
||||
}
|
||||
default:
|
||||
fatalParserError("Call or assignment expected.");
|
||||
fatalParserError(6913_error, "Call or assignment expected.");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -250,7 +251,7 @@ Case Parser::parseCase()
|
||||
advance();
|
||||
ElementaryOperation literal = parseElementaryOperation();
|
||||
if (!holds_alternative<Literal>(literal))
|
||||
fatalParserError("Literal expected.");
|
||||
fatalParserError(4805_error, "Literal expected.");
|
||||
_case.value = make_unique<Literal>(std::get<Literal>(std::move(literal)));
|
||||
}
|
||||
else
|
||||
@ -325,6 +326,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
||||
case Token::Bool:
|
||||
case Token::Address:
|
||||
case Token::Var:
|
||||
case Token::In:
|
||||
{
|
||||
YulString literal{currentLiteral()};
|
||||
if (m_dialect.builtin(literal))
|
||||
@ -352,7 +354,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
||||
break;
|
||||
case Token::Number:
|
||||
if (!isValidNumberLiteral(currentLiteral()))
|
||||
fatalParserError("Invalid number literal.");
|
||||
fatalParserError(4828_error, "Invalid number literal.");
|
||||
kind = LiteralKind::Number;
|
||||
break;
|
||||
case Token::TrueLiteral:
|
||||
@ -381,7 +383,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fatalParserError("Literal or identifier expected.");
|
||||
fatalParserError(1856_error, "Literal or identifier expected.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -416,6 +418,7 @@ FunctionDefinition Parser::parseFunctionDefinition()
|
||||
|
||||
if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre)
|
||||
m_errorReporter.syntaxError(
|
||||
3441_error,
|
||||
currentLocation(),
|
||||
"Functions cannot be defined inside a for-loop init block."
|
||||
);
|
||||
@ -470,7 +473,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
|
||||
else if (holds_alternative<FunctionCall>(_initialOp))
|
||||
ret = std::move(std::get<FunctionCall>(_initialOp));
|
||||
else
|
||||
fatalParserError("Function name expected.");
|
||||
fatalParserError(9980_error, "Function name expected.");
|
||||
|
||||
expectToken(Token::LParen);
|
||||
if (currentToken() != Token::RParen)
|
||||
@ -515,6 +518,7 @@ YulString Parser::expectAsmIdentifier()
|
||||
case Token::Bool:
|
||||
case Token::Identifier:
|
||||
case Token::Var:
|
||||
case Token::In:
|
||||
break;
|
||||
default:
|
||||
expectToken(Token::Identifier);
|
||||
@ -522,7 +526,7 @@ YulString Parser::expectAsmIdentifier()
|
||||
}
|
||||
|
||||
if (m_dialect.builtin(name))
|
||||
fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
|
||||
fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
|
||||
advance();
|
||||
return name;
|
||||
}
|
||||
@ -532,13 +536,13 @@ void Parser::checkBreakContinuePosition(string const& _which)
|
||||
switch (m_currentForLoopComponent)
|
||||
{
|
||||
case ForLoopComponent::None:
|
||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body.");
|
||||
m_errorReporter.syntaxError(2592_error, currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body.");
|
||||
break;
|
||||
case ForLoopComponent::ForLoopPre:
|
||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed.");
|
||||
m_errorReporter.syntaxError(9615_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed.");
|
||||
break;
|
||||
case ForLoopComponent::ForLoopPost:
|
||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed.");
|
||||
m_errorReporter.syntaxError(2461_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed.");
|
||||
break;
|
||||
case ForLoopComponent::ForLoopBody:
|
||||
break;
|
||||
|
@ -29,8 +29,6 @@
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
@ -141,6 +139,7 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const&
|
||||
{
|
||||
//@TODO secondary location
|
||||
m_errorReporter.declarationError(
|
||||
1395_error,
|
||||
_location,
|
||||
"Variable name " + _name.name.str() + " already taken in this scope."
|
||||
);
|
||||
@ -161,6 +160,7 @@ bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef)
|
||||
{
|
||||
//@TODO secondary location
|
||||
m_errorReporter.declarationError(
|
||||
6052_error,
|
||||
_funDef.location,
|
||||
"Function name " + _funDef.name.str() + " already taken in this scope."
|
||||
);
|
||||
|
@ -66,7 +66,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
|
||||
RecursionGuard guard(*this);
|
||||
|
||||
if (currentToken() != Token::Identifier || currentLiteral() != "object")
|
||||
fatalParserError("Expected keyword \"object\".");
|
||||
fatalParserError(4294_error, "Expected keyword \"object\".");
|
||||
advance();
|
||||
|
||||
shared_ptr<Object> ret = make_shared<Object>();
|
||||
@ -83,7 +83,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
|
||||
else if (currentToken() == Token::Identifier && currentLiteral() == "data")
|
||||
parseData(*ret);
|
||||
else
|
||||
fatalParserError("Expected keyword \"data\" or \"object\" or \"}\".");
|
||||
fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\".");
|
||||
}
|
||||
if (_containingObject)
|
||||
addNamedSubObject(*_containingObject, ret->name, ret);
|
||||
@ -96,7 +96,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
|
||||
shared_ptr<Block> ObjectParser::parseCode()
|
||||
{
|
||||
if (currentToken() != Token::Identifier || currentLiteral() != "code")
|
||||
fatalParserError("Expected keyword \"code\".");
|
||||
fatalParserError(4846_error, "Expected keyword \"code\".");
|
||||
advance();
|
||||
|
||||
return parseBlock();
|
||||
@ -133,11 +133,11 @@ YulString ObjectParser::parseUniqueName(Object const* _containingObject)
|
||||
expectToken(Token::StringLiteral, false);
|
||||
YulString name{currentLiteral()};
|
||||
if (name.empty())
|
||||
parserError("Object name cannot be empty.");
|
||||
parserError(3287_error, "Object name cannot be empty.");
|
||||
else if (_containingObject && _containingObject->name == name)
|
||||
parserError("Object name cannot be the same as the name of the containing object.");
|
||||
parserError(8311_error, "Object name cannot be the same as the name of the containing object.");
|
||||
else if (_containingObject && _containingObject->subIndexByName.count(name))
|
||||
parserError("Object name \"" + name.str() + "\" already exists inside the containing object.");
|
||||
parserError(8794_error, "Object name \"" + name.str() + "\" already exists inside the containing object.");
|
||||
advance();
|
||||
return name;
|
||||
}
|
||||
|
@ -38,9 +38,6 @@ using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::util;
|
||||
|
||||
using boost::split;
|
||||
using boost::is_any_of;
|
||||
|
||||
string solidity::yul::reindent(string const& _code)
|
||||
{
|
||||
int constexpr indentationWidth = 4;
|
||||
@ -55,7 +52,7 @@ string solidity::yul::reindent(string const& _code)
|
||||
};
|
||||
|
||||
vector<string> lines;
|
||||
split(lines, _code, is_any_of("\n"));
|
||||
boost::split(lines, _code, boost::is_any_of("\n"));
|
||||
for (string& line: lines)
|
||||
boost::trim(line);
|
||||
|
||||
|
@ -85,11 +85,11 @@ void VariableReferenceCounter::operator()(Block const& _block)
|
||||
void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
|
||||
{
|
||||
m_scope->lookup(_variableName, GenericVisitor{
|
||||
[=](Scope::Variable const& _var)
|
||||
[&](Scope::Variable const& _var)
|
||||
{
|
||||
++m_context.variableReferences[&_var];
|
||||
},
|
||||
[=](Scope::Function const&) { }
|
||||
[](Scope::Function const&) { }
|
||||
});
|
||||
}
|
||||
|
||||
@ -184,11 +184,13 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_assembly.setSourceLocation(_varDecl.location);
|
||||
int variablesLeft = numVariables;
|
||||
while (variablesLeft--)
|
||||
m_assembly.appendConstant(u256(0));
|
||||
}
|
||||
|
||||
m_assembly.setSourceLocation(_varDecl.location);
|
||||
bool atTopOfStack = true;
|
||||
for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex)
|
||||
{
|
||||
@ -203,7 +205,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
||||
if (atTopOfStack)
|
||||
{
|
||||
m_context->variableStackHeights.erase(&var);
|
||||
m_assembly.setSourceLocation(_varDecl.location);
|
||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||
}
|
||||
else
|
||||
@ -216,7 +217,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
||||
int slot = *m_unusedStackSlots.begin();
|
||||
m_unusedStackSlots.erase(m_unusedStackSlots.begin());
|
||||
m_context->variableStackHeights[&var] = slot;
|
||||
m_assembly.setSourceLocation(_varDecl.location);
|
||||
if (int heightDiff = variableHeightDiff(var, varName, true))
|
||||
m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1));
|
||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||
@ -272,7 +272,7 @@ void CodeTransform::operator()(FunctionCall const& _call)
|
||||
|
||||
Scope::Function* function = nullptr;
|
||||
yulAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{
|
||||
[=](Scope::Variable&) { yulAssert(false, "Expected function name."); },
|
||||
[](Scope::Variable&) { yulAssert(false, "Expected function name."); },
|
||||
[&](Scope::Function& _function) { function = &_function; }
|
||||
}), "Function name not found.");
|
||||
yulAssert(function, "");
|
||||
@ -296,7 +296,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
|
||||
// First search internals, then externals.
|
||||
yulAssert(m_scope, "");
|
||||
if (m_scope->lookup(_identifier.name, GenericVisitor{
|
||||
[=](Scope::Variable& _var)
|
||||
[&](Scope::Variable& _var)
|
||||
{
|
||||
// TODO: opportunity for optimization: Do not DUP if this is the last reference
|
||||
// to the top most element of the stack
|
||||
@ -307,7 +307,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
|
||||
m_assembly.appendConstant(u256(0));
|
||||
decreaseReference(_identifier.name, _var);
|
||||
},
|
||||
[=](Scope::Function&)
|
||||
[](Scope::Function&)
|
||||
{
|
||||
yulAssert(false, "Function not removed during desugaring.");
|
||||
}
|
||||
|
@ -1232,7 +1232,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
|
||||
|
||||
FunctionHoister::run(context, ast);
|
||||
FunctionGrouper::run(context, ast);
|
||||
MainFunction{}(ast);
|
||||
MainFunction::run(context, ast);
|
||||
ForLoopConditionIntoBody::run(context, ast);
|
||||
ExpressionSplitter::run(context, ast);
|
||||
WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser);
|
||||
|
@ -29,9 +29,6 @@
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/range/adaptor/transformed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -60,8 +59,9 @@ void removeEmptyDefaultFromSwitch(Switch& _switchStmt)
|
||||
|
||||
void removeEmptyCasesFromSwitch(Switch& _switchStmt)
|
||||
{
|
||||
bool hasDefault = boost::algorithm::any_of(
|
||||
_switchStmt.cases,
|
||||
bool hasDefault = std::any_of(
|
||||
_switchStmt.cases.begin(),
|
||||
_switchStmt.cases.end(),
|
||||
[](Case const& _case) { return !_case.value; }
|
||||
);
|
||||
|
||||
|
@ -31,8 +31,6 @@
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
|
@ -34,8 +34,6 @@
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
|
@ -4,6 +4,7 @@ planned state of the optimiser.
|
||||
|
||||
Table of Contents:
|
||||
|
||||
- [Selecting optimisations](#selecting-optimisations)
|
||||
- [Preprocessing](#preprocessing)
|
||||
- [Pseudo-SSA Transformation](#pseudo-ssa-transformation)
|
||||
- [Tools](#tools)
|
||||
@ -33,6 +34,17 @@ the following transformation steps are the main components:
|
||||
- [Redundant Assign Eliminator](#redundant-assign-eliminator)
|
||||
- [Full Function Inliner](#full-function-inliner)
|
||||
|
||||
## Selecting optimisations
|
||||
|
||||
By default the optimiser applies its predefined sequence of optimisation steps to the generated assembly.
|
||||
You can override this sequence and supply your own using the `--yul-optimizations` option:
|
||||
|
||||
``` bash
|
||||
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
|
||||
```
|
||||
|
||||
There's a [table listing available abbreviations in the optimiser docs](/docs/yul.rst#optimization-step-sequence).
|
||||
|
||||
## Preprocessing
|
||||
|
||||
The preprocessing components perform transformations to get the program
|
||||
|
@ -306,7 +306,7 @@ void RedundantAssignEliminator::finalize(YulString _variable, RedundantAssignEli
|
||||
|
||||
void AssignmentRemover::operator()(Block& _block)
|
||||
{
|
||||
boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool {
|
||||
boost::range::remove_erase_if(_block.statements, [&](Statement const& _statement) -> bool {
|
||||
return holds_alternative<Assignment>(_statement) && m_toRemove.count(&std::get<Assignment>(_statement));
|
||||
});
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <libsolutil/Visitor.h>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
|
@ -29,8 +29,6 @@
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/none_of.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
@ -85,9 +83,10 @@ void UnusedPruner::operator()(Block& _block)
|
||||
// movable or it returns a single value. In the latter case, we
|
||||
// replace `let a := f()` by `pop(f())` (in pure Yul, this will be
|
||||
// `drop(f())`).
|
||||
if (boost::algorithm::none_of(
|
||||
varDecl.variables,
|
||||
[=](TypedName const& _typedName) { return used(_typedName.name); }
|
||||
if (std::none_of(
|
||||
varDecl.variables.begin(),
|
||||
varDecl.variables.end(),
|
||||
[&](TypedName const& _typedName) { return used(_typedName.name); }
|
||||
))
|
||||
{
|
||||
if (!varDecl.value)
|
||||
|
156
scripts/correct_error_ids.py
Normal file
156
scripts/correct_error_ids.py
Normal 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()
|
@ -28,6 +28,6 @@
|
||||
|
||||
set -e
|
||||
cd docs
|
||||
pip install -r requirements.txt
|
||||
pip3 install -r requirements.txt
|
||||
sphinx-build -nW -b html -d _build/doctrees . _build/html
|
||||
cd ..
|
||||
|
@ -100,7 +100,7 @@ do
|
||||
force_abiv2_flag=""
|
||||
if [[ "$abiv2" == "yes" ]]
|
||||
then
|
||||
force_abiv2_flag="--abiencoderv2 --optimize-yul"
|
||||
force_abiv2_flag="--abiencoderv2"
|
||||
fi
|
||||
printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv2_flag..."
|
||||
|
||||
|
@ -699,7 +699,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
|
||||
string pathName = (p / _fileName).string();
|
||||
if (fs::exists(pathName) && !m_args.count(g_strOverwrite))
|
||||
{
|
||||
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl;
|
||||
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl;
|
||||
m_error = true;
|
||||
return;
|
||||
}
|
||||
@ -719,10 +719,10 @@ bool CommandLineInterface::parseArguments(int _argc, char** _argv)
|
||||
g_hasOutput = false;
|
||||
|
||||
// Declare the supported options.
|
||||
po::options_description desc(R"(solc, the Solidity commandline compiler.
|
||||
po::options_description desc((R"(solc, the Solidity commandline compiler.
|
||||
|
||||
This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you
|
||||
are welcome to redistribute it under certain conditions. See 'solc --license'
|
||||
are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"('
|
||||
for details.
|
||||
|
||||
Usage: solc [options] [input_file...]
|
||||
@ -732,9 +732,9 @@ at standard output or in files in the output directory, if specified.
|
||||
Imports are automatically read from the filesystem, but it is also possible to
|
||||
remap paths using the context:prefix=path syntax.
|
||||
Example:
|
||||
solc --bin -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
|
||||
solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
|
||||
|
||||
Allowed options)",
|
||||
Allowed options)").c_str(),
|
||||
po::options_description::m_default_line_length,
|
||||
po::options_description::m_default_line_length - 23
|
||||
);
|
||||
@ -780,20 +780,27 @@ Allowed options)",
|
||||
)
|
||||
(
|
||||
g_argImportAst.c_str(),
|
||||
"Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
|
||||
"Supported Inputs is the output of the --standard-json or the one produced by --combined-json ast,compact-format"
|
||||
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
|
||||
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
|
||||
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
|
||||
)
|
||||
(
|
||||
g_argAssemble.c_str(),
|
||||
"Switch to assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is assembly."
|
||||
("Switch to assembly mode, ignoring all options except "
|
||||
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
|
||||
"and assumes input is assembly.").c_str()
|
||||
)
|
||||
(
|
||||
g_argYul.c_str(),
|
||||
"Switch to Yul mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is Yul."
|
||||
("Switch to Yul mode, ignoring all options except "
|
||||
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
|
||||
"and assumes input is Yul.").c_str()
|
||||
)
|
||||
(
|
||||
g_argStrictAssembly.c_str(),
|
||||
"Switch to strict assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is strict assembly."
|
||||
("Switch to strict assembly mode, ignoring all options except "
|
||||
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
|
||||
"and assumes input is strict assembly.").c_str()
|
||||
)
|
||||
(
|
||||
g_strYulDialect.c_str(),
|
||||
@ -807,8 +814,8 @@ Allowed options)",
|
||||
)
|
||||
(
|
||||
g_argLink.c_str(),
|
||||
"Switch to linker mode, ignoring all options apart from --libraries "
|
||||
"and modify binaries in place."
|
||||
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
|
||||
"and modify binaries in place.").c_str()
|
||||
)
|
||||
(
|
||||
g_argMetadataHash.c_str(),
|
||||
@ -835,7 +842,7 @@ Allowed options)",
|
||||
"Set for how many contract runs to optimize. "
|
||||
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
|
||||
)
|
||||
(g_strOptimizeYul.c_str(), "Legacy option, ignored. Use the general --optimize to enable Yul optimizer.")
|
||||
(g_strOptimizeYul.c_str(), ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str())
|
||||
(g_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.")
|
||||
(
|
||||
g_strYulOptimizations.c_str(),
|
||||
@ -933,7 +940,7 @@ Allowed options)",
|
||||
for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(",")))
|
||||
if (!g_combinedJsonArgs.count(item))
|
||||
{
|
||||
serr() << "Invalid option to --combined-json: " << item << endl;
|
||||
serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1047,7 +1054,7 @@ bool CommandLineInterface::processInput()
|
||||
std::optional<langutil::EVMVersion> versionOption = langutil::EVMVersion::fromString(versionOptionStr);
|
||||
if (!versionOption)
|
||||
{
|
||||
serr() << "Invalid option for --evm-version: " << versionOptionStr << endl;
|
||||
serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl;
|
||||
return false;
|
||||
}
|
||||
m_evmVersion = *versionOption;
|
||||
@ -1064,14 +1071,37 @@ bool CommandLineInterface::processInput()
|
||||
bool optimize = m_args.count(g_argOptimize);
|
||||
if (m_args.count(g_strOptimizeYul))
|
||||
{
|
||||
serr() << "--optimize-yul is invalid in assembly mode. Use --optimize instead." << endl;
|
||||
serr() << "--" << g_strOptimizeYul << " is invalid in assembly mode. Use --" << g_argOptimize << " instead." << endl;
|
||||
return false;
|
||||
}
|
||||
if (m_args.count(g_strNoOptimizeYul))
|
||||
{
|
||||
serr() << "--no-optimize-yul is invalid in assembly mode. Optimization is disabled by default and can be enabled with --optimize." << endl;
|
||||
serr() << "--" << g_strNoOptimizeYul << " is invalid in assembly mode. Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
optional<string> yulOptimiserSteps;
|
||||
if (m_args.count(g_strYulOptimizations))
|
||||
{
|
||||
if (!optimize)
|
||||
{
|
||||
serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as<string>());
|
||||
}
|
||||
catch (yul::OptimizerException const& _exception)
|
||||
{
|
||||
serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
yulOptimiserSteps = m_args[g_strYulOptimizations].as<string>();
|
||||
}
|
||||
|
||||
if (m_args.count(g_argMachine))
|
||||
{
|
||||
string machine = m_args[g_argMachine].as<string>();
|
||||
@ -1083,7 +1113,7 @@ bool CommandLineInterface::processInput()
|
||||
targetMachine = Machine::Ewasm;
|
||||
else
|
||||
{
|
||||
serr() << "Invalid option for --machine: " << machine << endl;
|
||||
serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1099,13 +1129,14 @@ bool CommandLineInterface::processInput()
|
||||
inputLanguage = Input::Ewasm;
|
||||
if (targetMachine != Machine::Ewasm)
|
||||
{
|
||||
serr() << "If you select Ewasm as --yul-dialect, --machine has to be Ewasm as well." << endl;
|
||||
serr() << "If you select Ewasm as --" << g_strYulDialect << ", ";
|
||||
serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
serr() << "Invalid option for --yul-dialect: " << dialect << endl;
|
||||
serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1122,7 +1153,7 @@ bool CommandLineInterface::processInput()
|
||||
"Warning: Yul is still experimental. Please use the output with care." <<
|
||||
endl;
|
||||
|
||||
return assemble(inputLanguage, targetMachine, optimize);
|
||||
return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps);
|
||||
}
|
||||
if (m_args.count(g_argLink))
|
||||
{
|
||||
@ -1142,7 +1173,7 @@ bool CommandLineInterface::processInput()
|
||||
m_metadataHash = CompilerStack::MetadataHash::None;
|
||||
else
|
||||
{
|
||||
serr() << "Invalid option for --metadata-hash: " << hashStr << endl;
|
||||
serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1534,18 +1565,21 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _
|
||||
bool CommandLineInterface::assemble(
|
||||
yul::AssemblyStack::Language _language,
|
||||
yul::AssemblyStack::Machine _targetMachine,
|
||||
bool _optimize
|
||||
bool _optimize,
|
||||
optional<string> _yulOptimiserSteps
|
||||
)
|
||||
{
|
||||
solAssert(_optimize || !_yulOptimiserSteps.has_value(), "");
|
||||
|
||||
bool successful = true;
|
||||
map<string, yul::AssemblyStack> assemblyStacks;
|
||||
for (auto const& src: m_sourceCodes)
|
||||
{
|
||||
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(
|
||||
m_evmVersion,
|
||||
_language,
|
||||
_optimize ? OptimiserSettings::full() : OptimiserSettings::minimal()
|
||||
);
|
||||
OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal();
|
||||
if (_yulOptimiserSteps.has_value())
|
||||
settings.yulOptimiserSteps = _yulOptimiserSteps.value();
|
||||
|
||||
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings);
|
||||
try
|
||||
{
|
||||
if (!stack.parseAndAnalyze(src.first, src.second))
|
||||
|
@ -56,7 +56,12 @@ private:
|
||||
/// @returns the full object with library placeholder hints in hex.
|
||||
static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj);
|
||||
|
||||
bool assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize);
|
||||
bool assemble(
|
||||
yul::AssemblyStack::Language _language,
|
||||
yul::AssemblyStack::Machine _targetMachine,
|
||||
bool _optimize,
|
||||
std::optional<std::string> _yulOptimiserSteps = std::nullopt
|
||||
);
|
||||
|
||||
void outputCompilationResults();
|
||||
|
||||
|
@ -187,7 +187,7 @@ add_executable(soltest ${sources}
|
||||
${libsolidity_util_sources}
|
||||
${yul_phaser_sources}
|
||||
)
|
||||
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::program_options Boost::unit_test_framework evmc)
|
||||
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::filesystem Boost::program_options Boost::unit_test_framework evmc)
|
||||
|
||||
|
||||
# Special compilation flag for Visual Studio (version 2019 at least affected)
|
||||
|
@ -94,7 +94,7 @@ CommonOptions::CommonOptions(std::string _caption):
|
||||
("evmonepath", po::value<fs::path>(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library")
|
||||
("no-smt", po::bool_switch(&disableSMT), "disable SMT checker")
|
||||
("optimize", po::bool_switch(&optimize), "enables optimization")
|
||||
("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization")
|
||||
("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.")
|
||||
("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2")
|
||||
("show-messages", po::bool_switch(&showMessages), "enables message output")
|
||||
("show-metadata", po::bool_switch(&showMetadata), "enables metadata output");
|
||||
|
@ -46,7 +46,7 @@ struct CommonOptions: boost::noncopyable
|
||||
boost::filesystem::path evmonePath;
|
||||
boost::filesystem::path testPath;
|
||||
bool optimize = false;
|
||||
bool optimizeYul = false;
|
||||
bool enforceViaYul = false;
|
||||
bool disableSMT = false;
|
||||
bool useABIEncoderV2 = false;
|
||||
bool showMessages = false;
|
||||
|
@ -49,9 +49,7 @@ ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion):
|
||||
m_showMessages(solidity::test::CommonOptions::get().showMessages),
|
||||
m_evmHost(make_shared<EVMHost>(m_evmVersion))
|
||||
{
|
||||
if (solidity::test::CommonOptions::get().optimizeYul)
|
||||
m_optimiserSettings = solidity::frontend::OptimiserSettings::full();
|
||||
else if (solidity::test::CommonOptions::get().optimize)
|
||||
if (solidity::test::CommonOptions::get().optimize)
|
||||
m_optimiserSettings = solidity::frontend::OptimiserSettings::standard();
|
||||
|
||||
reset();
|
||||
|
@ -40,6 +40,11 @@ void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const
|
||||
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl;
|
||||
}
|
||||
|
||||
void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix)
|
||||
{
|
||||
printSettings(_stream, _linePrefix);
|
||||
}
|
||||
|
||||
bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
|
||||
{
|
||||
string extension = _filename.extension().string();
|
||||
|
@ -38,6 +38,7 @@ public:
|
||||
{
|
||||
std::string filename;
|
||||
langutil::EVMVersion evmVersion;
|
||||
bool enforceCompileViaYul;
|
||||
};
|
||||
|
||||
enum class TestResult { Success, Failure, FatalError };
|
||||
@ -59,6 +60,8 @@ public:
|
||||
virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0;
|
||||
/// Outputs settings.
|
||||
virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
|
||||
/// Outputs updated settings
|
||||
virtual void printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix = "");
|
||||
/// Outputs test expectations to @arg _stream that match the actual results of the test.
|
||||
/// Each line of output is prefixed with @arg _linePrefix.
|
||||
virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0;
|
||||
|
@ -64,12 +64,13 @@ int registerTests(
|
||||
boost::unit_test::test_suite& _suite,
|
||||
boost::filesystem::path const& _basepath,
|
||||
boost::filesystem::path const& _path,
|
||||
bool _enforceViaYul,
|
||||
TestCase::TestCaseCreator _testCaseCreator
|
||||
)
|
||||
{
|
||||
int numTestsAdded = 0;
|
||||
fs::path fullpath = _basepath / _path;
|
||||
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion()};
|
||||
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul};
|
||||
if (fs::is_directory(fullpath))
|
||||
{
|
||||
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
|
||||
@ -78,7 +79,12 @@ int registerTests(
|
||||
fs::directory_iterator()
|
||||
))
|
||||
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
|
||||
numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename(), _testCaseCreator);
|
||||
numTestsAdded += registerTests(
|
||||
*sub_suite,
|
||||
_basepath, _path / entry.path().filename(),
|
||||
_enforceViaYul,
|
||||
_testCaseCreator
|
||||
);
|
||||
_suite.add(sub_suite);
|
||||
}
|
||||
else
|
||||
@ -164,6 +170,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
||||
master,
|
||||
options.testPath / ts.path,
|
||||
ts.subpath,
|
||||
options.enforceViaYul,
|
||||
ts.testCaseCreator
|
||||
) > 0, std::string("no ") + ts.title + " tests found");
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user