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
|
```sh
|
||||||
cd .circleci/docker/
|
cd .circleci/docker/
|
||||||
|
|
||||||
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 .
|
docker build -t ethereum/solidity-buildpack-deps:ubuntu2004-<revision> -f Dockerfile.ubuntu2004 .
|
||||||
docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
|
docker push ethereum/solidity-buildpack-deps:ubuntu2004-<revision>
|
||||||
```
|
```
|
||||||
|
|
||||||
The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `<image-desc>-docker-image-rev` (e.g., `ubuntu-1904-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ.
|
The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `<image-desc>-docker-image-rev` (e.g., `ubuntu-2004-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ.
|
||||||
|
|
||||||
Once the docker image has been built and pushed to Dockerhub, you can find it at:
|
Once the docker image has been built and pushed to Dockerhub, you can find it at:
|
||||||
|
|
||||||
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
|
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu2004-<revision>
|
||||||
|
|
||||||
where the image tag reflects the target OS and revision to build Solidity and run its tests on.
|
where the image tag reflects the target OS and revision to build Solidity and run its tests on.
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ where the image tag reflects the target OS and revision to build Solidity and ru
|
|||||||
```sh
|
```sh
|
||||||
cd solidity
|
cd solidity
|
||||||
# Mounts your local solidity directory in docker container for testing
|
# Mounts your local solidity directory in docker container for testing
|
||||||
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904-<revision> /bin/bash
|
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu2004-<revision> /bin/bash
|
||||||
cd /src/solidity
|
cd /src/solidity
|
||||||
<commands_to_test_build_with_new_docker_image>
|
<commands_to_test_build_with_new_docker_image>
|
||||||
```
|
```
|
||||||
|
@ -10,12 +10,12 @@ parameters:
|
|||||||
ubuntu-1804-docker-image-rev:
|
ubuntu-1804-docker-image-rev:
|
||||||
type: string
|
type: string
|
||||||
default: "4"
|
default: "4"
|
||||||
ubuntu-1904-docker-image-rev:
|
ubuntu-2004-docker-image-rev:
|
||||||
type: string
|
type: string
|
||||||
default: "4"
|
default: "1"
|
||||||
ubuntu-1904-clang-docker-image-rev:
|
ubuntu-2004-clang-docker-image-rev:
|
||||||
type: string
|
type: string
|
||||||
default: "5"
|
default: "1"
|
||||||
ubuntu-1604-clang-ossfuzz-docker-image-rev:
|
ubuntu-1604-clang-ossfuzz-docker-image-rev:
|
||||||
type: string
|
type: string
|
||||||
default: "2"
|
default: "2"
|
||||||
@ -50,6 +50,7 @@ defaults:
|
|||||||
cd build
|
cd build
|
||||||
protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz
|
protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz
|
||||||
protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz
|
protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz
|
||||||
|
protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS
|
cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS
|
||||||
make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4
|
make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4
|
||||||
|
|
||||||
@ -97,6 +98,7 @@ defaults:
|
|||||||
- test/tools/ossfuzz/strictasm_opt_ossfuzz
|
- test/tools/ossfuzz/strictasm_opt_ossfuzz
|
||||||
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
|
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
|
||||||
- test/tools/ossfuzz/yul_proto_ossfuzz
|
- test/tools/ossfuzz/yul_proto_ossfuzz
|
||||||
|
- test/tools/ossfuzz/sol_proto_ossfuzz
|
||||||
|
|
||||||
# test result output directory
|
# test result output directory
|
||||||
- artifacts_test_results: &artifacts_test_results
|
- artifacts_test_results: &artifacts_test_results
|
||||||
@ -137,9 +139,9 @@ defaults:
|
|||||||
- store_test_results: *store_test_results
|
- store_test_results: *store_test_results
|
||||||
- store_artifacts: *artifacts_test_results
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
- test_ubuntu1904_clang: &test_ubuntu1904_clang
|
- test_ubuntu2004_clang: &test_ubuntu2004_clang
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >>
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
@ -148,9 +150,9 @@ defaults:
|
|||||||
- store_test_results: *store_test_results
|
- store_test_results: *store_test_results
|
||||||
- store_artifacts: *artifacts_test_results
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
- test_ubuntu1904: &test_ubuntu1904
|
- test_ubuntu2004: &test_ubuntu2004
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
@ -160,7 +162,7 @@ defaults:
|
|||||||
- store_artifacts: *artifacts_test_results
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
- test_asan: &test_asan
|
- test_asan: &test_asan
|
||||||
<<: *test_ubuntu1904
|
<<: *test_ubuntu2004
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
@ -179,7 +181,7 @@ defaults:
|
|||||||
tags:
|
tags:
|
||||||
only: /.*/
|
only: /.*/
|
||||||
|
|
||||||
- workflow_ubuntu1904: &workflow_ubuntu1904
|
- workflow_ubuntu2004: &workflow_ubuntu2004
|
||||||
<<: *workflow_trigger_on_tags
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_ubu
|
- b_ubu
|
||||||
@ -189,17 +191,17 @@ defaults:
|
|||||||
requires:
|
requires:
|
||||||
- b_ubu_ossfuzz
|
- b_ubu_ossfuzz
|
||||||
|
|
||||||
- workflow_ubuntu1904_clang: &workflow_ubuntu1904_clang
|
- workflow_ubuntu2004_clang: &workflow_ubuntu2004_clang
|
||||||
<<: *workflow_trigger_on_tags
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_ubu_clang
|
- b_ubu_clang
|
||||||
|
|
||||||
- workflow_ubuntu1904_release: &workflow_ubuntu1904_release
|
- workflow_ubuntu2004_release: &workflow_ubuntu2004_release
|
||||||
<<: *workflow_trigger_on_tags
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_ubu_release
|
- b_ubu_release
|
||||||
|
|
||||||
- workflow_ubuntu1904_codecov: &workflow_ubuntu1904_codecov
|
- workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov
|
||||||
<<: *workflow_trigger_on_tags
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_ubu_codecov
|
- b_ubu_codecov
|
||||||
@ -209,7 +211,7 @@ defaults:
|
|||||||
requires:
|
requires:
|
||||||
- b_osx
|
- b_osx
|
||||||
|
|
||||||
- workflow_ubuntu1904_asan: &workflow_ubuntu1904_asan
|
- workflow_ubuntu2004_asan: &workflow_ubuntu2004_asan
|
||||||
<<: *workflow_trigger_on_tags
|
<<: *workflow_trigger_on_tags
|
||||||
requires:
|
requires:
|
||||||
- b_ubu_asan
|
- b_ubu_asan
|
||||||
@ -359,16 +361,16 @@ jobs:
|
|||||||
|
|
||||||
chk_docs_pragma_min_version:
|
chk_docs_pragma_min_version:
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: *run_docs_pragma_min_version
|
- run: *run_docs_pragma_min_version
|
||||||
|
|
||||||
b_ubu_clang: &build_ubuntu1904_clang
|
b_ubu_clang: &build_ubuntu2004_clang
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >>
|
||||||
environment:
|
environment:
|
||||||
CC: clang
|
CC: clang
|
||||||
CXX: clang++
|
CXX: clang++
|
||||||
@ -378,9 +380,9 @@ jobs:
|
|||||||
- store_artifacts: *artifacts_solc
|
- store_artifacts: *artifacts_solc
|
||||||
- persist_to_workspace: *artifacts_executables
|
- persist_to_workspace: *artifacts_executables
|
||||||
|
|
||||||
b_ubu: &build_ubuntu1904
|
b_ubu: &build_ubuntu2004
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: *run_build
|
- run: *run_build
|
||||||
@ -388,8 +390,8 @@ jobs:
|
|||||||
- store_artifacts: *artifacts_tools
|
- store_artifacts: *artifacts_tools
|
||||||
- persist_to_workspace: *artifacts_executables
|
- persist_to_workspace: *artifacts_executables
|
||||||
|
|
||||||
b_ubu_release: &build_ubuntu1904_release
|
b_ubu_release: &build_ubuntu2004_release
|
||||||
<<: *build_ubuntu1904
|
<<: *build_ubuntu2004
|
||||||
environment:
|
environment:
|
||||||
FORCE_RELEASE: ON
|
FORCE_RELEASE: ON
|
||||||
|
|
||||||
@ -406,7 +408,7 @@ jobs:
|
|||||||
- persist_to_workspace: *artifacts_executables
|
- persist_to_workspace: *artifacts_executables
|
||||||
|
|
||||||
b_ubu_codecov:
|
b_ubu_codecov:
|
||||||
<<: *build_ubuntu1904
|
<<: *build_ubuntu2004
|
||||||
environment:
|
environment:
|
||||||
COVERAGE: ON
|
COVERAGE: ON
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
@ -416,7 +418,7 @@ jobs:
|
|||||||
- persist_to_workspace: *artifacts_build_dir
|
- persist_to_workspace: *artifacts_build_dir
|
||||||
|
|
||||||
t_ubu_codecov:
|
t_ubu_codecov:
|
||||||
<<: *test_ubuntu1904
|
<<: *test_ubuntu2004
|
||||||
environment:
|
environment:
|
||||||
EVM: constantinople
|
EVM: constantinople
|
||||||
OPTIMIZE: 1
|
OPTIMIZE: 1
|
||||||
@ -439,7 +441,7 @@ jobs:
|
|||||||
# Builds in C++20 mode and uses debug build in order to speed up.
|
# Builds in C++20 mode and uses debug build in order to speed up.
|
||||||
# Do *NOT* store any artifacts or workspace as we don't run tests on this build.
|
# Do *NOT* store any artifacts or workspace as we don't run tests on this build.
|
||||||
b_ubu_cxx20:
|
b_ubu_cxx20:
|
||||||
<<: *build_ubuntu1904
|
<<: *build_ubuntu2004
|
||||||
environment:
|
environment:
|
||||||
CMAKE_BUILD_TYPE: Debug
|
CMAKE_BUILD_TYPE: Debug
|
||||||
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF
|
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF
|
||||||
@ -591,7 +593,7 @@ jobs:
|
|||||||
|
|
||||||
# x64 ASAN build, for testing for memory related bugs
|
# x64 ASAN build, for testing for memory related bugs
|
||||||
b_ubu_asan: &b_ubu_asan
|
b_ubu_asan: &b_ubu_asan
|
||||||
<<: *build_ubuntu1904
|
<<: *build_ubuntu2004
|
||||||
environment:
|
environment:
|
||||||
CMAKE_OPTIONS: -DSANITIZE=address
|
CMAKE_OPTIONS: -DSANITIZE=address
|
||||||
CMAKE_BUILD_TYPE: Release
|
CMAKE_BUILD_TYPE: Release
|
||||||
@ -603,7 +605,7 @@ jobs:
|
|||||||
|
|
||||||
b_docs:
|
b_docs:
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: *setup_prerelease_commit_hash
|
- run: *setup_prerelease_commit_hash
|
||||||
@ -615,10 +617,27 @@ jobs:
|
|||||||
destination: docs-html
|
destination: docs-html
|
||||||
|
|
||||||
t_ubu_soltest: &t_ubu_soltest
|
t_ubu_soltest: &t_ubu_soltest
|
||||||
<<: *test_ubuntu1904
|
<<: *test_ubuntu2004
|
||||||
|
|
||||||
|
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
|
||||||
|
docker:
|
||||||
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||||
|
environment:
|
||||||
|
EVM: constantinople
|
||||||
|
SOLTEST_FLAGS: --enforce-via-yul
|
||||||
|
OPTIMIZE: 0
|
||||||
|
TERM: xterm
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- attach_workspace:
|
||||||
|
at: build
|
||||||
|
- run: *run_soltest
|
||||||
|
- store_test_results: *store_test_results
|
||||||
|
- store_artifacts: *artifacts_test_results
|
||||||
|
|
||||||
|
|
||||||
t_ubu_clang_soltest: &t_ubu_clang_soltest
|
t_ubu_clang_soltest: &t_ubu_clang_soltest
|
||||||
<<: *test_ubuntu1904_clang
|
<<: *test_ubuntu2004_clang
|
||||||
environment:
|
environment:
|
||||||
EVM: constantinople
|
EVM: constantinople
|
||||||
OPTIMIZE: 0
|
OPTIMIZE: 0
|
||||||
@ -628,7 +647,7 @@ jobs:
|
|||||||
|
|
||||||
t_ubu_cli: &t_ubu_cli
|
t_ubu_cli: &t_ubu_cli
|
||||||
docker:
|
docker:
|
||||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
|
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
|
||||||
environment:
|
environment:
|
||||||
TERM: xterm
|
TERM: xterm
|
||||||
steps:
|
steps:
|
||||||
@ -816,20 +835,21 @@ workflows:
|
|||||||
# Ubuntu build and tests
|
# Ubuntu build and tests
|
||||||
- b_ubu: *workflow_trigger_on_tags
|
- b_ubu: *workflow_trigger_on_tags
|
||||||
- b_ubu18: *workflow_trigger_on_tags
|
- b_ubu18: *workflow_trigger_on_tags
|
||||||
- t_ubu_cli: *workflow_ubuntu1904
|
- t_ubu_cli: *workflow_ubuntu2004
|
||||||
- t_ubu_soltest: *workflow_ubuntu1904
|
- t_ubu_soltest: *workflow_ubuntu2004
|
||||||
|
- t_ubu_soltest_enforce_yul: *workflow_ubuntu2004
|
||||||
- b_ubu_clang: *workflow_trigger_on_tags
|
- b_ubu_clang: *workflow_trigger_on_tags
|
||||||
- t_ubu_clang_soltest: *workflow_ubuntu1904_clang
|
- t_ubu_clang_soltest: *workflow_ubuntu2004_clang
|
||||||
|
|
||||||
# Ubuntu fake release build and tests
|
# Ubuntu fake release build and tests
|
||||||
- b_ubu_release: *workflow_trigger_on_tags
|
- b_ubu_release: *workflow_trigger_on_tags
|
||||||
- t_ubu_release_cli: *workflow_ubuntu1904_release
|
- t_ubu_release_cli: *workflow_ubuntu2004_release
|
||||||
- t_ubu_release_soltest: *workflow_ubuntu1904_release
|
- t_ubu_release_soltest: *workflow_ubuntu2004_release
|
||||||
|
|
||||||
# ASan build and tests
|
# ASan build and tests
|
||||||
- b_ubu_asan: *workflow_trigger_on_tags
|
- b_ubu_asan: *workflow_trigger_on_tags
|
||||||
- t_ubu_asan_constantinople: *workflow_ubuntu1904_asan
|
- t_ubu_asan_constantinople: *workflow_ubuntu2004_asan
|
||||||
- t_ubu_asan_cli: *workflow_ubuntu1904_asan
|
- t_ubu_asan_cli: *workflow_ubuntu2004_asan
|
||||||
|
|
||||||
# Emscripten build and selected tests
|
# Emscripten build and selected tests
|
||||||
- b_ems: *workflow_trigger_on_tags
|
- b_ems: *workflow_trigger_on_tags
|
||||||
@ -856,4 +876,4 @@ workflows:
|
|||||||
|
|
||||||
# Code Coverage enabled build and tests
|
# Code Coverage enabled build and tests
|
||||||
- b_ubu_codecov: *workflow_trigger_on_tags
|
- b_ubu_codecov: *workflow_trigger_on_tags
|
||||||
- t_ubu_codecov: *workflow_ubuntu1904_codecov
|
- t_ubu_codecov: *workflow_ubuntu2004_codecov
|
||||||
|
@ -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.
|
# (c) 2016-2019 solidity contributors.
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
FROM buildpack-deps:disco AS base
|
FROM buildpack-deps:focal AS base
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \
|
|
||||||
echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \
|
|
||||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \
|
|
||||||
apt-get update; \
|
apt-get update; \
|
||||||
apt-get install -qqy --no-install-recommends \
|
apt-get install -qqy --no-install-recommends \
|
||||||
build-essential \
|
build-essential \
|
||||||
software-properties-common \
|
software-properties-common \
|
||||||
cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \
|
cmake ninja-build \
|
||||||
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
|
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
|
||||||
libboost-program-options-dev \
|
libboost-program-options-dev \
|
||||||
libjsoncpp-dev \
|
libcvc4-dev z3 libz3-dev \
|
||||||
llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \
|
|
||||||
; \
|
; \
|
||||||
apt-get install -qy python-pip python-sphinx; \
|
apt-get install -qy python3-pip python3-sphinx; \
|
||||||
update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \
|
pip3 install codecov; \
|
||||||
pip install codecov; \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
FROM base AS libraries
|
FROM base AS libraries
|
||||||
|
|
||||||
# OSSFUZZ: libprotobuf-mutator
|
|
||||||
RUN set -ex; \
|
|
||||||
git clone https://github.com/google/libprotobuf-mutator.git \
|
|
||||||
/usr/src/libprotobuf-mutator; \
|
|
||||||
cd /usr/src/libprotobuf-mutator; \
|
|
||||||
git checkout d1fe8a7d8ae18f3d454f055eba5213c291986f21; \
|
|
||||||
mkdir build; \
|
|
||||||
cd build; \
|
|
||||||
cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \
|
|
||||||
-DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_INSTALL_PREFIX="/usr"; \
|
|
||||||
ninja; \
|
|
||||||
cp -vpr external.protobuf/bin/* /usr/bin/; \
|
|
||||||
cp -vpr external.protobuf/include/* /usr/include/; \
|
|
||||||
cp -vpr external.protobuf/lib/* /usr/lib/; \
|
|
||||||
ninja install/strip; \
|
|
||||||
rm -rf /usr/src/libprotobuf-mutator
|
|
||||||
|
|
||||||
# OSSFUZZ: libfuzzer
|
|
||||||
RUN set -ex; \
|
|
||||||
cd /var/tmp; \
|
|
||||||
svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer libfuzzer; \
|
|
||||||
mkdir -p build-libfuzzer; \
|
|
||||||
cd build-libfuzzer; \
|
|
||||||
clang++-8 -O1 -stdlib=libstdc++ -std=c++11 -O2 -fPIC -c ../libfuzzer/*.cpp -I../libfuzzer; \
|
|
||||||
ar r /usr/lib/libFuzzingEngine.a *.o; \
|
|
||||||
rm -rf /var/lib/libfuzzer
|
|
||||||
|
|
||||||
# EVMONE
|
# EVMONE
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
cd /usr/src; \
|
cd /usr/src; \
|
||||||
@ -81,7 +48,6 @@ RUN set -ex; \
|
|||||||
cd evmone; \
|
cd evmone; \
|
||||||
mkdir build; \
|
mkdir build; \
|
||||||
cd build; \
|
cd build; \
|
||||||
# isoltest links against the evmone shared library
|
|
||||||
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
|
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
|
||||||
ninja; \
|
ninja; \
|
||||||
ninja install/strip; \
|
ninja install/strip; \
|
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"
|
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml"
|
||||||
SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
|
SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
|
||||||
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
|
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
|
||||||
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul"
|
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2"
|
||||||
|
|
||||||
echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS} -- ${SOLTEST_ARGS}"
|
echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS} -- ${SOLTEST_ARGS}"
|
||||||
|
|
||||||
|
14
Changelog.md
14
Changelog.md
@ -16,17 +16,25 @@ Bugfixes:
|
|||||||
|
|
||||||
### 0.6.8 (unreleased)
|
### 0.6.8 (unreleased)
|
||||||
|
|
||||||
Language Features:
|
Important Bugfixes:
|
||||||
|
* Add missing callvalue check to the creation code of a contract that does not define a constructor but has a base that does define a constructor.
|
||||||
|
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* Implemented ``type(X).min`` and ``type(X).max`` for every integer type ``X`` that returns the smallest and largest value representable by the type.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
* Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
* ABI: Skip ``private`` or ``internal`` constructors.
|
||||||
|
* Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables.
|
||||||
|
* Fixed an "Assembly Exception in Bytecode" error where requested functions were generated twice.
|
||||||
|
* Natspec: Fixed a bug that ignored ``@return`` tag when no other developer-documentation tags were present.
|
||||||
|
* Yul assembler: Fix source location of variable declarations without value.
|
||||||
|
|
||||||
|
|
||||||
### 0.6.7 (2020-05-04)
|
### 0.6.7 (2020-05-04)
|
||||||
|
@ -1,4 +1,12 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"name": "ImplicitConstructorCallvalueCheck",
|
||||||
|
"summary": "The creation code of a contract that does not define a constructor but has a base that does define a constructor did not revert for calls with non-zero value.",
|
||||||
|
"description": "Starting from Solidity 0.4.5 the creation code of contracts without explicit payable constructor is supposed to contain a callvalue check that results in contract creation reverting, if non-zero value is passed. However, this check was missing in case no explicit constructor was defined in a contract at all, but the contract has a base that does define a constructor. In these cases it is possible to send value in a contract creation transaction or using inline assembly without revert, even though the creation code is supposed to be non-payable.",
|
||||||
|
"introduced": "0.4.5",
|
||||||
|
"fixed": "0.6.8",
|
||||||
|
"severity": "very low"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "TupleAssignmentMultiStackSlotComponents",
|
"name": "TupleAssignmentMultiStackSlotComponents",
|
||||||
"summary": "Tuple assignments with components that occupy several stack slots, i.e. nested tuples, pointers to external functions or references to dynamically sized calldata arrays, can result in invalid values.",
|
"summary": "Tuple assignments with components that occupy several stack slots, i.e. nested tuples, pointers to external functions or references to dynamically sized calldata arrays, can result in invalid values.",
|
||||||
|
@ -415,6 +415,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.10": {
|
"0.4.10": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -433,6 +434,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.11": {
|
"0.4.11": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -450,6 +452,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.12": {
|
"0.4.12": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -466,6 +469,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.13": {
|
"0.4.13": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -482,6 +486,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.14": {
|
"0.4.14": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -497,6 +502,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.15": {
|
"0.4.15": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -511,6 +517,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.16": {
|
"0.4.16": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -527,6 +534,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.17": {
|
"0.4.17": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -544,6 +552,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.18": {
|
"0.4.18": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -560,6 +569,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.19": {
|
"0.4.19": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -596,6 +606,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.20": {
|
"0.4.20": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -613,6 +624,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.21": {
|
"0.4.21": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -630,6 +642,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.22": {
|
"0.4.22": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -647,6 +660,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.23": {
|
"0.4.23": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -663,6 +677,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.24": {
|
"0.4.24": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -679,6 +694,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.25": {
|
"0.4.25": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -693,6 +709,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.26": {
|
"0.4.26": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -739,6 +756,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.5": {
|
"0.4.5": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -758,6 +776,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.6": {
|
"0.4.6": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -776,6 +795,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.7": {
|
"0.4.7": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -794,6 +814,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.8": {
|
"0.4.8": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -812,6 +833,7 @@
|
|||||||
},
|
},
|
||||||
"0.4.9": {
|
"0.4.9": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -830,6 +852,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.0": {
|
"0.5.0": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -844,6 +867,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.1": {
|
"0.5.1": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -858,6 +882,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.10": {
|
"0.5.10": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -868,6 +893,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.11": {
|
"0.5.11": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -877,6 +903,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.12": {
|
"0.5.12": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -886,6 +913,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.13": {
|
"0.5.13": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -895,6 +923,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.14": {
|
"0.5.14": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -905,6 +934,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.15": {
|
"0.5.15": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -914,6 +944,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.16": {
|
"0.5.16": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden"
|
"privateCanBeOverridden"
|
||||||
@ -922,6 +953,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.17": {
|
"0.5.17": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -929,6 +961,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.2": {
|
"0.5.2": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -943,6 +976,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.3": {
|
"0.5.3": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -957,6 +991,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.4": {
|
"0.5.4": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -971,6 +1006,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.5": {
|
"0.5.5": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -987,6 +1023,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.6": {
|
"0.5.6": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1003,6 +1040,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.7": {
|
"0.5.7": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1017,6 +1055,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.8": {
|
"0.5.8": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1030,6 +1069,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.9": {
|
"0.5.9": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1042,6 +1082,7 @@
|
|||||||
},
|
},
|
||||||
"0.6.0": {
|
"0.6.0": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"YulOptimizerRedundantAssignmentBreakContinue"
|
"YulOptimizerRedundantAssignmentBreakContinue"
|
||||||
@ -1050,6 +1091,7 @@
|
|||||||
},
|
},
|
||||||
"0.6.1": {
|
"0.6.1": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1057,6 +1099,7 @@
|
|||||||
},
|
},
|
||||||
"0.6.2": {
|
"0.6.2": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1064,6 +1107,7 @@
|
|||||||
},
|
},
|
||||||
"0.6.3": {
|
"0.6.3": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1071,6 +1115,7 @@
|
|||||||
},
|
},
|
||||||
"0.6.4": {
|
"0.6.4": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1078,16 +1123,21 @@
|
|||||||
},
|
},
|
||||||
"0.6.5": {
|
"0.6.5": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents"
|
"TupleAssignmentMultiStackSlotComponents"
|
||||||
],
|
],
|
||||||
"released": "2020-04-06"
|
"released": "2020-04-06"
|
||||||
},
|
},
|
||||||
"0.6.6": {
|
"0.6.6": {
|
||||||
"bugs": [],
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck"
|
||||||
|
],
|
||||||
"released": "2020-04-09"
|
"released": "2020-04-09"
|
||||||
},
|
},
|
||||||
"0.6.7": {
|
"0.6.7": {
|
||||||
"bugs": [],
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck"
|
||||||
|
],
|
||||||
"released": "2020-05-04"
|
"released": "2020-05-04"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -121,6 +121,8 @@ Global Variables
|
|||||||
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||||
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
|
||||||
- ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information<meta-type>`.
|
- ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information<meta-type>`.
|
||||||
|
- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||||
|
- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
|
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
|
||||||
|
@ -40,6 +40,9 @@ Operators:
|
|||||||
* Shift operators: ``<<`` (left shift), ``>>`` (right shift)
|
* Shift operators: ``<<`` (left shift), ``>>`` (right shift)
|
||||||
* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation)
|
* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation)
|
||||||
|
|
||||||
|
For an integer type ``X``, you can use ``type(X).min`` and ``type(X).max`` to
|
||||||
|
access the minimum and maximum value representable by the type.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
|
|
||||||
Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``.
|
Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``.
|
||||||
|
@ -296,10 +296,11 @@ Furthermore, all functions of the current contract are callable directly includi
|
|||||||
Type Information
|
Type Information
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The expression ``type(X)`` can be used to retrieve information about the
|
The expression ``type(X)`` can be used to retrieve information about the type
|
||||||
type ``X``. Currently, there is limited support for this feature, but
|
``X``. Currently, there is limited support for this feature (``X`` can be either
|
||||||
it might be expanded in the future. The following properties are
|
a contract or an integer type) but it might be expanded in the future.
|
||||||
available for a contract type ``C``:
|
|
||||||
|
The following properties are available for a contract type ``C``:
|
||||||
|
|
||||||
``type(C).name``
|
``type(C).name``
|
||||||
The name of the contract.
|
The name of the contract.
|
||||||
@ -329,3 +330,10 @@ for an interface type ``I``:
|
|||||||
A ``bytes4`` value containing the `EIP-165 <https://eips.ethereum.org/EIPS/eip-165>`_
|
A ``bytes4`` value containing the `EIP-165 <https://eips.ethereum.org/EIPS/eip-165>`_
|
||||||
interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all
|
interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all
|
||||||
function selectors defined within the interface itself - excluding all inherited functions.
|
function selectors defined within the interface itself - excluding all inherited functions.
|
||||||
|
The following properties are available for an integer type ``T``:
|
||||||
|
|
||||||
|
``type(T).min``
|
||||||
|
The smallest value representable by type ``T``.
|
||||||
|
|
||||||
|
``type(T).max``
|
||||||
|
The largest value representable by type ``T``.
|
||||||
|
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.
|
By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly.
|
||||||
You can override this sequence and supply your own using the `--yul-optimizations` option when compiling
|
You can override this sequence and supply your own using the ``--yul-optimizations`` option:
|
||||||
in Solidity mode:
|
|
||||||
|
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
|
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
|
||||||
|
|
||||||
By enclosing part of the sequence in square brackets (`[]`) you tell the optimizer to repeatedly
|
The order of steps is significant and affects the quality of the output.
|
||||||
|
Moreover, applying a step may uncover new optimization opportunities for others that were already
|
||||||
|
applied so repeating steps is often beneficial.
|
||||||
|
By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly
|
||||||
apply that part until it no longer improves the size of the resulting assembly.
|
apply that part until it no longer improves the size of the resulting assembly.
|
||||||
You can use brackets multiple times in a single sequence but they cannot be nested.
|
You can use brackets multiple times in a single sequence but they cannot be nested.
|
||||||
|
|
||||||
@ -1055,35 +1059,230 @@ The following optimization steps are available:
|
|||||||
============ ===============================
|
============ ===============================
|
||||||
Abbreviation Full name
|
Abbreviation Full name
|
||||||
============ ===============================
|
============ ===============================
|
||||||
f `BlockFlattener`
|
``f`` ``BlockFlattener``
|
||||||
l `CircularReferencesPruner`
|
``l`` ``CircularReferencesPruner``
|
||||||
c `CommonSubexpressionEliminator`
|
``c`` ``CommonSubexpressionEliminator``
|
||||||
C `ConditionalSimplifier`
|
``C`` ``ConditionalSimplifier``
|
||||||
U `ConditionalUnsimplifier`
|
``U`` ``ConditionalUnsimplifier``
|
||||||
n `ControlFlowSimplifier`
|
``n`` ``ControlFlowSimplifier``
|
||||||
D `DeadCodeEliminator`
|
``D`` ``DeadCodeEliminator``
|
||||||
v `EquivalentFunctionCombiner`
|
``v`` ``EquivalentFunctionCombiner``
|
||||||
e `ExpressionInliner`
|
``e`` ``ExpressionInliner``
|
||||||
j `ExpressionJoiner`
|
``j`` ``ExpressionJoiner``
|
||||||
s `ExpressionSimplifier`
|
``s`` ``ExpressionSimplifier``
|
||||||
x `ExpressionSplitter`
|
``x`` ``ExpressionSplitter``
|
||||||
I `ForLoopConditionIntoBody`
|
``I`` ``ForLoopConditionIntoBody``
|
||||||
O `ForLoopConditionOutOfBody`
|
``O`` ``ForLoopConditionOutOfBody``
|
||||||
o `ForLoopInitRewriter`
|
``o`` ``ForLoopInitRewriter``
|
||||||
i `FullInliner`
|
``i`` ``FullInliner``
|
||||||
g `FunctionGrouper`
|
``g`` ``FunctionGrouper``
|
||||||
h `FunctionHoister`
|
``h`` ``FunctionHoister``
|
||||||
T `LiteralRematerialiser`
|
``T`` ``LiteralRematerialiser``
|
||||||
L `LoadResolver`
|
``L`` ``LoadResolver``
|
||||||
M `LoopInvariantCodeMotion`
|
``M`` ``LoopInvariantCodeMotion``
|
||||||
r `RedundantAssignEliminator`
|
``r`` ``RedundantAssignEliminator``
|
||||||
m `Rematerialiser`
|
``m`` ``Rematerialiser``
|
||||||
V `SSAReverser`
|
``V`` ``SSAReverser``
|
||||||
a `SSATransform`
|
``a`` ``SSATransform``
|
||||||
t `StructuralSimplifier`
|
``t`` ``StructuralSimplifier``
|
||||||
u `UnusedPruner`
|
``u`` ``UnusedPruner``
|
||||||
d `VarDeclInitializer`
|
``d`` ``VarDeclInitializer``
|
||||||
============ ===============================
|
============ ===============================
|
||||||
|
|
||||||
Some steps depend on properties ensured by `BlockFlattener`, `FunctionGrouper`, `ForLoopInitRewriter`.
|
Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``.
|
||||||
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
|
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
|
||||||
|
|
||||||
|
|
||||||
|
.. _erc20yul:
|
||||||
|
|
||||||
|
Complete ERC20 Example
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. code-block:: yul
|
||||||
|
|
||||||
|
object "Token" {
|
||||||
|
code {
|
||||||
|
// Store the creator in slot zero.
|
||||||
|
sstore(0, caller())
|
||||||
|
|
||||||
|
// Deploy the contract
|
||||||
|
datacopy(0, dataoffset("runtime"), datasize("runtime"))
|
||||||
|
return(0, datasize("runtime"))
|
||||||
|
}
|
||||||
|
object "runtime" {
|
||||||
|
code {
|
||||||
|
// Protection against sending Ether
|
||||||
|
require(iszero(callvalue()))
|
||||||
|
|
||||||
|
// Dispatcher
|
||||||
|
switch selector()
|
||||||
|
case 0x70a08231 /* "balanceOf(address)" */ {
|
||||||
|
returnUint(balanceOf(decodeAsAddress(0)))
|
||||||
|
}
|
||||||
|
case 0x18160ddd /* "totalSupply()" */ {
|
||||||
|
returnUint(totalSupply())
|
||||||
|
}
|
||||||
|
case 0xa9059cbb /* "transfer(address,uint256)" */ {
|
||||||
|
transfer(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0x23b872dd /* "transferFrom(address,address,uint256)" */ {
|
||||||
|
transferFrom(decodeAsAddress(0), decodeAsAddress(1), decodeAsUint(2))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0x095ea7b3 /* "approve(address,uint256)" */ {
|
||||||
|
approve(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0xdd62ed3e /* "allowance(address,address)" */ {
|
||||||
|
returnUint(allowance(decodeAsAddress(0), decodeAsAddress(1)))
|
||||||
|
}
|
||||||
|
case 0x40c10f19 /* "mint(address,uint256)" */ {
|
||||||
|
mint(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mint(account, amount) {
|
||||||
|
require(calledByOwner())
|
||||||
|
|
||||||
|
mintTokens(amount)
|
||||||
|
addToBalance(account, amount)
|
||||||
|
emitTransfer(0, account, amount)
|
||||||
|
}
|
||||||
|
function transfer(to, amount) {
|
||||||
|
executeTransfer(caller(), to, amount)
|
||||||
|
}
|
||||||
|
function approve(spender, amount) {
|
||||||
|
revertIfZeroAddress(spender)
|
||||||
|
setAllowance(caller(), spender, amount)
|
||||||
|
emitApproval(caller(), spender, amount)
|
||||||
|
}
|
||||||
|
function transferFrom(from, to, amount) {
|
||||||
|
decreaseAllowanceBy(from, caller(), amount)
|
||||||
|
executeTransfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeTransfer(from, to, amount) {
|
||||||
|
revertIfZeroAddress(to)
|
||||||
|
deductFromBalance(from, amount)
|
||||||
|
addToBalance(to, amount)
|
||||||
|
emitTransfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- calldata decoding functions ----------- */
|
||||||
|
function selector() -> s {
|
||||||
|
s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeAsAddress(offset) -> v {
|
||||||
|
v := decodeAsUint(offset)
|
||||||
|
if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function decodeAsUint(offset) -> v {
|
||||||
|
let pos := add(4, mul(offset, 0x20))
|
||||||
|
if lt(calldatasize(), add(pos, 0x20)) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
v := calldataload(pos)
|
||||||
|
}
|
||||||
|
/* ---------- calldata encoding functions ---------- */
|
||||||
|
function returnUint(v) {
|
||||||
|
mstore(0, v)
|
||||||
|
return(0, 0x20)
|
||||||
|
}
|
||||||
|
function returnTrue() {
|
||||||
|
returnUint(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- events ---------- */
|
||||||
|
function emitTransfer(from, to, amount) {
|
||||||
|
let signatureHash := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
|
||||||
|
emitEvent(signatureHash, from, to, amount)
|
||||||
|
}
|
||||||
|
function emitApproval(from, spender, amount) {
|
||||||
|
let signatureHash := 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
|
||||||
|
emitEvent(signatureHash, from, spender, amount)
|
||||||
|
}
|
||||||
|
function emitEvent(signatureHash, indexed1, indexed2, nonIndexed) {
|
||||||
|
mstore(0, nonIndexed)
|
||||||
|
log3(0, 0x20, signatureHash, indexed1, indexed2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- storage layout ---------- */
|
||||||
|
function ownerPos() -> p { p := 0 }
|
||||||
|
function totalSupplyPos() -> p { p := 1 }
|
||||||
|
function accountToStorageOffset(account) -> offset {
|
||||||
|
offset := add(0x1000, account)
|
||||||
|
}
|
||||||
|
function allowanceStorageOffset(account, spender) -> offset {
|
||||||
|
offset := accountToStorageOffset(account)
|
||||||
|
mstore(0, offset)
|
||||||
|
mstore(0x20, spender)
|
||||||
|
offset := keccak256(0, 0x40)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- storage access ---------- */
|
||||||
|
function owner() -> o {
|
||||||
|
o := sload(ownerPos())
|
||||||
|
}
|
||||||
|
function totalSupply() -> supply {
|
||||||
|
supply := sload(totalSupplyPos())
|
||||||
|
}
|
||||||
|
function mintTokens(amount) {
|
||||||
|
sstore(totalSupplyPos(), safeAdd(totalSupply(), amount))
|
||||||
|
}
|
||||||
|
function balanceOf(account) -> bal {
|
||||||
|
bal := sload(accountToStorageOffset(account))
|
||||||
|
}
|
||||||
|
function addToBalance(account, amount) {
|
||||||
|
let offset := accountToStorageOffset(account)
|
||||||
|
sstore(offset, safeAdd(sload(offset), amount))
|
||||||
|
}
|
||||||
|
function deductFromBalance(account, amount) {
|
||||||
|
let offset := accountToStorageOffset(account)
|
||||||
|
let bal := sload(offset)
|
||||||
|
require(lte(amount, bal))
|
||||||
|
sstore(offset, sub(bal, amount))
|
||||||
|
}
|
||||||
|
function allowance(account, spender) -> amount {
|
||||||
|
amount := sload(allowanceStorageOffset(account, spender))
|
||||||
|
}
|
||||||
|
function setAllowance(account, spender, amount) {
|
||||||
|
sstore(allowanceStorageOffset(account, spender), amount)
|
||||||
|
}
|
||||||
|
function decreaseAllowanceBy(account, spender, amount) {
|
||||||
|
let offset := allowanceStorageOffset(account, spender)
|
||||||
|
let currentAllowance := sload(offset)
|
||||||
|
require(lte(amount, currentAllowance))
|
||||||
|
sstore(offset, sub(currentAllowance, amount))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- utility functions ---------- */
|
||||||
|
function lte(a, b) -> r {
|
||||||
|
r := iszero(gt(a, b))
|
||||||
|
}
|
||||||
|
function gte(a, b) -> r {
|
||||||
|
r := iszero(lt(a, b))
|
||||||
|
}
|
||||||
|
function safeAdd(a, b) -> r {
|
||||||
|
r := add(a, b)
|
||||||
|
if or(lt(r, a), lt(r, b)) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
function calledByOwner() -> cbo {
|
||||||
|
cbo := eq(owner(), caller())
|
||||||
|
}
|
||||||
|
function revertIfZeroAddress(addr) {
|
||||||
|
require(addr)
|
||||||
|
}
|
||||||
|
function require(condition) {
|
||||||
|
if iszero(condition) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,9 +26,6 @@
|
|||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||||
#include <libevmasm/SimplificationRules.h>
|
#include <libevmasm/SimplificationRules.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -29,9 +29,6 @@
|
|||||||
#include <libevmasm/RuleList.h>
|
#include <libevmasm/RuleList.h>
|
||||||
#include <libsolutil/Assertions.h>
|
#include <libsolutil/Assertions.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <boost/noncopyable.hpp>
|
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ using namespace std;
|
|||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
|
ErrorId solidity::langutil::operator"" _error(unsigned long long _error) { return ErrorId{ _error }; }
|
||||||
|
|
||||||
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
|
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
|
||||||
{
|
{
|
||||||
if (&_errorReporter == this)
|
if (&_errorReporter == this)
|
||||||
@ -36,30 +38,31 @@ ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ErrorReporter::warning(ErrorId _error, string const& _description)
|
||||||
void ErrorReporter::warning(string const& _description)
|
|
||||||
{
|
{
|
||||||
error(Error::Type::Warning, SourceLocation(), _description);
|
error(_error, Error::Type::Warning, SourceLocation(), _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::warning(
|
void ErrorReporter::warning(
|
||||||
|
ErrorId _error,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
string const& _description
|
string const& _description
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
error(Error::Type::Warning, _location, _description);
|
error(_error, Error::Type::Warning, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::warning(
|
void ErrorReporter::warning(
|
||||||
|
ErrorId _error,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
string const& _description,
|
string const& _description,
|
||||||
SecondarySourceLocation const& _secondaryLocation
|
SecondarySourceLocation const& _secondaryLocation
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
error(Error::Type::Warning, _location, _secondaryLocation, _description);
|
error(_error, Error::Type::Warning, _location, _secondaryLocation, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
|
void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
if (checkForExcessiveErrors(_type))
|
if (checkForExcessiveErrors(_type))
|
||||||
return;
|
return;
|
||||||
@ -72,7 +75,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st
|
|||||||
m_errorList.push_back(err);
|
m_errorList.push_back(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||||
{
|
{
|
||||||
if (checkForExcessiveErrors(_type))
|
if (checkForExcessiveErrors(_type))
|
||||||
return;
|
return;
|
||||||
@ -123,15 +126,15 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||||
{
|
{
|
||||||
error(_type, _location, _secondaryLocation, _description);
|
error(_error, _type, _location, _secondaryLocation, _description);
|
||||||
BOOST_THROW_EXCEPTION(FatalError());
|
BOOST_THROW_EXCEPTION(FatalError());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description)
|
void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
error(_type, _location, _description);
|
error(_error, _type, _location, _description);
|
||||||
BOOST_THROW_EXCEPTION(FatalError());
|
BOOST_THROW_EXCEPTION(FatalError());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,9 +148,10 @@ void ErrorReporter::clear()
|
|||||||
m_errorList.clear();
|
m_errorList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::DeclarationError,
|
Error::Type::DeclarationError,
|
||||||
_location,
|
_location,
|
||||||
_secondaryLocation,
|
_secondaryLocation,
|
||||||
@ -155,53 +159,59 @@ void ErrorReporter::declarationError(SourceLocation const& _location, SecondaryS
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::declarationError(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::DeclarationError,
|
Error::Type::DeclarationError,
|
||||||
_location,
|
_location,
|
||||||
_description
|
_description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description)
|
void ErrorReporter::fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description)
|
||||||
{
|
{
|
||||||
fatalError(
|
fatalError(
|
||||||
|
_error,
|
||||||
Error::Type::DeclarationError,
|
Error::Type::DeclarationError,
|
||||||
_location,
|
_location,
|
||||||
_description);
|
_description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::parserError(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::ParserError,
|
Error::Type::ParserError,
|
||||||
_location,
|
_location,
|
||||||
_description
|
_description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
fatalError(
|
fatalError(
|
||||||
|
_error,
|
||||||
Error::Type::ParserError,
|
Error::Type::ParserError,
|
||||||
_location,
|
_location,
|
||||||
_description
|
_description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::syntaxError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::SyntaxError,
|
Error::Type::SyntaxError,
|
||||||
_location,
|
_location,
|
||||||
_description
|
_description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::TypeError,
|
Error::Type::TypeError,
|
||||||
_location,
|
_location,
|
||||||
_secondaryLocation,
|
_secondaryLocation,
|
||||||
@ -209,18 +219,21 @@ void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::typeError(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::TypeError,
|
Error::Type::TypeError,
|
||||||
_location,
|
_location,
|
||||||
_description
|
_description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
|
||||||
|
void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
|
||||||
{
|
{
|
||||||
fatalError(
|
fatalError(
|
||||||
|
_error,
|
||||||
Error::Type::TypeError,
|
Error::Type::TypeError,
|
||||||
_location,
|
_location,
|
||||||
_secondaryLocation,
|
_secondaryLocation,
|
||||||
@ -228,26 +241,30 @@ void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySou
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::fatalTypeError(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
fatalError(Error::Type::TypeError,
|
fatalError(
|
||||||
|
_error,
|
||||||
|
Error::Type::TypeError,
|
||||||
_location,
|
_location,
|
||||||
_description
|
_description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::docstringParsingError(string const& _description)
|
void ErrorReporter::docstringParsingError(ErrorId _error, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::DocstringParsingError,
|
Error::Type::DocstringParsingError,
|
||||||
SourceLocation(),
|
SourceLocation(),
|
||||||
_description
|
_description
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorReporter::docstringParsingError(SourceLocation const& _location, string const& _description)
|
void ErrorReporter::docstringParsingError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
error(
|
error(
|
||||||
|
_error,
|
||||||
Error::Type::DocstringParsingError,
|
Error::Type::DocstringParsingError,
|
||||||
_location,
|
_location,
|
||||||
_description
|
_description
|
||||||
|
@ -33,6 +33,17 @@
|
|||||||
namespace solidity::langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifiers are used to tag and track individual error cases.
|
||||||
|
* They are passed as the first parameter of error reporting functions.
|
||||||
|
* Suffix _error helps to find them in the sources.
|
||||||
|
* The struct ErrorId prevents incidental calls like typeError(3141) instead of typeError(3141_error).
|
||||||
|
* To create a new ID, one can add 0000_error and then run "python ./scripts/correct_error_ids.py"
|
||||||
|
* from the root of the repo.
|
||||||
|
*/
|
||||||
|
struct ErrorId { unsigned long long error = 0; };
|
||||||
|
ErrorId operator"" _error(unsigned long long error);
|
||||||
|
|
||||||
class ErrorReporter
|
class ErrorReporter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -50,64 +61,68 @@ public:
|
|||||||
m_errorList += _errorList;
|
m_errorList += _errorList;
|
||||||
}
|
}
|
||||||
|
|
||||||
void warning(std::string const& _description);
|
void warning(ErrorId _error, std::string const& _description);
|
||||||
|
|
||||||
void warning(SourceLocation const& _location, std::string const& _description);
|
void warning(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
void warning(
|
void warning(
|
||||||
|
ErrorId _error,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
std::string const& _description,
|
std::string const& _description,
|
||||||
SecondarySourceLocation const& _secondaryLocation
|
SecondarySourceLocation const& _secondaryLocation
|
||||||
);
|
);
|
||||||
|
|
||||||
void error(
|
void error(
|
||||||
|
ErrorId _error,
|
||||||
Error::Type _type,
|
Error::Type _type,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
std::string const& _description
|
std::string const& _description
|
||||||
);
|
);
|
||||||
|
|
||||||
void declarationError(
|
void declarationError(
|
||||||
|
ErrorId _error,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
SecondarySourceLocation const& _secondaryLocation,
|
SecondarySourceLocation const& _secondaryLocation,
|
||||||
std::string const& _description
|
std::string const& _description
|
||||||
);
|
);
|
||||||
|
|
||||||
void declarationError(SourceLocation const& _location, std::string const& _description);
|
void declarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
|
void fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
void parserError(SourceLocation const& _location, std::string const& _description);
|
void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
void fatalParserError(SourceLocation const& _location, std::string const& _description);
|
void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
void syntaxError(SourceLocation const& _location, std::string const& _description);
|
void syntaxError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
void typeError(
|
void typeError(
|
||||||
|
ErrorId _error,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
|
SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
|
||||||
std::string const& _description = std::string()
|
std::string const& _description = std::string()
|
||||||
);
|
);
|
||||||
|
|
||||||
void typeError(SourceLocation const& _location, std::string const& _description);
|
void typeError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
template <typename... Strings>
|
template <typename... Strings>
|
||||||
void typeErrorConcatenateDescriptions(SourceLocation const& _location, Strings const&... _descriptions)
|
void typeErrorConcatenateDescriptions(ErrorId _error, SourceLocation const& _location, Strings const&... _descriptions)
|
||||||
{
|
{
|
||||||
std::initializer_list<std::string> const descs = {_descriptions...};
|
std::initializer_list<std::string> const descs = { _descriptions... };
|
||||||
solAssert(descs.size() > 0, "Need error descriptions!");
|
solAssert(descs.size() > 0, "Need error descriptions!");
|
||||||
|
|
||||||
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
|
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
|
||||||
|
|
||||||
std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " ");
|
std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " ");
|
||||||
|
|
||||||
error(Error::Type::TypeError, _location, errorStr);
|
error(_error, Error::Type::TypeError, _location, errorStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fatalTypeError(SourceLocation const& _location, std::string const& _description);
|
void fatalTypeError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
void fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description);
|
void fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description);
|
||||||
|
|
||||||
void docstringParsingError(std::string const& _description);
|
void docstringParsingError(ErrorId _error, std::string const& _description);
|
||||||
void docstringParsingError(SourceLocation const& _location, std::string const& _description);
|
void docstringParsingError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
ErrorList const& errors() const;
|
ErrorList const& errors() const;
|
||||||
|
|
||||||
@ -124,18 +139,21 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void error(
|
void error(
|
||||||
|
ErrorId _error,
|
||||||
Error::Type _type,
|
Error::Type _type,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
SecondarySourceLocation const& _secondaryLocation,
|
SecondarySourceLocation const& _secondaryLocation,
|
||||||
std::string const& _description = std::string());
|
std::string const& _description = std::string());
|
||||||
|
|
||||||
void fatalError(
|
void fatalError(
|
||||||
|
ErrorId _error,
|
||||||
Error::Type _type,
|
Error::Type _type,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
SecondarySourceLocation const& _secondaryLocation,
|
SecondarySourceLocation const& _secondaryLocation,
|
||||||
std::string const& _description = std::string());
|
std::string const& _description = std::string());
|
||||||
|
|
||||||
void fatalError(
|
void fatalError(
|
||||||
|
ErrorId _error,
|
||||||
Error::Type _type,
|
Error::Type _type,
|
||||||
SourceLocation const& _location = SourceLocation(),
|
SourceLocation const& _location = SourceLocation(),
|
||||||
std::string const& _description = std::string());
|
std::string const& _description = std::string());
|
||||||
|
@ -77,9 +77,9 @@ void ParserBase::expectToken(Token _value, bool _advance)
|
|||||||
{
|
{
|
||||||
string const expectedToken = ParserBase::tokenName(_value);
|
string const expectedToken = ParserBase::tokenName(_value);
|
||||||
if (m_parserErrorRecovery)
|
if (m_parserErrorRecovery)
|
||||||
parserError("Expected " + expectedToken + " but got " + tokenName(tok));
|
parserError(6635_error, "Expected " + expectedToken + " but got " + tokenName(tok));
|
||||||
else
|
else
|
||||||
fatalParserError("Expected " + expectedToken + " but got " + tokenName(tok));
|
fatalParserError(2314_error, "Expected " + expectedToken + " but got " + tokenName(tok));
|
||||||
// Do not advance so that recovery can sync or make use of the current token.
|
// Do not advance so that recovery can sync or make use of the current token.
|
||||||
// This is especially useful if the expected token
|
// This is especially useful if the expected token
|
||||||
// is the only one that is missing and is at the end of a construct.
|
// is the only one that is missing and is at the end of a construct.
|
||||||
@ -108,21 +108,21 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN
|
|||||||
// rollback to where the token started, and raise exception to be caught at a higher level.
|
// rollback to where the token started, and raise exception to be caught at a higher level.
|
||||||
m_scanner->setPosition(startPosition);
|
m_scanner->setPosition(startPosition);
|
||||||
m_inParserRecovery = true;
|
m_inParserRecovery = true;
|
||||||
fatalParserError(errorLoc, msg);
|
fatalParserError(1957_error, errorLoc, msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_inParserRecovery)
|
if (m_inParserRecovery)
|
||||||
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
||||||
else
|
else
|
||||||
parserError(errorLoc, msg + "Recovered at next " + expectedToken);
|
parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken);
|
||||||
m_inParserRecovery = false;
|
m_inParserRecovery = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_inParserRecovery)
|
else if (m_inParserRecovery)
|
||||||
{
|
{
|
||||||
string expectedToken = ParserBase::tokenName(_value);
|
string expectedToken = ParserBase::tokenName(_value);
|
||||||
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
|
||||||
m_inParserRecovery = false;
|
m_inParserRecovery = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ void ParserBase::increaseRecursionDepth()
|
|||||||
{
|
{
|
||||||
m_recursionDepth++;
|
m_recursionDepth++;
|
||||||
if (m_recursionDepth >= 1200)
|
if (m_recursionDepth >= 1200)
|
||||||
fatalParserError("Maximum recursion depth reached during parsing.");
|
fatalParserError(7319_error, "Maximum recursion depth reached during parsing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::decreaseRecursionDepth()
|
void ParserBase::decreaseRecursionDepth()
|
||||||
@ -143,27 +143,27 @@ void ParserBase::decreaseRecursionDepth()
|
|||||||
m_recursionDepth--;
|
m_recursionDepth--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::parserWarning(string const& _description)
|
void ParserBase::parserWarning(ErrorId _error, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(currentLocation(), _description);
|
m_errorReporter.warning(_error, currentLocation(), _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::parserError(SourceLocation const& _location, string const& _description)
|
void ParserBase::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.parserError(_location, _description);
|
m_errorReporter.parserError(_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::parserError(string const& _description)
|
void ParserBase::parserError(ErrorId _error, string const& _description)
|
||||||
{
|
{
|
||||||
parserError(currentLocation(), _description);
|
parserError(_error, currentLocation(), _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::fatalParserError(string const& _description)
|
void ParserBase::fatalParserError(ErrorId _error, string const& _description)
|
||||||
{
|
{
|
||||||
fatalParserError(currentLocation(), _description);
|
fatalParserError(_error, currentLocation(), _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description)
|
void ParserBase::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.fatalParserError(_location, _description);
|
m_errorReporter.fatalParserError(_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ namespace solidity::langutil
|
|||||||
|
|
||||||
class ErrorReporter;
|
class ErrorReporter;
|
||||||
class Scanner;
|
class Scanner;
|
||||||
|
struct ErrorId;
|
||||||
|
|
||||||
class ParserBase
|
class ParserBase
|
||||||
{
|
{
|
||||||
@ -88,17 +89,17 @@ protected:
|
|||||||
|
|
||||||
/// Creates a @ref ParserError and annotates it with the current position and the
|
/// Creates a @ref ParserError and annotates it with the current position and the
|
||||||
/// given @a _description.
|
/// given @a _description.
|
||||||
void parserError(std::string const& _description);
|
void parserError(ErrorId _error, std::string const& _description);
|
||||||
void parserError(SourceLocation const& _location, std::string const& _description);
|
void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
/// Creates a @ref ParserWarning and annotates it with the current position and the
|
/// Creates a @ref ParserWarning and annotates it with the current position and the
|
||||||
/// given @a _description.
|
/// given @a _description.
|
||||||
void parserWarning(std::string const& _description);
|
void parserWarning(ErrorId _error, std::string const& _description);
|
||||||
|
|
||||||
/// Creates a @ref ParserError and annotates it with the current position and the
|
/// Creates a @ref ParserError and annotates it with the current position and the
|
||||||
/// given @a _description. Throws the FatalError.
|
/// given @a _description. Throws the FatalError.
|
||||||
void fatalParserError(std::string const& _description);
|
void fatalParserError(ErrorId _error, std::string const& _description);
|
||||||
void fatalParserError(SourceLocation const& _location, std::string const& _description);
|
void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
std::shared_ptr<Scanner> m_scanner;
|
std::shared_ptr<Scanner> m_scanner;
|
||||||
/// The reference to the list of errors and warning to add errors/warnings during parsing
|
/// The reference to the list of errors and warning to add errors/warnings during parsing
|
||||||
|
@ -166,7 +166,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
|
||||||
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::filesystem Boost::system)
|
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost)
|
||||||
|
|
||||||
if (${Z3_FOUND})
|
if (${Z3_FOUND})
|
||||||
target_link_libraries(solidity PUBLIC z3::libz3)
|
target_link_libraries(solidity PUBLIC z3::libz3)
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::frontend;
|
using namespace solidity::frontend;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
|
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
|
||||||
{
|
{
|
||||||
@ -46,6 +47,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
|||||||
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
||||||
if (!commonType)
|
if (!commonType)
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
|
6020_error,
|
||||||
_operation.location(),
|
_operation.location(),
|
||||||
"Operator " +
|
"Operator " +
|
||||||
string(TokenTraits::toString(_operation.getOperator())) +
|
string(TokenTraits::toString(_operation.getOperator())) +
|
||||||
@ -82,7 +84,7 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier)
|
|||||||
else if (!m_types->count(value.get()))
|
else if (!m_types->count(value.get()))
|
||||||
{
|
{
|
||||||
if (m_depth > 32)
|
if (m_depth > 32)
|
||||||
m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
|
m_errorReporter.fatalTypeError(5210_error, _identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
|
||||||
ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
|
ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include <libsolidity/analysis/TypeChecker.h>
|
#include <libsolidity/analysis/TypeChecker.h>
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -78,6 +77,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
|||||||
{
|
{
|
||||||
if (constructor)
|
if (constructor)
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
7997_error,
|
||||||
function->location(),
|
function->location(),
|
||||||
SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
|
SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
|
||||||
"More than one constructor defined."
|
"More than one constructor defined."
|
||||||
@ -88,6 +88,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
|||||||
{
|
{
|
||||||
if (fallback)
|
if (fallback)
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
7301_error,
|
||||||
function->location(),
|
function->location(),
|
||||||
SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
|
SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
|
||||||
"Only one fallback function is allowed."
|
"Only one fallback function is allowed."
|
||||||
@ -98,6 +99,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
|||||||
{
|
{
|
||||||
if (receive)
|
if (receive)
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
4046_error,
|
||||||
function->location(),
|
function->location(),
|
||||||
SecondarySourceLocation().append("Another declaration is here:", receive->location()),
|
SecondarySourceLocation().append("Another declaration is here:", receive->location()),
|
||||||
"Only one receive function is allowed."
|
"Only one receive function is allowed."
|
||||||
@ -110,7 +112,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
|||||||
functions[function->name()].push_back(function);
|
functions[function->name()].push_back(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
|
findDuplicateDefinitions(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
|
void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
|
||||||
@ -121,11 +123,11 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr
|
|||||||
for (EventDefinition const* event: _contract.events())
|
for (EventDefinition const* event: _contract.events())
|
||||||
events[event->name()].push_back(event);
|
events[event->name()].push_back(event);
|
||||||
|
|
||||||
findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
|
findDuplicateDefinitions(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
|
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions)
|
||||||
{
|
{
|
||||||
for (auto const& it: _definitions)
|
for (auto const& it: _definitions)
|
||||||
{
|
{
|
||||||
@ -144,12 +146,27 @@ void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const
|
|||||||
|
|
||||||
if (ssl.infos.size() > 0)
|
if (ssl.infos.size() > 0)
|
||||||
{
|
{
|
||||||
ssl.limitSize(_message);
|
ErrorId error;
|
||||||
|
string message;
|
||||||
|
if constexpr (is_same_v<T, FunctionDefinition const*>)
|
||||||
|
{
|
||||||
|
error = 1686_error;
|
||||||
|
message = "Function with same name and arguments defined twice.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\"");
|
||||||
|
error = 5883_error;
|
||||||
|
message = "Event with same name and arguments defined twice.";
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl.limitSize(message);
|
||||||
|
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
error,
|
||||||
overloads[i]->location(),
|
overloads[i]->location(),
|
||||||
ssl,
|
ssl,
|
||||||
_message
|
message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -197,9 +214,9 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
|
|||||||
if (_contract.abstract())
|
if (_contract.abstract())
|
||||||
{
|
{
|
||||||
if (_contract.contractKind() == ContractKind::Interface)
|
if (_contract.contractKind() == ContractKind::Interface)
|
||||||
m_errorReporter.typeError(_contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
|
m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
|
||||||
else if (_contract.contractKind() == ContractKind::Library)
|
else if (_contract.contractKind() == ContractKind::Library)
|
||||||
m_errorReporter.typeError(_contract.location(), "Libraries cannot be abstract.");
|
m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract.");
|
||||||
else
|
else
|
||||||
solAssert(_contract.contractKind() == ContractKind::Contract, "");
|
solAssert(_contract.contractKind() == ContractKind::Contract, "");
|
||||||
}
|
}
|
||||||
@ -215,10 +232,12 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
|
|||||||
SecondarySourceLocation ssl;
|
SecondarySourceLocation ssl;
|
||||||
for (auto declaration: _contract.annotation().unimplementedDeclarations)
|
for (auto declaration: _contract.annotation().unimplementedDeclarations)
|
||||||
ssl.append("Missing implementation: ", declaration->location());
|
ssl.append("Missing implementation: ", declaration->location());
|
||||||
m_errorReporter.typeError(_contract.location(), ssl,
|
m_errorReporter.typeError(
|
||||||
"Contract \"" + _contract.annotation().canonicalName
|
3656_error,
|
||||||
+ "\" should be marked as abstract.");
|
_contract.location(),
|
||||||
|
ssl,
|
||||||
|
"Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,6 +262,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
1563_error,
|
||||||
modifier->location(),
|
modifier->location(),
|
||||||
"Modifier-style base constructor call without arguments."
|
"Modifier-style base constructor call without arguments."
|
||||||
);
|
);
|
||||||
@ -304,6 +324,7 @@ void ContractLevelChecker::annotateBaseConstructorArguments(
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
3364_error,
|
||||||
*mainLocation,
|
*mainLocation,
|
||||||
ssl,
|
ssl,
|
||||||
"Base constructor arguments given twice."
|
"Base constructor arguments given twice."
|
||||||
@ -343,6 +364,7 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
|
|||||||
for (size_t j = i + 1; j < it.second.size(); ++j)
|
for (size_t j = i + 1; j < it.second.size(); ++j)
|
||||||
if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
|
if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
9914_error,
|
||||||
it.second[j].first->location(),
|
it.second[j].first->location(),
|
||||||
"Function overload clash during conversion to external types for arguments."
|
"Function overload clash during conversion to external types for arguments."
|
||||||
);
|
);
|
||||||
@ -356,6 +378,7 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
|
|||||||
util::FixedHash<4> const& hash = it.first;
|
util::FixedHash<4> const& hash = it.first;
|
||||||
if (hashes.count(hash))
|
if (hashes.count(hash))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
1860_error,
|
||||||
_contract.location(),
|
_contract.location(),
|
||||||
string("Function signature hash collision for ") + it.second->externalSignature()
|
string("Function signature hash collision for ") + it.second->externalSignature()
|
||||||
);
|
);
|
||||||
@ -369,11 +392,11 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_contract.baseContracts().empty())
|
if (!_contract.baseContracts().empty())
|
||||||
m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
|
m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit.");
|
||||||
|
|
||||||
for (auto const& var: _contract.stateVariables())
|
for (auto const& var: _contract.stateVariables())
|
||||||
if (!var->isConstant())
|
if (!var->isConstant())
|
||||||
m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
|
m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
|
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
|
||||||
@ -412,6 +435,7 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
|
|||||||
|
|
||||||
if (!errors.infos.empty())
|
if (!errors.infos.empty())
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
|
6594_error,
|
||||||
_contract.location(),
|
_contract.location(),
|
||||||
errors,
|
errors,
|
||||||
std::string("Contract \"") +
|
std::string("Contract \"") +
|
||||||
@ -428,6 +452,7 @@ void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition
|
|||||||
if (auto const* fallback = _contract.fallbackFunction())
|
if (auto const* fallback = _contract.fallbackFunction())
|
||||||
if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction())
|
if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction())
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
3628_error,
|
||||||
_contract.location(),
|
_contract.location(),
|
||||||
"This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.",
|
"This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.",
|
||||||
SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location())
|
SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location())
|
||||||
|
@ -60,7 +60,7 @@ private:
|
|||||||
void checkDuplicateFunctions(ContractDefinition const& _contract);
|
void checkDuplicateFunctions(ContractDefinition const& _contract);
|
||||||
void checkDuplicateEvents(ContractDefinition const& _contract);
|
void checkDuplicateEvents(ContractDefinition const& _contract);
|
||||||
template <class T>
|
template <class T>
|
||||||
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
|
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions);
|
||||||
/// Checks for unimplemented functions and modifiers.
|
/// Checks for unimplemented functions and modifiers.
|
||||||
void checkAbstractDefinitions(ContractDefinition const& _contract);
|
void checkAbstractDefinitions(ContractDefinition const& _contract);
|
||||||
/// Checks that the base constructor arguments are properly provided.
|
/// Checks that the base constructor arguments are properly provided.
|
||||||
|
@ -136,6 +136,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
|
|||||||
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
|
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
|
||||||
|
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
3464_error,
|
||||||
variableOccurrence->occurrence() ?
|
variableOccurrence->occurrence() ?
|
||||||
*variableOccurrence->occurrence() :
|
*variableOccurrence->occurrence() :
|
||||||
variableOccurrence->declaration().location(),
|
variableOccurrence->declaration().location(),
|
||||||
@ -176,6 +177,6 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const*
|
|||||||
// Extend the location, as long as the next location overlaps (unreachable is sorted).
|
// Extend the location, as long as the next location overlaps (unreachable is sorted).
|
||||||
for (; it != unreachable.end() && it->start <= location.end; ++it)
|
for (; it != unreachable.end() && it->start <= location.end; ++it)
|
||||||
location.end = std::max(location.end, it->end);
|
location.end = std::max(location.end, it->end);
|
||||||
m_errorReporter.warning(location, "Unreachable code.");
|
m_errorReporter.warning(5740_error, location, "Unreachable code.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#include <libsolidity/analysis/ControlFlowGraph.h>
|
#include <libsolidity/analysis/ControlFlowGraph.h>
|
||||||
|
|
||||||
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -188,12 +188,14 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
|
|||||||
{
|
{
|
||||||
if (contractType->contractDefinition().isLibrary())
|
if (contractType->contractDefinition().isLibrary())
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
|
1665_error,
|
||||||
typeName->location(),
|
typeName->location(),
|
||||||
"Library types cannot be used as mapping keys."
|
"Library types cannot be used as mapping keys."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (typeName->annotation().type->category() != Type::Category::Enum)
|
else if (typeName->annotation().type->category() != Type::Category::Enum)
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
|
7804_error,
|
||||||
typeName->location(),
|
typeName->location(),
|
||||||
"Only elementary types, contract types or enums are allowed as mapping keys."
|
"Only elementary types, contract types or enums are allowed as mapping keys."
|
||||||
);
|
);
|
||||||
@ -253,9 +255,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
if (_variable.isConstant() && !_variable.isStateVariable())
|
||||||
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
m_errorReporter.declarationError(1788_error, _variable.location(), "The \"constant\" keyword can only be used for state variables.");
|
||||||
if (_variable.immutable() && !_variable.isStateVariable())
|
if (_variable.immutable() && !_variable.isStateVariable())
|
||||||
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
m_errorReporter.declarationError(8297_error, _variable.location(), "The \"immutable\" keyword can only be used for state variables.");
|
||||||
|
|
||||||
if (!_variable.typeName())
|
if (!_variable.typeName())
|
||||||
{
|
{
|
||||||
@ -360,19 +362,19 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
|
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorOccurred = true;
|
||||||
m_errorReporter.typeError(_location, _description);
|
m_errorReporter.typeError(2311_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorOccurred = true;
|
||||||
m_errorReporter.fatalTypeError(_location, _description);
|
m_errorReporter.fatalTypeError(5651_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorOccurred = true;
|
||||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
m_errorReporter.fatalDeclarationError(2046_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
||||||
|
@ -173,5 +173,5 @@ void DocStringAnalyser::parseDocStrings(
|
|||||||
void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
|
void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccured = true;
|
m_errorOccured = true;
|
||||||
m_errorReporter.docstringParsingError(_location, _description);
|
m_errorReporter.docstringParsingError(7816_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
@ -100,11 +100,13 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
|
|||||||
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
|
||||||
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
|
||||||
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
|
||||||
|
// Accepts a MagicType that can be any contract type or an Integer type and returns a
|
||||||
|
// MagicType. The TypeChecker handles the correctness of the input and output types.
|
||||||
magicVarDecl("type", TypeProvider::function(
|
magicVarDecl("type", TypeProvider::function(
|
||||||
strings{"address"} /* accepts any contract type, handled by the type checker */,
|
strings{},
|
||||||
strings{} /* returns a MagicType, handled by the type checker */,
|
strings{},
|
||||||
FunctionType::Kind::MetaType,
|
FunctionType::Kind::MetaType,
|
||||||
false,
|
true,
|
||||||
StateMutability::Pure
|
StateMutability::Pure
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
@ -123,7 +125,7 @@ vector<Declaration const*> GlobalContext::declarations() const
|
|||||||
{
|
{
|
||||||
vector<Declaration const*> declarations;
|
vector<Declaration const*> declarations;
|
||||||
declarations.reserve(m_magicVariables.size());
|
declarations.reserve(m_magicVariables.size());
|
||||||
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
|
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
|
||||||
declarations.push_back(variable.get());
|
declarations.push_back(variable.get());
|
||||||
return declarations;
|
return declarations;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
using namespace solidity::frontend;
|
using namespace solidity::frontend;
|
||||||
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
void ImmutableValidator::analyze()
|
void ImmutableValidator::analyze()
|
||||||
{
|
{
|
||||||
@ -42,7 +43,7 @@ void ImmutableValidator::analyze()
|
|||||||
visitCallableIfNew(*contract->constructor());
|
visitCallableIfNew(*contract->constructor());
|
||||||
|
|
||||||
for (ContractDefinition const* contract: linearizedContracts)
|
for (ContractDefinition const* contract: linearizedContracts)
|
||||||
for (std::shared_ptr<InheritanceSpecifier> const inheritSpec: contract->baseContracts())
|
for (std::shared_ptr<InheritanceSpecifier> const& inheritSpec: contract->baseContracts())
|
||||||
if (auto args = inheritSpec->arguments())
|
if (auto args = inheritSpec->arguments())
|
||||||
ASTNode::listAccept(*args, *this);
|
ASTNode::listAccept(*args, *this);
|
||||||
|
|
||||||
@ -165,33 +166,39 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
|
|||||||
{
|
{
|
||||||
if (!m_currentConstructor)
|
if (!m_currentConstructor)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
1581_error,
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Immutable variables can only be initialized inline or assigned directly in the constructor."
|
"Immutable variables can only be initialized inline or assigned directly in the constructor."
|
||||||
);
|
);
|
||||||
else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id())
|
else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id())
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
7484_error,
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Immutable variables must be initialized in the constructor of the contract they are defined in."
|
"Immutable variables must be initialized in the constructor of the contract they are defined in."
|
||||||
);
|
);
|
||||||
else if (m_inLoop)
|
else if (m_inLoop)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
6672_error,
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Immutable variables can only be initialized once, not in a while statement."
|
"Immutable variables can only be initialized once, not in a while statement."
|
||||||
);
|
);
|
||||||
else if (m_inBranch)
|
else if (m_inBranch)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
4599_error,
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Immutable variables must be initialized unconditionally, not in an if statement."
|
"Immutable variables must be initialized unconditionally, not in an if statement."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!m_initializedStateVariables.emplace(&_variableReference).second)
|
if (!m_initializedStateVariables.emplace(&_variableReference).second)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
1574_error,
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Immutable state variable already initialized."
|
"Immutable state variable already initialized."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (m_inConstructionContext)
|
else if (m_inConstructionContext)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
7733_error,
|
||||||
_expression.location(),
|
_expression.location(),
|
||||||
"Immutable variables cannot be read during contract creation time, which means "
|
"Immutable variables cannot be read during contract creation time, which means "
|
||||||
"they cannot be read in the constructor or any function or modifier called from it."
|
"they cannot be read in the constructor or any function or modifier called from it."
|
||||||
@ -205,6 +212,7 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
|
|||||||
if (varDecl->immutable())
|
if (varDecl->immutable())
|
||||||
if (!util::contains(m_initializedStateVariables, varDecl))
|
if (!util::contains(m_initializedStateVariables, varDecl))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
2658_error,
|
||||||
_location,
|
_location,
|
||||||
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
|
||||||
"Construction control flow ends without initializing all immutable state variables."
|
"Construction control flow ends without initializing all immutable state variables."
|
||||||
|
@ -80,6 +80,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
|||||||
if (!_sourceUnits.count(path))
|
if (!_sourceUnits.count(path))
|
||||||
{
|
{
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
5073_error,
|
||||||
imp->location(),
|
imp->location(),
|
||||||
"Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
|
"Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
|
||||||
);
|
);
|
||||||
@ -95,6 +96,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
|
|||||||
if (declarations.empty())
|
if (declarations.empty())
|
||||||
{
|
{
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
2904_error,
|
||||||
imp->location(),
|
imp->location(),
|
||||||
"Declaration \"" +
|
"Declaration \"" +
|
||||||
alias.symbol->name() +
|
alias.symbol->name() +
|
||||||
@ -208,6 +210,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
|||||||
// Don't warn the user for what the user did not.
|
// Don't warn the user for what the user did not.
|
||||||
continue;
|
continue;
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
8261_error,
|
||||||
declaration->location(),
|
declaration->location(),
|
||||||
"Variable is shadowed in inline assembly by an instruction of the same name"
|
"Variable is shadowed in inline assembly by an instruction of the same name"
|
||||||
);
|
);
|
||||||
@ -326,6 +329,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
9097_error,
|
||||||
secondDeclarationLocation,
|
secondDeclarationLocation,
|
||||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
||||||
"Identifier already declared."
|
"Identifier already declared."
|
||||||
@ -343,19 +347,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
|
|||||||
UserDefinedTypeName const& baseName = baseSpecifier->name();
|
UserDefinedTypeName const& baseName = baseSpecifier->name();
|
||||||
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
|
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
|
||||||
if (!base)
|
if (!base)
|
||||||
m_errorReporter.fatalTypeError(baseName.location(), "Contract expected.");
|
m_errorReporter.fatalTypeError(8758_error, baseName.location(), "Contract expected.");
|
||||||
// "push_front" has the effect that bases mentioned later can overwrite members of bases
|
// "push_front" has the effect that bases mentioned later can overwrite members of bases
|
||||||
// mentioned earlier
|
// mentioned earlier
|
||||||
input.back().push_front(base);
|
input.back().push_front(base);
|
||||||
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
|
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
|
||||||
if (basesBases.empty())
|
if (basesBases.empty())
|
||||||
m_errorReporter.fatalTypeError(baseName.location(), "Definition of base has to precede definition of derived contract");
|
m_errorReporter.fatalTypeError(2449_error, baseName.location(), "Definition of base has to precede definition of derived contract");
|
||||||
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
|
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
|
||||||
}
|
}
|
||||||
input.back().push_front(&_contract);
|
input.back().push_front(&_contract);
|
||||||
vector<ContractDefinition const*> result = cThreeMerge(input);
|
vector<ContractDefinition const*> result = cThreeMerge(input);
|
||||||
if (result.empty())
|
if (result.empty())
|
||||||
m_errorReporter.fatalTypeError(_contract.location(), "Linearization of inheritance graph impossible");
|
m_errorReporter.fatalTypeError(5005_error, _contract.location(), "Linearization of inheritance graph impossible");
|
||||||
_contract.annotation().linearizedBaseContracts = result;
|
_contract.annotation().linearizedBaseContracts = result;
|
||||||
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
|
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
|
||||||
}
|
}
|
||||||
@ -476,6 +480,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
|||||||
}
|
}
|
||||||
|
|
||||||
_errorReporter.declarationError(
|
_errorReporter.declarationError(
|
||||||
|
2333_error,
|
||||||
secondDeclarationLocation,
|
secondDeclarationLocation,
|
||||||
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
|
||||||
"Identifier already declared."
|
"Identifier already declared."
|
||||||
@ -486,6 +491,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
|||||||
{
|
{
|
||||||
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
|
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
|
||||||
_errorReporter.warning(
|
_errorReporter.warning(
|
||||||
|
2319_error,
|
||||||
*_errorLocation,
|
*_errorLocation,
|
||||||
"This declaration shadows a builtin symbol."
|
"This declaration shadows a builtin symbol."
|
||||||
);
|
);
|
||||||
@ -493,6 +499,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
|||||||
{
|
{
|
||||||
auto shadowedLocation = shadowedDeclaration->location();
|
auto shadowedLocation = shadowedDeclaration->location();
|
||||||
_errorReporter.warning(
|
_errorReporter.warning(
|
||||||
|
2519_error,
|
||||||
_declaration.location(),
|
_declaration.location(),
|
||||||
"This declaration shadows an existing declaration.",
|
"This declaration shadows an existing declaration.",
|
||||||
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
|
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
|
|
||||||
@ -461,6 +460,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
|||||||
{
|
{
|
||||||
if (contains_if(inheritedFuncs, MatchByName{modifier->name()}))
|
if (contains_if(inheritedFuncs, MatchByName{modifier->name()}))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
5631_error,
|
||||||
modifier->location(),
|
modifier->location(),
|
||||||
"Override changes function or public state variable to modifier."
|
"Override changes function or public state variable to modifier."
|
||||||
);
|
);
|
||||||
@ -474,7 +474,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (contains_if(inheritedMods, MatchByName{function->name()}))
|
if (contains_if(inheritedMods, MatchByName{function->name()}))
|
||||||
m_errorReporter.typeError(function->location(), "Override changes modifier to function.");
|
m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function.");
|
||||||
|
|
||||||
checkOverrideList(OverrideProxy{function}, inheritedFuncs);
|
checkOverrideList(OverrideProxy{function}, inheritedFuncs);
|
||||||
}
|
}
|
||||||
@ -484,7 +484,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (contains_if(inheritedMods, MatchByName{stateVar->name()}))
|
if (contains_if(inheritedMods, MatchByName{stateVar->name()}))
|
||||||
m_errorReporter.typeError(stateVar->location(), "Override changes modifier to public state variable.");
|
m_errorReporter.typeError(1456_error, stateVar->location(), "Override changes modifier to public state variable.");
|
||||||
|
|
||||||
checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs);
|
checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs);
|
||||||
}
|
}
|
||||||
@ -500,17 +500,19 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
|
|
||||||
if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType())
|
if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType())
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
1078_error,
|
||||||
_overriding.location(),
|
_overriding.location(),
|
||||||
"Override changes modifier signature."
|
"Override changes modifier signature."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_overriding.overrides())
|
if (!_overriding.overrides())
|
||||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
|
overrideError(_overriding, _super, 9456_error, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
|
||||||
|
|
||||||
if (_super.isVariable())
|
if (_super.isVariable())
|
||||||
overrideError(
|
overrideError(
|
||||||
_super,
|
_super,
|
||||||
_overriding,
|
_overriding,
|
||||||
|
1452_error,
|
||||||
"Cannot override public state variable.",
|
"Cannot override public state variable.",
|
||||||
"Overriding " + _overriding.astNodeName() + " is here:"
|
"Overriding " + _overriding.astNodeName() + " is here:"
|
||||||
);
|
);
|
||||||
@ -518,6 +520,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
overrideError(
|
overrideError(
|
||||||
_super,
|
_super,
|
||||||
_overriding,
|
_overriding,
|
||||||
|
4334_error,
|
||||||
"Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?",
|
"Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?",
|
||||||
"Overriding " + _overriding.astNodeName() + " is here:"
|
"Overriding " + _overriding.astNodeName() + " is here:"
|
||||||
);
|
);
|
||||||
@ -525,7 +528,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
if (_overriding.isVariable())
|
if (_overriding.isVariable())
|
||||||
{
|
{
|
||||||
if (_super.visibility() != Visibility::External)
|
if (_super.visibility() != Visibility::External)
|
||||||
overrideError(_overriding, _super, "Public state variables can only override functions with external visibility.");
|
overrideError(_overriding, _super, 5225_error, "Public state variables can only override functions with external visibility.");
|
||||||
solAssert(_overriding.visibility() == Visibility::External, "");
|
solAssert(_overriding.visibility() == Visibility::External, "");
|
||||||
}
|
}
|
||||||
else if (_overriding.visibility() != _super.visibility())
|
else if (_overriding.visibility() != _super.visibility())
|
||||||
@ -536,7 +539,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
_super.visibility() == Visibility::External &&
|
_super.visibility() == Visibility::External &&
|
||||||
_overriding.visibility() == Visibility::Public
|
_overriding.visibility() == Visibility::Public
|
||||||
))
|
))
|
||||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " visibility differs.");
|
overrideError(_overriding, _super, 9098_error, "Overriding " + _overriding.astNodeName() + " visibility differs.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_super.isFunction())
|
if (_super.isFunction())
|
||||||
@ -547,7 +550,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
|
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
|
||||||
|
|
||||||
if (!functionType->hasEqualReturnTypes(*superType))
|
if (!functionType->hasEqualReturnTypes(*superType))
|
||||||
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " return types differ.");
|
overrideError(_overriding, _super, 4822_error, "Overriding " + _overriding.astNodeName() + " return types differ.");
|
||||||
|
|
||||||
// This is only relevant for a function overriding a function.
|
// This is only relevant for a function overriding a function.
|
||||||
if (_overriding.isFunction())
|
if (_overriding.isFunction())
|
||||||
@ -556,6 +559,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
overrideError(
|
overrideError(
|
||||||
_overriding,
|
_overriding,
|
||||||
_super,
|
_super,
|
||||||
|
2837_error,
|
||||||
"Overriding function changes state mutability from \"" +
|
"Overriding function changes state mutability from \"" +
|
||||||
stateMutabilityToString(_super.stateMutability()) +
|
stateMutabilityToString(_super.stateMutability()) +
|
||||||
"\" to \"" +
|
"\" to \"" +
|
||||||
@ -567,6 +571,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
overrideError(
|
overrideError(
|
||||||
_overriding,
|
_overriding,
|
||||||
_super,
|
_super,
|
||||||
|
4593_error,
|
||||||
"Overriding an implemented function with an unimplemented function is not allowed."
|
"Overriding an implemented function with an unimplemented function is not allowed."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -576,6 +581,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
|||||||
void OverrideChecker::overrideListError(
|
void OverrideChecker::overrideListError(
|
||||||
OverrideProxy const& _item,
|
OverrideProxy const& _item,
|
||||||
set<ContractDefinition const*, CompareByID> _secondary,
|
set<ContractDefinition const*, CompareByID> _secondary,
|
||||||
|
ErrorId _error,
|
||||||
string const& _message1,
|
string const& _message1,
|
||||||
string const& _message2
|
string const& _message2
|
||||||
)
|
)
|
||||||
@ -593,6 +599,7 @@ void OverrideChecker::overrideListError(
|
|||||||
contractSingularPlural = "contracts ";
|
contractSingularPlural = "contracts ";
|
||||||
|
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
_error,
|
||||||
_item.overrides() ? _item.overrides()->location() : _item.location(),
|
_item.overrides() ? _item.overrides()->location() : _item.location(),
|
||||||
ssl,
|
ssl,
|
||||||
_message1 +
|
_message1 +
|
||||||
@ -603,9 +610,10 @@ void OverrideChecker::overrideListError(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, string const& _message, string const& _secondaryMsg)
|
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
_error,
|
||||||
_overriding.location(),
|
_overriding.location(),
|
||||||
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
||||||
_message
|
_message
|
||||||
@ -613,9 +621,10 @@ void OverrideChecker::overrideError(Declaration const& _overriding, Declaration
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, string const& _message, string const& _secondaryMsg)
|
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
_error,
|
||||||
_overriding.location(),
|
_overriding.location(),
|
||||||
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
|
||||||
_message
|
_message
|
||||||
@ -718,7 +727,7 @@ void OverrideChecker::checkAmbiguousOverridesInternal(set<OverrideProxy> _baseCa
|
|||||||
" Since one of the bases defines a public state variable which cannot be overridden, "
|
" Since one of the bases defines a public state variable which cannot be overridden, "
|
||||||
"you have to change the inheritance layout or the names of the functions.";
|
"you have to change the inheritance layout or the names of the functions.";
|
||||||
|
|
||||||
m_errorReporter.typeError(_location, ssl, message);
|
m_errorReporter.typeError(6480_error, _location, ssl, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
set<ContractDefinition const*, OverrideChecker::CompareByID> OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const
|
set<ContractDefinition const*, OverrideChecker::CompareByID> OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const
|
||||||
@ -766,13 +775,14 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
|||||||
SecondarySourceLocation ssl;
|
SecondarySourceLocation ssl;
|
||||||
ssl.append("First occurrence here: ", list[i-1]->location());
|
ssl.append("First occurrence here: ", list[i-1]->location());
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
4520_error,
|
||||||
list[i]->location(),
|
list[i]->location(),
|
||||||
ssl,
|
ssl,
|
||||||
"Duplicate contract \"" +
|
"Duplicate contract \"" +
|
||||||
joinHumanReadable(list[i]->namePath(), ".") +
|
joinHumanReadable(list[i]->namePath(), ".") +
|
||||||
"\" found in override list of \"" +
|
"\" found in override list of \"" +
|
||||||
_item.name() +
|
_item.name() +
|
||||||
"\"."
|
"\"."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -791,6 +801,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
|||||||
|
|
||||||
if (_item.overrides() && expectedContracts.empty())
|
if (_item.overrides() && expectedContracts.empty())
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
7792_error,
|
||||||
_item.overrides()->location(),
|
_item.overrides()->location(),
|
||||||
_item.astNodeNameCapitalized() + " has override specified but does not override anything."
|
_item.astNodeNameCapitalized() + " has override specified but does not override anything."
|
||||||
);
|
);
|
||||||
@ -804,6 +815,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
|||||||
overrideListError(
|
overrideListError(
|
||||||
_item,
|
_item,
|
||||||
missingContracts,
|
missingContracts,
|
||||||
|
4327_error,
|
||||||
_item.astNodeNameCapitalized() + " needs to specify overridden ",
|
_item.astNodeNameCapitalized() + " needs to specify overridden ",
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
@ -813,6 +825,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
|
|||||||
overrideListError(
|
overrideListError(
|
||||||
_item,
|
_item,
|
||||||
surplusContracts,
|
surplusContracts,
|
||||||
|
2353_error,
|
||||||
"Invalid ",
|
"Invalid ",
|
||||||
"specified in override list: "
|
"specified in override list: "
|
||||||
);
|
);
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
namespace solidity::langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
class ErrorReporter;
|
class ErrorReporter;
|
||||||
|
struct ErrorId;
|
||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,18 +161,21 @@ private:
|
|||||||
void overrideListError(
|
void overrideListError(
|
||||||
OverrideProxy const& _item,
|
OverrideProxy const& _item,
|
||||||
std::set<ContractDefinition const*, CompareByID> _secondary,
|
std::set<ContractDefinition const*, CompareByID> _secondary,
|
||||||
|
langutil::ErrorId _error,
|
||||||
std::string const& _message1,
|
std::string const& _message1,
|
||||||
std::string const& _message2
|
std::string const& _message2
|
||||||
);
|
);
|
||||||
void overrideError(
|
void overrideError(
|
||||||
Declaration const& _overriding,
|
Declaration const& _overriding,
|
||||||
Declaration const& _super,
|
Declaration const& _super,
|
||||||
|
langutil::ErrorId _error,
|
||||||
std::string const& _message,
|
std::string const& _message,
|
||||||
std::string const& _secondaryMsg = "Overridden function is here:"
|
std::string const& _secondaryMsg = "Overridden function is here:"
|
||||||
);
|
);
|
||||||
void overrideError(
|
void overrideError(
|
||||||
OverrideProxy const& _overriding,
|
OverrideProxy const& _overriding,
|
||||||
OverrideProxy const& _super,
|
OverrideProxy const& _super,
|
||||||
|
langutil::ErrorId _error,
|
||||||
std::string const& _message,
|
std::string const& _message,
|
||||||
std::string const& _secondaryMsg = "Overridden function is here:"
|
std::string const& _secondaryMsg = "Overridden function is here:"
|
||||||
);
|
);
|
||||||
|
@ -122,6 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
|||||||
for (auto declaration: m_constVariables)
|
for (auto declaration: m_constVariables)
|
||||||
if (auto identifier = findCycle(*declaration))
|
if (auto identifier = findCycle(*declaration))
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
6161_error,
|
||||||
declaration->location(),
|
declaration->location(),
|
||||||
"The value of the constant " + declaration->name() +
|
"The value of the constant " + declaration->name() +
|
||||||
" has a cyclic dependency via " + identifier->name() + "."
|
" has a cyclic dependency via " + identifier->name() + "."
|
||||||
@ -165,7 +166,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
|||||||
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
|
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
|
||||||
{
|
{
|
||||||
if (_depth >= 256)
|
if (_depth >= 256)
|
||||||
m_errorReporter.fatalDeclarationError(_variable.location(), "Variable definition exhausting cyclic dependency validator.");
|
m_errorReporter.fatalDeclarationError(7380_error, _variable.location(), "Variable definition exhausting cyclic dependency validator.");
|
||||||
|
|
||||||
// Iterating through the dependencies needs to be deterministic and thus cannot
|
// Iterating through the dependencies needs to be deterministic and thus cannot
|
||||||
// depend on the memory layout.
|
// depend on the memory layout.
|
||||||
@ -209,6 +210,7 @@ struct OverrideSpecifierChecker: public PostTypeChecker::Checker
|
|||||||
TypeType const* actualTypeType = dynamic_cast<TypeType const*>(decl->type());
|
TypeType const* actualTypeType = dynamic_cast<TypeType const*>(decl->type());
|
||||||
|
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
9301_error,
|
||||||
override->location(),
|
override->location(),
|
||||||
"Expected contract but got " +
|
"Expected contract but got " +
|
||||||
actualTypeType->actualType()->toString(true) +
|
actualTypeType->actualType()->toString(true) +
|
||||||
@ -243,6 +245,7 @@ struct ModifierContextChecker: public PostTypeChecker::Checker
|
|||||||
if (ModifierType const* type = dynamic_cast<decltype(type)>(_identifier.annotation().type))
|
if (ModifierType const* type = dynamic_cast<decltype(type)>(_identifier.annotation().type))
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
3112_error,
|
||||||
_identifier.location(),
|
_identifier.location(),
|
||||||
"Modifier can only be referenced in function headers."
|
"Modifier can only be referenced in function headers."
|
||||||
);
|
);
|
||||||
@ -280,6 +283,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker
|
|||||||
// Check for event outside of emit statement
|
// Check for event outside of emit statement
|
||||||
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
|
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
3132_error,
|
||||||
_functionCall.location(),
|
_functionCall.location(),
|
||||||
"Event invocations have to be prefixed by \"emit\"."
|
"Event invocations have to be prefixed by \"emit\"."
|
||||||
);
|
);
|
||||||
@ -308,7 +312,7 @@ struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker
|
|||||||
&& !_variable.isCallableOrCatchParameter()
|
&& !_variable.isCallableOrCatchParameter()
|
||||||
&& !m_insideStruct
|
&& !m_insideStruct
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
|
m_errorReporter.typeError(8274_error, _variable.location(), "Variables cannot be declared in interfaces.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -268,19 +268,19 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
|
|||||||
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
|
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorOccurred = true;
|
||||||
m_errorReporter.declarationError(_location, _description);
|
m_errorReporter.declarationError(8532_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
|
void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorOccurred = true;
|
||||||
m_errorReporter.declarationError(_location, _ssl, _description);
|
m_errorReporter.declarationError(3881_error, _location, _ssl, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorOccurred = true;
|
||||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
m_errorReporter.fatalDeclarationError(6546_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -120,13 +120,14 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
|
|||||||
{
|
{
|
||||||
if (var.first.second->isCallableOrCatchParameter())
|
if (var.first.second->isCallableOrCatchParameter())
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
5667_error,
|
||||||
var.first.second->location(),
|
var.first.second->location(),
|
||||||
"Unused " +
|
"Unused " +
|
||||||
string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") +
|
string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") +
|
||||||
" parameter. Remove or comment out the variable name to silence this warning."
|
" parameter. Remove or comment out the variable name to silence this warning."
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
|
m_errorReporter.warning(2072_error, var.first.second->location(), "Unused local variable.");
|
||||||
}
|
}
|
||||||
m_localVarUseCount.clear();
|
m_localVarUseCount.clear();
|
||||||
m_constructor = false;
|
m_constructor = false;
|
||||||
@ -159,6 +160,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
|
|||||||
set<StructDefinition const*> structsSeen;
|
set<StructDefinition const*> structsSeen;
|
||||||
if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
|
if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
3408_error,
|
||||||
_variable.location(),
|
_variable.location(),
|
||||||
"Variable covers a large part of storage and thus makes collisions likely. "
|
"Variable covers a large part of storage and thus makes collisions likely. "
|
||||||
"Either use mappings or dynamic arrays and allow their size to be increased only "
|
"Either use mappings or dynamic arrays and allow their size to be increased only "
|
||||||
@ -183,6 +185,7 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
|
|||||||
{
|
{
|
||||||
if (_statement.expression().annotation().isPure)
|
if (_statement.expression().annotation().isPure)
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
6133_error,
|
||||||
_statement.location(),
|
_statement.location(),
|
||||||
"Statement has no effect."
|
"Statement has no effect."
|
||||||
);
|
);
|
||||||
@ -196,11 +199,13 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
|
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
1400_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
|
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
|
||||||
);
|
);
|
||||||
else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash")
|
else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash")
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
8113_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
|
||||||
);
|
);
|
||||||
@ -211,6 +216,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
|||||||
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
|
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
|
||||||
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
|
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
6417_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"The constructor of the contract (or its base) uses inline assembly. "
|
"The constructor of the contract (or its base) uses inline assembly. "
|
||||||
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
|
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
|
||||||
@ -222,6 +228,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
|||||||
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
||||||
if (type->kind() == FunctionType::Kind::BareCallCode)
|
if (type->kind() == FunctionType::Kind::BareCallCode)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
2256_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
|
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
|
||||||
);
|
);
|
||||||
@ -235,6 +242,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
if (id->name() == "this")
|
if (id->name() == "this")
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
5805_error,
|
||||||
id->location(),
|
id->location(),
|
||||||
"\"this\" used in constructor. "
|
"\"this\" used in constructor. "
|
||||||
"Note that external functions of a contract "
|
"Note that external functions of a contract "
|
||||||
@ -285,6 +293,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation)
|
|||||||
))
|
))
|
||||||
if (rhs->isZero())
|
if (rhs->isZero())
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
1211_error,
|
||||||
_operation.location(),
|
_operation.location(),
|
||||||
(_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero."
|
(_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero."
|
||||||
);
|
);
|
||||||
@ -307,6 +316,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
|||||||
))
|
))
|
||||||
if (lastArg->isZero())
|
if (lastArg->isZero())
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
4195_error,
|
||||||
_functionCall.location(),
|
_functionCall.location(),
|
||||||
"Arithmetic modulo zero."
|
"Arithmetic modulo zero."
|
||||||
);
|
);
|
||||||
@ -317,6 +327,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
|
|||||||
functionType->declaration().scope() == m_currentContract
|
functionType->declaration().scope() == m_currentContract
|
||||||
)
|
)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
6700_error,
|
||||||
_functionCall.location(),
|
_functionCall.location(),
|
||||||
SecondarySourceLocation().append(
|
SecondarySourceLocation().append(
|
||||||
"The function declaration is here:",
|
"The function declaration is here:",
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/SemVerHandler.h>
|
#include <liblangutil/SemVerHandler.h>
|
||||||
|
|
||||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -69,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
|
|||||||
string(";\"");
|
string(";\"");
|
||||||
|
|
||||||
// when reporting the warning, print the source name only
|
// when reporting the warning, print the source name only
|
||||||
m_errorReporter.warning({-1, -1, _sourceUnit.location().source}, errorString);
|
m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString);
|
||||||
}
|
}
|
||||||
m_sourceUnit = nullptr;
|
m_sourceUnit = nullptr;
|
||||||
}
|
}
|
||||||
@ -79,18 +78,20 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
solAssert(!_pragma.tokens().empty(), "");
|
solAssert(!_pragma.tokens().empty(), "");
|
||||||
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
|
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
|
||||||
if (_pragma.tokens()[0] != Token::Identifier)
|
if (_pragma.tokens()[0] != Token::Identifier)
|
||||||
m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
|
m_errorReporter.syntaxError(5226_error, _pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
|
||||||
else if (_pragma.literals()[0] == "experimental")
|
else if (_pragma.literals()[0] == "experimental")
|
||||||
{
|
{
|
||||||
solAssert(m_sourceUnit, "");
|
solAssert(m_sourceUnit, "");
|
||||||
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
|
||||||
if (literals.empty())
|
if (literals.empty())
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
9679_error,
|
||||||
_pragma.location(),
|
_pragma.location(),
|
||||||
"Experimental feature name is missing."
|
"Experimental feature name is missing."
|
||||||
);
|
);
|
||||||
else if (literals.size() > 1)
|
else if (literals.size() > 1)
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
6022_error,
|
||||||
_pragma.location(),
|
_pragma.location(),
|
||||||
"Stray arguments."
|
"Stray arguments."
|
||||||
);
|
);
|
||||||
@ -98,17 +99,17 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
{
|
{
|
||||||
string const literal = literals[0];
|
string const literal = literals[0];
|
||||||
if (literal.empty())
|
if (literal.empty())
|
||||||
m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid.");
|
m_errorReporter.syntaxError(3250_error, _pragma.location(), "Empty experimental feature name is invalid.");
|
||||||
else if (!ExperimentalFeatureNames.count(literal))
|
else if (!ExperimentalFeatureNames.count(literal))
|
||||||
m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name.");
|
m_errorReporter.syntaxError(8491_error, _pragma.location(), "Unsupported experimental feature name.");
|
||||||
else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal)))
|
else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal)))
|
||||||
m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name.");
|
m_errorReporter.syntaxError(1231_error, _pragma.location(), "Duplicate experimental feature name.");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto feature = ExperimentalFeatureNames.at(literal);
|
auto feature = ExperimentalFeatureNames.at(literal);
|
||||||
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
|
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
|
||||||
if (!ExperimentalFeatureWithoutWarning.count(feature))
|
if (!ExperimentalFeatureWithoutWarning.count(feature))
|
||||||
m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,15 +122,16 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
|
|||||||
static SemVerVersion const currentVersion{string(VersionString)};
|
static SemVerVersion const currentVersion{string(VersionString)};
|
||||||
if (!matchExpression.matches(currentVersion))
|
if (!matchExpression.matches(currentVersion))
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
3997_error,
|
||||||
_pragma.location(),
|
_pragma.location(),
|
||||||
"Source file requires different compiler version (current compiler is " +
|
"Source file requires different compiler version (current compiler is " +
|
||||||
string(VersionString) + " - note that nightly builds are considered to be "
|
string(VersionString) + ") - note that nightly builds are considered to be "
|
||||||
"strictly less than the released version"
|
"strictly less than the released version"
|
||||||
);
|
);
|
||||||
m_versionPragmaFound = true;
|
m_versionPragmaFound = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&)
|
|||||||
void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
|
void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
|
||||||
{
|
{
|
||||||
if (_modifier.isImplemented() && !m_placeholderFound)
|
if (_modifier.isImplemented() && !m_placeholderFound)
|
||||||
m_errorReporter.syntaxError(_modifier.body().location(), "Modifier body does not contain '_'.");
|
m_errorReporter.syntaxError(2883_error, _modifier.body().location(), "Modifier body does not contain '_'.");
|
||||||
m_placeholderFound = false;
|
m_placeholderFound = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _stat
|
|||||||
{
|
{
|
||||||
auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement);
|
auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement);
|
||||||
if (varDecl)
|
if (varDecl)
|
||||||
m_errorReporter.syntaxError(_statement.location(), "Variable declarations can only be used inside blocks.");
|
m_errorReporter.syntaxError(9079_error, _statement.location(), "Variable declarations can only be used inside blocks.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyntaxChecker::visit(IfStatement const& _ifStatement)
|
bool SyntaxChecker::visit(IfStatement const& _ifStatement)
|
||||||
@ -189,7 +191,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement)
|
|||||||
{
|
{
|
||||||
if (m_inLoopDepth <= 0)
|
if (m_inLoopDepth <= 0)
|
||||||
// we're not in a for/while loop, report syntax error
|
// we're not in a for/while loop, report syntax error
|
||||||
m_errorReporter.syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
|
m_errorReporter.syntaxError(4123_error, _continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,13 +199,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
|
|||||||
{
|
{
|
||||||
if (m_inLoopDepth <= 0)
|
if (m_inLoopDepth <= 0)
|
||||||
// we're not in a for/while loop, report syntax error
|
// we're not in a for/while loop, report syntax error
|
||||||
m_errorReporter.syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
|
m_errorReporter.syntaxError(6102_error, _breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SyntaxChecker::visit(Throw const& _throwStatement)
|
bool SyntaxChecker::visit(Throw const& _throwStatement)
|
||||||
{
|
{
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
4538_error,
|
||||||
_throwStatement.location(),
|
_throwStatement.location(),
|
||||||
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
|
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
|
||||||
);
|
);
|
||||||
@ -222,29 +225,29 @@ bool SyntaxChecker::visit(Literal const& _literal)
|
|||||||
// Generic checks no matter what base this number literal is of:
|
// Generic checks no matter what base this number literal is of:
|
||||||
if (value.back() == '_')
|
if (value.back() == '_')
|
||||||
{
|
{
|
||||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
|
m_errorReporter.syntaxError(2090_error, _literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.find("__") != ASTString::npos)
|
if (value.find("__") != ASTString::npos)
|
||||||
{
|
{
|
||||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
|
m_errorReporter.syntaxError(2990_error, _literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_literal.isHexNumber()) // decimal literal
|
if (!_literal.isHexNumber()) // decimal literal
|
||||||
{
|
{
|
||||||
if (value.find("._") != ASTString::npos)
|
if (value.find("._") != ASTString::npos)
|
||||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
m_errorReporter.syntaxError(3891_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||||
|
|
||||||
if (value.find("_.") != ASTString::npos)
|
if (value.find("_.") != ASTString::npos)
|
||||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
m_errorReporter.syntaxError(1023_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
|
||||||
|
|
||||||
if (value.find("_e") != ASTString::npos)
|
if (value.find("_e") != ASTString::npos)
|
||||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
|
m_errorReporter.syntaxError(6415_error, _literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
|
||||||
|
|
||||||
if (value.find("e_") != ASTString::npos)
|
if (value.find("e_") != ASTString::npos)
|
||||||
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
|
m_errorReporter.syntaxError(6165_error, _literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -253,7 +256,7 @@ bool SyntaxChecker::visit(Literal const& _literal)
|
|||||||
bool SyntaxChecker::visit(UnaryOperation const& _operation)
|
bool SyntaxChecker::visit(UnaryOperation const& _operation)
|
||||||
{
|
{
|
||||||
if (_operation.getOperator() == Token::Add)
|
if (_operation.getOperator() == Token::Add)
|
||||||
m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed.");
|
m_errorReporter.syntaxError(9636_error, _operation.location(), "Use of unary + is disallowed.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -265,6 +268,7 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
|
|
||||||
if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations()))
|
if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations()))
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
6553_error,
|
||||||
_inlineAssembly.location(),
|
_inlineAssembly.location(),
|
||||||
"The msize instruction cannot be used when the Yul optimizer is activated because "
|
"The msize instruction cannot be used when the Yul optimizer is activated because "
|
||||||
"it can change its semantics. Either disable the Yul optimizer or do not use the instruction."
|
"it can change its semantics. Either disable the Yul optimizer or do not use the instruction."
|
||||||
@ -285,7 +289,9 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
|
|||||||
ASTString const& contractName = _contract.name();
|
ASTString const& contractName = _contract.name();
|
||||||
for (FunctionDefinition const* function: _contract.definedFunctions())
|
for (FunctionDefinition const* function: _contract.definedFunctions())
|
||||||
if (function->name() == contractName)
|
if (function->name() == contractName)
|
||||||
m_errorReporter.syntaxError(function->location(),
|
m_errorReporter.syntaxError(
|
||||||
|
5796_error,
|
||||||
|
function->location(),
|
||||||
"Functions are not allowed to have the same name as the contract. "
|
"Functions are not allowed to have the same name as the contract. "
|
||||||
"If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
|
"If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
|
||||||
);
|
);
|
||||||
@ -298,15 +304,16 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
|||||||
{
|
{
|
||||||
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
|
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
4937_error,
|
||||||
_function.location(),
|
_function.location(),
|
||||||
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
|
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_isInterface && !_function.modifiers().empty())
|
if (m_isInterface && !_function.modifiers().empty())
|
||||||
m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers.");
|
m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers.");
|
||||||
else if (!_function.isImplemented() && !_function.modifiers().empty())
|
else if (!_function.isImplemented() && !_function.modifiers().empty())
|
||||||
m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers.");
|
m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -315,11 +322,11 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
|
|||||||
{
|
{
|
||||||
for (auto const& decl: _node.parameterTypeList()->parameters())
|
for (auto const& decl: _node.parameterTypeList()->parameters())
|
||||||
if (!decl->name().empty())
|
if (!decl->name().empty())
|
||||||
m_errorReporter.warning(decl->location(), "Naming function type parameters is deprecated.");
|
m_errorReporter.warning(6162_error, decl->location(), "Naming function type parameters is deprecated.");
|
||||||
|
|
||||||
for (auto const& decl: _node.returnParameterTypeList()->parameters())
|
for (auto const& decl: _node.returnParameterTypeList()->parameters())
|
||||||
if (!decl->name().empty())
|
if (!decl->name().empty())
|
||||||
m_errorReporter.syntaxError(decl->location(), "Return parameters in function types may not be named.");
|
m_errorReporter.syntaxError(7304_error, decl->location(), "Return parameters in function types may not be named.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -327,8 +334,13 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
|
|||||||
bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
|
bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
|
||||||
{
|
{
|
||||||
// Report if none of the variable components in the tuple have a name (only possible via deprecated "var")
|
// Report if none of the variable components in the tuple have a name (only possible via deprecated "var")
|
||||||
if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr))
|
if (std::all_of(
|
||||||
|
_statement.declarations().begin(),
|
||||||
|
_statement.declarations().end(),
|
||||||
|
[](ASTPointer<VariableDeclaration> const& declaration) { return declaration == nullptr; }
|
||||||
|
))
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
3299_error,
|
||||||
_statement.location(),
|
_statement.location(),
|
||||||
"The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty."
|
"The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty."
|
||||||
);
|
);
|
||||||
@ -339,7 +351,7 @@ bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
|
|||||||
bool SyntaxChecker::visit(StructDefinition const& _struct)
|
bool SyntaxChecker::visit(StructDefinition const& _struct)
|
||||||
{
|
{
|
||||||
if (_struct.members().empty())
|
if (_struct.members().empty())
|
||||||
m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed.");
|
m_errorReporter.syntaxError(5306_error, _struct.location(), "Defining empty structs is disallowed.");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -171,6 +171,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
|||||||
!_funDef.overrides()
|
!_funDef.overrides()
|
||||||
)
|
)
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
2018_error,
|
||||||
_funDef.location(),
|
_funDef.location(),
|
||||||
"Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability)
|
"Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability)
|
||||||
);
|
);
|
||||||
@ -229,7 +230,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
|
|||||||
{
|
{
|
||||||
AssemblyViewPureChecker{
|
AssemblyViewPureChecker{
|
||||||
_inlineAssembly.dialect(),
|
_inlineAssembly.dialect(),
|
||||||
[=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
[&](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
|
||||||
}(_inlineAssembly.operations());
|
}(_inlineAssembly.operations());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +253,7 @@ void ViewPureChecker::reportMutability(
|
|||||||
))
|
))
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
2527_error,
|
||||||
_location,
|
_location,
|
||||||
"Function declared as pure, but this expression (potentially) reads from the "
|
"Function declared as pure, but this expression (potentially) reads from the "
|
||||||
"environment or state and thus requires \"view\"."
|
"environment or state and thus requires \"view\"."
|
||||||
@ -261,6 +263,7 @@ void ViewPureChecker::reportMutability(
|
|||||||
else if (_mutability == StateMutability::NonPayable)
|
else if (_mutability == StateMutability::NonPayable)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
8961_error,
|
||||||
_location,
|
_location,
|
||||||
"Function declared as " +
|
"Function declared as " +
|
||||||
stateMutabilityToString(m_currentFunction->stateMutability()) +
|
stateMutabilityToString(m_currentFunction->stateMutability()) +
|
||||||
@ -277,12 +280,14 @@ void ViewPureChecker::reportMutability(
|
|||||||
{
|
{
|
||||||
if (_nestedLocation)
|
if (_nestedLocation)
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
4006_error,
|
||||||
_location,
|
_location,
|
||||||
SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation),
|
SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation),
|
||||||
"This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal."
|
"This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal."
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
5887_error,
|
||||||
_location,
|
_location,
|
||||||
"\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function "
|
"\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function "
|
||||||
"\"payable\" or use an internal function to avoid this error."
|
"\"payable\" or use an internal function to avoid this error."
|
||||||
@ -357,6 +362,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
|||||||
{MagicType::Kind::MetaType, "runtimeCode"},
|
{MagicType::Kind::MetaType, "runtimeCode"},
|
||||||
{MagicType::Kind::MetaType, "name"},
|
{MagicType::Kind::MetaType, "name"},
|
||||||
{MagicType::Kind::MetaType, "interfaceId"},
|
{MagicType::Kind::MetaType, "interfaceId"},
|
||||||
|
{MagicType::Kind::MetaType, "min"},
|
||||||
|
{MagicType::Kind::MetaType, "max"},
|
||||||
};
|
};
|
||||||
set<MagicMember> static const payableMembers{
|
set<MagicMember> static const payableMembers{
|
||||||
{MagicType::Kind::Message, "value"}
|
{MagicType::Kind::Message, "value"}
|
||||||
|
@ -556,7 +556,13 @@ MagicType const* TypeProvider::magic(MagicType::Kind _kind)
|
|||||||
|
|
||||||
MagicType const* TypeProvider::meta(Type const* _type)
|
MagicType const* TypeProvider::meta(Type const* _type)
|
||||||
{
|
{
|
||||||
solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
|
solAssert(
|
||||||
|
_type && (
|
||||||
|
_type->category() == Type::Category::Contract ||
|
||||||
|
_type->category() == Type::Category::Integer
|
||||||
|
),
|
||||||
|
"Only contracts or integer types supported for now."
|
||||||
|
);
|
||||||
return createAndGet<MagicType>(_type);
|
return createAndGet<MagicType>(_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,6 +556,22 @@ string IntegerType::toString(bool) const
|
|||||||
return prefix + util::toString(m_bits);
|
return prefix + util::toString(m_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u256 IntegerType::min() const
|
||||||
|
{
|
||||||
|
if (isSigned())
|
||||||
|
return s2u(s256(minValue()));
|
||||||
|
else
|
||||||
|
return u256(minValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
u256 IntegerType::max() const
|
||||||
|
{
|
||||||
|
if (isSigned())
|
||||||
|
return s2u(s256(maxValue()));
|
||||||
|
else
|
||||||
|
return u256(maxValue());
|
||||||
|
}
|
||||||
|
|
||||||
bigint IntegerType::minValue() const
|
bigint IntegerType::minValue() const
|
||||||
{
|
{
|
||||||
if (isSigned())
|
if (isSigned())
|
||||||
@ -3763,20 +3779,35 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
|||||||
case Kind::MetaType:
|
case Kind::MetaType:
|
||||||
{
|
{
|
||||||
solAssert(
|
solAssert(
|
||||||
m_typeArgument && m_typeArgument->category() == Type::Category::Contract,
|
m_typeArgument && (
|
||||||
"Only contracts supported for now"
|
m_typeArgument->category() == Type::Category::Contract ||
|
||||||
|
m_typeArgument->category() == Type::Category::Integer
|
||||||
|
),
|
||||||
|
"Only contracts or integer types supported for now"
|
||||||
);
|
);
|
||||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
|
||||||
if (contract.canBeDeployed())
|
if (m_typeArgument->category() == Type::Category::Contract)
|
||||||
|
{
|
||||||
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
||||||
|
if (contract.canBeDeployed())
|
||||||
|
return MemberList::MemberMap({
|
||||||
|
{"creationCode", TypeProvider::array(DataLocation::Memory)},
|
||||||
|
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
|
||||||
|
{"name", TypeProvider::stringMemory()},
|
||||||
|
});
|
||||||
|
else
|
||||||
|
return MemberList::MemberMap({
|
||||||
|
{"interfaceId", TypeProvider::fixedBytes(4)},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (m_typeArgument->category() == Type::Category::Integer)
|
||||||
|
{
|
||||||
|
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
|
||||||
return MemberList::MemberMap({
|
return MemberList::MemberMap({
|
||||||
{"creationCode", TypeProvider::array(DataLocation::Memory)},
|
{"min", integerTypePointer},
|
||||||
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
|
{"max", integerTypePointer},
|
||||||
{"name", TypeProvider::stringMemory()},
|
|
||||||
});
|
|
||||||
else
|
|
||||||
return MemberList::MemberMap({
|
|
||||||
{"interfaceId", TypeProvider::fixedBytes(4)},
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
solAssert(false, "Unknown kind of magic.");
|
solAssert(false, "Unknown kind of magic.");
|
||||||
|
@ -452,6 +452,9 @@ public:
|
|||||||
unsigned numBits() const { return m_bits; }
|
unsigned numBits() const { return m_bits; }
|
||||||
bool isSigned() const { return m_modifier == Modifier::Signed; }
|
bool isSigned() const { return m_modifier == Modifier::Signed; }
|
||||||
|
|
||||||
|
u256 min() const;
|
||||||
|
u256 max() const;
|
||||||
|
|
||||||
bigint minValue() const;
|
bigint minValue() const;
|
||||||
bigint maxValue() const;
|
bigint maxValue() const;
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
#include <libsolutil/StringUtils.h>
|
#include <libsolutil/StringUtils.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -38,7 +37,8 @@ using namespace solidity::frontend;
|
|||||||
string ABIFunctions::tupleEncoder(
|
string ABIFunctions::tupleEncoder(
|
||||||
TypePointers const& _givenTypes,
|
TypePointers const& _givenTypes,
|
||||||
TypePointers const& _targetTypes,
|
TypePointers const& _targetTypes,
|
||||||
bool _encodeAsLibraryTypes
|
bool _encodeAsLibraryTypes,
|
||||||
|
bool _reversed
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EncodingOptions options;
|
EncodingOptions options;
|
||||||
@ -54,6 +54,8 @@ string ABIFunctions::tupleEncoder(
|
|||||||
for (auto const& t: _targetTypes)
|
for (auto const& t: _targetTypes)
|
||||||
functionName += t->identifier() + "_";
|
functionName += t->identifier() + "_";
|
||||||
functionName += options.toFunctionNameSuffix();
|
functionName += options.toFunctionNameSuffix();
|
||||||
|
if (_reversed)
|
||||||
|
functionName += "_reversed";
|
||||||
|
|
||||||
return createFunction(functionName, [&]() {
|
return createFunction(functionName, [&]() {
|
||||||
// Note that the values are in reverse due to the difference in calling semantics.
|
// Note that the values are in reverse due to the difference in calling semantics.
|
||||||
@ -94,7 +96,10 @@ string ABIFunctions::tupleEncoder(
|
|||||||
stackPos += sizeOnStack;
|
stackPos += sizeOnStack;
|
||||||
}
|
}
|
||||||
solAssert(headPos == headSize_, "");
|
solAssert(headPos == headSize_, "");
|
||||||
string valueParams = suffixedVariableNameList("value", stackPos, 0);
|
string valueParams =
|
||||||
|
_reversed ?
|
||||||
|
suffixedVariableNameList("value", stackPos, 0) :
|
||||||
|
suffixedVariableNameList("value", 0, stackPos);
|
||||||
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
||||||
templ("encodeElements", encodeElements);
|
templ("encodeElements", encodeElements);
|
||||||
|
|
||||||
@ -104,7 +109,8 @@ string ABIFunctions::tupleEncoder(
|
|||||||
|
|
||||||
string ABIFunctions::tupleEncoderPacked(
|
string ABIFunctions::tupleEncoderPacked(
|
||||||
TypePointers const& _givenTypes,
|
TypePointers const& _givenTypes,
|
||||||
TypePointers const& _targetTypes
|
TypePointers const& _targetTypes,
|
||||||
|
bool _reversed
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EncodingOptions options;
|
EncodingOptions options;
|
||||||
@ -120,6 +126,8 @@ string ABIFunctions::tupleEncoderPacked(
|
|||||||
for (auto const& t: _targetTypes)
|
for (auto const& t: _targetTypes)
|
||||||
functionName += t->identifier() + "_";
|
functionName += t->identifier() + "_";
|
||||||
functionName += options.toFunctionNameSuffix();
|
functionName += options.toFunctionNameSuffix();
|
||||||
|
if (_reversed)
|
||||||
|
functionName += "_reversed";
|
||||||
|
|
||||||
return createFunction(functionName, [&]() {
|
return createFunction(functionName, [&]() {
|
||||||
solAssert(!_givenTypes.empty(), "");
|
solAssert(!_givenTypes.empty(), "");
|
||||||
@ -158,7 +166,10 @@ string ABIFunctions::tupleEncoderPacked(
|
|||||||
encodeElements += elementTempl.render();
|
encodeElements += elementTempl.render();
|
||||||
stackPos += sizeOnStack;
|
stackPos += sizeOnStack;
|
||||||
}
|
}
|
||||||
string valueParams = suffixedVariableNameList("value", stackPos, 0);
|
string valueParams =
|
||||||
|
_reversed ?
|
||||||
|
suffixedVariableNameList("value", stackPos, 0) :
|
||||||
|
suffixedVariableNameList("value", 0, stackPos);
|
||||||
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
|
||||||
templ("encodeElements", encodeElements);
|
templ("encodeElements", encodeElements);
|
||||||
|
|
||||||
|
@ -67,31 +67,53 @@ public:
|
|||||||
|
|
||||||
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
|
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
|
||||||
/// into memory, converting the types to @a _targetTypes on the fly.
|
/// into memory, converting the types to @a _targetTypes on the fly.
|
||||||
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e.
|
/// Parameters are: <headStart> <value_1> ... <value_n>, i.e.
|
||||||
/// the layout on the stack is <value_1> ... <value_n> <headStart> with
|
/// the layout on the stack is <value_n> ... <value_1> <headStart> with
|
||||||
/// the top of the stack on the right.
|
/// the top of the stack on the right.
|
||||||
/// The values represent stack slots. If a type occupies more or less than one
|
/// The values represent stack slots. If a type occupies more or less than one
|
||||||
/// stack slot, it takes exactly that number of values.
|
/// stack slot, it takes exactly that number of values.
|
||||||
/// Returns a pointer to the end of the area written in memory.
|
/// Returns a pointer to the end of the area written in memory.
|
||||||
/// Does not allocate memory (does not change the free memory pointer), but writes
|
/// Does not allocate memory (does not change the free memory pointer), but writes
|
||||||
/// to memory starting at $headStart and an unrestricted amount after that.
|
/// to memory starting at $headStart and an unrestricted amount after that.
|
||||||
|
/// If @reversed is true, the order of the variables after <headStart> is reversed.
|
||||||
std::string tupleEncoder(
|
std::string tupleEncoder(
|
||||||
TypePointers const& _givenTypes,
|
TypePointers const& _givenTypes,
|
||||||
TypePointers const& _targetTypes,
|
TypePointers const& _targetTypes,
|
||||||
bool _encodeAsLibraryTypes = false
|
bool _encodeAsLibraryTypes = false,
|
||||||
|
bool _reversed = false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Specialization of tupleEncoder to _reversed = true
|
||||||
|
std::string tupleEncoderReversed(
|
||||||
|
TypePointers const& _givenTypes,
|
||||||
|
TypePointers const& _targetTypes,
|
||||||
|
bool _encodeAsLibraryTypes = false
|
||||||
|
) {
|
||||||
|
return tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes, true);
|
||||||
|
}
|
||||||
|
|
||||||
/// @returns name of an assembly function to encode values of @a _givenTypes
|
/// @returns name of an assembly function to encode values of @a _givenTypes
|
||||||
/// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
|
/// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
|
||||||
/// Parameters are: <memPos> <value_n> ... <value_1>, i.e.
|
/// Parameters are: <memPos> <value_1> ... <value_n>, i.e.
|
||||||
/// the layout on the stack is <value_1> ... <value_n> <memPos> with
|
/// the layout on the stack is <value_n> ... <value_1> <memPos> with
|
||||||
/// the top of the stack on the right.
|
/// the top of the stack on the right.
|
||||||
/// The values represent stack slots. If a type occupies more or less than one
|
/// The values represent stack slots. If a type occupies more or less than one
|
||||||
/// stack slot, it takes exactly that number of values.
|
/// stack slot, it takes exactly that number of values.
|
||||||
/// Returns a pointer to the end of the area written in memory.
|
/// Returns a pointer to the end of the area written in memory.
|
||||||
/// Does not allocate memory (does not change the free memory pointer), but writes
|
/// Does not allocate memory (does not change the free memory pointer), but writes
|
||||||
/// to memory starting at memPos and an unrestricted amount after that.
|
/// to memory starting at memPos and an unrestricted amount after that.
|
||||||
std::string tupleEncoderPacked(TypePointers const& _givenTypes, TypePointers const& _targetTypes);
|
/// If @reversed is true, the order of the variables after <headStart> is reversed.
|
||||||
|
std::string tupleEncoderPacked(
|
||||||
|
TypePointers const& _givenTypes,
|
||||||
|
TypePointers const& _targetTypes,
|
||||||
|
bool _reversed = false
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Specialization of tupleEncoderPacked to _reversed = true
|
||||||
|
std::string tupleEncoderPackedReversed(TypePointers const& _givenTypes, TypePointers const& _targetTypes)
|
||||||
|
{
|
||||||
|
return tupleEncoderPacked(_givenTypes, _targetTypes, true);
|
||||||
|
}
|
||||||
|
|
||||||
/// @returns name of an assembly function to ABI-decode values of @a _types
|
/// @returns name of an assembly function to ABI-decode values of @a _types
|
||||||
/// into memory. If @a _fromMemory is true, decodes from memory instead of
|
/// into memory. If @a _fromMemory is true, decodes from memory instead of
|
||||||
|
@ -49,6 +49,9 @@ void Compiler::compileContract(
|
|||||||
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
|
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
|
||||||
|
|
||||||
m_context.optimise(m_optimiserSettings);
|
m_context.optimise(m_optimiserSettings);
|
||||||
|
|
||||||
|
solAssert(m_context.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
|
||||||
|
solAssert(m_runtimeContext.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<evmasm::Assembly> Compiler::runtimeAssemblyPtr() const
|
std::shared_ptr<evmasm::Assembly> Compiler::runtimeAssemblyPtr() const
|
||||||
|
@ -192,6 +192,9 @@ void CompilerContext::appendMissingLowLevelFunctions()
|
|||||||
|
|
||||||
pair<string, set<string>> CompilerContext::requestedYulFunctions()
|
pair<string, set<string>> CompilerContext::requestedYulFunctions()
|
||||||
{
|
{
|
||||||
|
solAssert(!m_requestedYulFunctionsRan, "requestedYulFunctions called more than once.");
|
||||||
|
m_requestedYulFunctionsRan = true;
|
||||||
|
|
||||||
set<string> empty;
|
set<string> empty;
|
||||||
swap(empty, m_externallyUsedYulFunctions);
|
swap(empty, m_externallyUsedYulFunctions);
|
||||||
return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)};
|
return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)};
|
||||||
|
@ -167,6 +167,7 @@ public:
|
|||||||
/// Clears the internal list, i.e. calling it again will result in an
|
/// Clears the internal list, i.e. calling it again will result in an
|
||||||
/// empty return value.
|
/// empty return value.
|
||||||
std::pair<std::string, std::set<std::string>> requestedYulFunctions();
|
std::pair<std::string, std::set<std::string>> requestedYulFunctions();
|
||||||
|
bool requestedYulFunctionsRan() const { return m_requestedYulFunctionsRan; }
|
||||||
|
|
||||||
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
|
||||||
unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
|
unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
|
||||||
@ -389,6 +390,8 @@ private:
|
|||||||
YulUtilFunctions m_yulUtilFunctions;
|
YulUtilFunctions m_yulUtilFunctions;
|
||||||
/// The queue of low-level functions to generate.
|
/// The queue of low-level functions to generate.
|
||||||
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
|
||||||
|
/// Flag to check that requestedYulFunctions() was called exactly once
|
||||||
|
bool m_requestedYulFunctionsRan = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -559,8 +559,8 @@ void CompilerUtils::abiEncodeV2(
|
|||||||
|
|
||||||
string encoderName =
|
string encoderName =
|
||||||
_padToWordBoundaries ?
|
_padToWordBoundaries ?
|
||||||
m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
|
m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
|
||||||
m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes);
|
m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes);
|
||||||
m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1);
|
m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,8 +103,6 @@ void ContractCompiler::compileContract(
|
|||||||
// and adds the function to the compilation queue. Additionally internal functions,
|
// and adds the function to the compilation queue. Additionally internal functions,
|
||||||
// which are referenced directly or indirectly will be added.
|
// which are referenced directly or indirectly will be added.
|
||||||
appendFunctionSelector(_contract);
|
appendFunctionSelector(_contract);
|
||||||
// This processes the above populated queue until it is empty.
|
|
||||||
appendMissingFunctions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ContractCompiler::compileConstructor(
|
size_t ContractCompiler::compileConstructor(
|
||||||
@ -159,10 +157,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
|
|||||||
|
|
||||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||||
appendConstructor(*constructor);
|
appendConstructor(*constructor);
|
||||||
else if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
|
|
||||||
appendBaseConstructor(*c);
|
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// Implicit constructors are always non-payable.
|
||||||
appendCallValueCheck();
|
appendCallValueCheck();
|
||||||
|
if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
|
||||||
|
appendBaseConstructor(*c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
|
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
|
||||||
@ -215,6 +216,9 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
|
|||||||
solAssert(!!m_runtimeCompiler, "");
|
solAssert(!!m_runtimeCompiler, "");
|
||||||
solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
|
solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
|
||||||
|
|
||||||
|
appendMissingFunctions();
|
||||||
|
m_runtimeCompiler->appendMissingFunctions();
|
||||||
|
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||||
|
|
||||||
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
|
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
|
||||||
@ -586,13 +590,13 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
|||||||
if (!_function.isConstructor())
|
if (!_function.isConstructor())
|
||||||
// adding 1 for return address.
|
// adding 1 for return address.
|
||||||
m_context.adjustStackOffset(parametersSize + 1);
|
m_context.adjustStackOffset(parametersSize + 1);
|
||||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters())
|
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters())
|
||||||
{
|
{
|
||||||
m_context.addVariable(*variable, parametersSize);
|
m_context.addVariable(*variable, parametersSize);
|
||||||
parametersSize -= variable->annotation().type->sizeOnStack();
|
parametersSize -= variable->annotation().type->sizeOnStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
|
for (ASTPointer<VariableDeclaration> const& variable: _function.returnParameters())
|
||||||
appendStackVariableInitialisation(*variable);
|
appendStackVariableInitialisation(*variable);
|
||||||
|
|
||||||
if (_function.isConstructor())
|
if (_function.isConstructor())
|
||||||
@ -649,7 +653,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
|||||||
if (stackLayout[i] != i)
|
if (stackLayout[i] != i)
|
||||||
solAssert(false, "Invalid stack layout on cleanup.");
|
solAssert(false, "Invalid stack layout on cleanup.");
|
||||||
|
|
||||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters())
|
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters() + _function.returnParameters())
|
||||||
m_context.removeVariable(*variable);
|
m_context.removeVariable(*variable);
|
||||||
|
|
||||||
m_context.adjustStackOffset(-(int)c_returnValuesSize);
|
m_context.adjustStackOffset(-(int)c_returnValuesSize);
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
#include <libsolutil/Whiskers.h>
|
#include <libsolutil/Whiskers.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
@ -1591,6 +1590,16 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||||
m_context << (u256{result} << (256 - 32));
|
m_context << (u256{result} << (256 - 32));
|
||||||
}
|
}
|
||||||
|
else if (member == "min" || member == "max")
|
||||||
|
{
|
||||||
|
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||||
|
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||||
|
|
||||||
|
if (member == "min")
|
||||||
|
m_context << integerType->min();
|
||||||
|
else
|
||||||
|
m_context << integerType->max();
|
||||||
|
}
|
||||||
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
|
@ -23,8 +23,6 @@
|
|||||||
|
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
|
@ -28,9 +28,6 @@
|
|||||||
#include <libsolutil/Whiskers.h>
|
#include <libsolutil/Whiskers.h>
|
||||||
#include <libsolutil/StringUtils.h>
|
#include <libsolutil/StringUtils.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
@ -299,9 +296,6 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
|||||||
|
|
||||||
string YulUtilFunctions::shiftRightFunctionDynamic()
|
string YulUtilFunctions::shiftRightFunctionDynamic()
|
||||||
{
|
{
|
||||||
// Note that if this is extended with signed shifts,
|
|
||||||
// the opcodes SAR and SDIV behave differently with regards to rounding!
|
|
||||||
|
|
||||||
string const functionName = "shift_right_unsigned_dynamic";
|
string const functionName = "shift_right_unsigned_dynamic";
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
return
|
return
|
||||||
@ -321,6 +315,86 @@ string YulUtilFunctions::shiftRightFunctionDynamic()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::shiftRightSignedFunctionDynamic()
|
||||||
|
{
|
||||||
|
string const functionName = "shift_right_signed_dynamic";
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(bits, value) -> result {
|
||||||
|
<?hasShifts>
|
||||||
|
result := sar(bits, value)
|
||||||
|
<!hasShifts>
|
||||||
|
let divisor := exp(2, bits)
|
||||||
|
let xor_mask := sub(0, slt(value, 0))
|
||||||
|
result := xor(div(xor(value, xor_mask), divisor), xor_mask)
|
||||||
|
// combined version of
|
||||||
|
// switch slt(value, 0)
|
||||||
|
// case 0 { result := div(value, divisor) }
|
||||||
|
// default { result := not(div(not(value), divisor)) }
|
||||||
|
</hasShifts>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("hasShifts", m_evmVersion.hasBitwiseShifting())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType)
|
||||||
|
{
|
||||||
|
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
|
||||||
|
solAssert(_amountType.category() == Type::Category::Integer, "");
|
||||||
|
string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier();
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(value, bits) -> result {
|
||||||
|
bits := <cleanAmount>(bits)
|
||||||
|
<?amountSigned>
|
||||||
|
if slt(bits, 0) { invalid() }
|
||||||
|
</amountSigned>
|
||||||
|
result := <cleanup>(<shift>(bits, value))
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
|
||||||
|
("cleanAmount", cleanupFunction(_amountType))
|
||||||
|
("shift", shiftLeftFunctionDynamic())
|
||||||
|
("cleanup", cleanupFunction(_type))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType)
|
||||||
|
{
|
||||||
|
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
|
||||||
|
solAssert(_amountType.category() == Type::Category::Integer, "");
|
||||||
|
IntegerType const* integerType = dynamic_cast<IntegerType const*>(&_type);
|
||||||
|
bool valueSigned = integerType && integerType->isSigned();
|
||||||
|
|
||||||
|
string const functionName = "shift_right_" + _type.identifier() + "_" + _amountType.identifier();
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(value, bits) -> result {
|
||||||
|
bits := <cleanAmount>(bits)
|
||||||
|
<?amountSigned>
|
||||||
|
if slt(bits, 0) { invalid() }
|
||||||
|
</amountSigned>
|
||||||
|
result := <cleanup>(<shift>(bits, <cleanup>(value)))
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
|
||||||
|
("cleanAmount", cleanupFunction(_amountType))
|
||||||
|
("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic())
|
||||||
|
("cleanup", cleanupFunction(_type))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
||||||
{
|
{
|
||||||
solAssert(_numBytes <= 32, "");
|
solAssert(_numBytes <= 32, "");
|
||||||
@ -393,6 +467,8 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
|||||||
return
|
return
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
function <functionName>(x, y) -> sum {
|
function <functionName>(x, y) -> sum {
|
||||||
|
x := <cleanupFunction>(x)
|
||||||
|
y := <cleanupFunction>(y)
|
||||||
<?signed>
|
<?signed>
|
||||||
// overflow, if x >= 0 and y > (maxValue - x)
|
// overflow, if x >= 0 and y > (maxValue - x)
|
||||||
if and(iszero(slt(x, 0)), sgt(y, sub(<maxValue>, x))) { revert(0, 0) }
|
if and(iszero(slt(x, 0)), sgt(y, sub(<maxValue>, x))) { revert(0, 0) }
|
||||||
@ -409,6 +485,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
|||||||
("signed", _type.isSigned())
|
("signed", _type.isSigned())
|
||||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -421,6 +498,8 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
|||||||
// Multiplication by zero could be treated separately and directly return zero.
|
// Multiplication by zero could be treated separately and directly return zero.
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
function <functionName>(x, y) -> product {
|
function <functionName>(x, y) -> product {
|
||||||
|
x := <cleanupFunction>(x)
|
||||||
|
y := <cleanupFunction>(y)
|
||||||
<?signed>
|
<?signed>
|
||||||
// overflow, if x > 0, y > 0 and x > (maxValue / y)
|
// overflow, if x > 0, y > 0 and x > (maxValue / y)
|
||||||
if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(<maxValue>, y))) { revert(0, 0) }
|
if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(<maxValue>, y))) { revert(0, 0) }
|
||||||
@ -441,6 +520,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
|||||||
("signed", _type.isSigned())
|
("signed", _type.isSigned())
|
||||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -452,6 +532,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
|||||||
return
|
return
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
function <functionName>(x, y) -> r {
|
function <functionName>(x, y) -> r {
|
||||||
|
x := <cleanupFunction>(x)
|
||||||
|
y := <cleanupFunction>(y)
|
||||||
if iszero(y) { revert(0, 0) }
|
if iszero(y) { revert(0, 0) }
|
||||||
<?signed>
|
<?signed>
|
||||||
// overflow for minVal / -1
|
// overflow for minVal / -1
|
||||||
@ -466,6 +548,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
|||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("signed", _type.isSigned())
|
("signed", _type.isSigned())
|
||||||
("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
|
("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -477,12 +560,15 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
|
|||||||
return
|
return
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
function <functionName>(x, y) -> r {
|
function <functionName>(x, y) -> r {
|
||||||
|
x := <cleanupFunction>(x)
|
||||||
|
y := <cleanupFunction>(y)
|
||||||
if iszero(y) { revert(0, 0) }
|
if iszero(y) { revert(0, 0) }
|
||||||
r := <?signed>s</signed>mod(x, y)
|
r := <?signed>s</signed>mod(x, y)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("signed", _type.isSigned())
|
("signed", _type.isSigned())
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -494,6 +580,8 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
|||||||
return
|
return
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
function <functionName>(x, y) -> diff {
|
function <functionName>(x, y) -> diff {
|
||||||
|
x := <cleanupFunction>(x)
|
||||||
|
y := <cleanupFunction>(y)
|
||||||
<?signed>
|
<?signed>
|
||||||
// underflow, if y >= 0 and x < (minValue + y)
|
// underflow, if y >= 0 and x < (minValue + y)
|
||||||
if and(iszero(slt(y, 0)), slt(x, add(<minValue>, y))) { revert(0, 0) }
|
if and(iszero(slt(y, 0)), slt(x, add(<minValue>, y))) { revert(0, 0) }
|
||||||
@ -509,6 +597,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
|||||||
("signed", _type.isSigned())
|
("signed", _type.isSigned())
|
||||||
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
|
||||||
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1033,13 +1122,12 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
|
|||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
if (_mappingType.keyType()->isDynamicallySized())
|
if (_mappingType.keyType()->isDynamicallySized())
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot <comma> <key>) -> dataSlot {
|
function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
|
||||||
dataSlot := <hash>(slot <comma> <key>)
|
dataSlot := <hash>(<key> <?+key>,</+key> slot)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("key", _keyType.sizeOnStack() > 0 ? "key" : "")
|
("key", _keyType.sizeOnStack() > 0 ? "key" : "")
|
||||||
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
|
|
||||||
("hash", packedHashFunction(
|
("hash", packedHashFunction(
|
||||||
{&_keyType, TypeProvider::uint256()},
|
{&_keyType, TypeProvider::uint256()},
|
||||||
{_mappingType.keyType(), TypeProvider::uint256()}
|
{_mappingType.keyType(), TypeProvider::uint256()}
|
||||||
@ -1764,8 +1852,18 @@ string YulUtilFunctions::cleanupFunction(Type const& _type)
|
|||||||
solUnimplemented("Fixed point types not implemented.");
|
solUnimplemented("Fixed point types not implemented.");
|
||||||
break;
|
break;
|
||||||
case Type::Category::Function:
|
case Type::Category::Function:
|
||||||
solAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External, "");
|
switch (dynamic_cast<FunctionType const&>(_type).kind())
|
||||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
{
|
||||||
|
case FunctionType::Kind::External:
|
||||||
|
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||||
|
break;
|
||||||
|
case FunctionType::Kind::Internal:
|
||||||
|
templ("body", "cleaned := value");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Category::Array:
|
case Type::Category::Array:
|
||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
@ -1938,14 +2036,16 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
|
|||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(value) -> ret {
|
function <functionName>(value) -> ret {
|
||||||
|
value := <cleanupFunction>(value)
|
||||||
if <lt>(value, <minval>) { revert(0,0) }
|
if <lt>(value, <minval>) { revert(0,0) }
|
||||||
ret := sub(value, 1)
|
ret := sub(value, 1)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("minval", toCompactHexWithPrefix(minintval))
|
("minval", toCompactHexWithPrefix(minintval))
|
||||||
("lt", type.isSigned() ? "slt" : "lt")
|
("lt", type.isSigned() ? "slt" : "lt")
|
||||||
.render();
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1966,14 +2066,16 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
|
|||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(value) -> ret {
|
function <functionName>(value) -> ret {
|
||||||
|
value := <cleanupFunction>(value)
|
||||||
if <gt>(value, <maxval>) { revert(0,0) }
|
if <gt>(value, <maxval>) { revert(0,0) }
|
||||||
ret := add(value, 1)
|
ret := add(value, 1)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("maxval", toCompactHexWithPrefix(maxintval))
|
("maxval", toCompactHexWithPrefix(maxintval))
|
||||||
("gt", type.isSigned() ? "sgt" : "gt")
|
("gt", type.isSigned() ? "sgt" : "gt")
|
||||||
.render();
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1988,15 +2090,17 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
|
|||||||
|
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(_value) -> ret {
|
function <functionName>(value) -> ret {
|
||||||
if slt(_value, <minval>) { revert(0,0) }
|
value := <cleanupFunction>(value)
|
||||||
ret := sub(0, _value)
|
if slt(value, <minval>) { revert(0,0) }
|
||||||
|
ret := sub(0, value)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("minval", toCompactHexWithPrefix(minintval))
|
("minval", toCompactHexWithPrefix(minintval))
|
||||||
.render();
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
});
|
.render();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
|
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
|
||||||
@ -2214,7 +2318,6 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
|||||||
}
|
}
|
||||||
|
|
||||||
solAssert(_type.isValueType(), "");
|
solAssert(_type.isValueType(), "");
|
||||||
|
|
||||||
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
if (funType->kind() == FunctionType::Kind::External)
|
if (funType->kind() == FunctionType::Kind::External)
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
@ -2228,18 +2331,23 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
|||||||
("splitFunction", splitExternalFunctionIdFunction())
|
("splitFunction", splitExternalFunctionIdFunction())
|
||||||
.render();
|
.render();
|
||||||
|
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(memPtr) -> value {
|
function <functionName>(ptr) -> value {
|
||||||
value := <load>(memPtr)
|
<?fromCalldata>
|
||||||
<?needsValidation>
|
value := calldataload(ptr)
|
||||||
<validate>(value)
|
<validate>(value)
|
||||||
</needsValidation>
|
<!fromCalldata>
|
||||||
|
value := <cleanup>(mload(ptr))
|
||||||
|
</fromCalldata>
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("load", _fromCalldata ? "calldataload" : "mload")
|
("fromCalldata", _fromCalldata)
|
||||||
("needsValidation", _fromCalldata)
|
("validate", validatorFunction(_type))
|
||||||
("validate", _fromCalldata ? validatorFunction(_type) : "")
|
// Byte array elements generally need cleanup.
|
||||||
|
// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
|
||||||
|
("cleanup", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,14 @@ public:
|
|||||||
std::string shiftLeftFunctionDynamic();
|
std::string shiftLeftFunctionDynamic();
|
||||||
std::string shiftRightFunction(size_t _numBits);
|
std::string shiftRightFunction(size_t _numBits);
|
||||||
std::string shiftRightFunctionDynamic();
|
std::string shiftRightFunctionDynamic();
|
||||||
|
std::string shiftRightSignedFunctionDynamic();
|
||||||
|
|
||||||
|
/// @returns the name of a function that performs a left shift and subsequent cleanup
|
||||||
|
/// and, if needed, prior cleanup.
|
||||||
|
/// If the amount to shift by is signed, a check for negativeness is performed.
|
||||||
|
/// signature: (value, amountToShift) -> result
|
||||||
|
std::string typedShiftLeftFunction(Type const& _type, Type const& _amountType);
|
||||||
|
std::string typedShiftRightFunction(Type const& _type, Type const& _amountType);
|
||||||
|
|
||||||
/// @returns the name of a function which replaces the
|
/// @returns the name of a function which replaces the
|
||||||
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
|
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
|
||||||
@ -204,7 +212,7 @@ public:
|
|||||||
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||||
std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes);
|
std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes);
|
||||||
|
|
||||||
/// @returns a function that reads a value type from memory.
|
/// @returns a function that reads a value type from memory. Performs cleanup.
|
||||||
/// signature: (addr) -> value
|
/// signature: (addr) -> value
|
||||||
std::string readFromMemory(Type const& _type);
|
std::string readFromMemory(Type const& _type);
|
||||||
/// @returns a function that reads a value type from calldata.
|
/// @returns a function that reads a value type from calldata.
|
||||||
|
@ -38,9 +38,6 @@
|
|||||||
|
|
||||||
#include <liblangutil/SourceReferenceFormatter.h>
|
#include <liblangutil/SourceReferenceFormatter.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -172,24 +169,24 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
|||||||
string functionName = m_context.functionName(_function);
|
string functionName = m_context.functionName(_function);
|
||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
function <functionName>(<params>) <returns> {
|
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
|
||||||
<initReturnVariables>
|
<initReturnVariables>
|
||||||
<body>
|
<body>
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
t("functionName", functionName);
|
t("functionName", functionName);
|
||||||
string params;
|
vector<string> params;
|
||||||
for (auto const& varDecl: _function.parameters())
|
for (auto const& varDecl: _function.parameters())
|
||||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||||
t("params", params);
|
t("params", joinHumanReadable(params));
|
||||||
string retParams;
|
vector<string> retParams;
|
||||||
string retInit;
|
string retInit;
|
||||||
for (auto const& varDecl: _function.returnParameters())
|
for (auto const& varDecl: _function.returnParameters())
|
||||||
{
|
{
|
||||||
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
retParams += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||||
retInit += generateInitialAssignment(*varDecl);
|
retInit += generateInitialAssignment(*varDecl);
|
||||||
}
|
}
|
||||||
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
t("retParams", joinHumanReadable(retParams));
|
||||||
t("initReturnVariables", retInit);
|
t("initReturnVariables", retInit);
|
||||||
t("body", generate(_function.body()));
|
t("body", generate(_function.body()));
|
||||||
return t.render();
|
return t.render();
|
||||||
@ -202,11 +199,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
|
|
||||||
Type const* type = _varDecl.annotation().type;
|
Type const* type = _varDecl.annotation().type;
|
||||||
|
|
||||||
solAssert(!_varDecl.isConstant(), "");
|
|
||||||
solAssert(_varDecl.isStateVariable(), "");
|
solAssert(_varDecl.isStateVariable(), "");
|
||||||
|
|
||||||
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
|
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
|
||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||||
|
solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), "");
|
||||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
||||||
solAssert(slot_offset.second == 0, "");
|
solAssert(slot_offset.second == 0, "");
|
||||||
FunctionType funType(_varDecl);
|
FunctionType funType(_varDecl);
|
||||||
@ -268,6 +265,16 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
("id", to_string(_varDecl.id()))
|
("id", to_string(_varDecl.id()))
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
|
else if (_varDecl.isConstant())
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>() -> <ret> {
|
||||||
|
<ret> := <constantValueFunction>()
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
|
||||||
|
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
|
||||||
|
.render();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
||||||
@ -293,31 +300,35 @@ string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDec
|
|||||||
return generator.code();
|
return generator.code();
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstructorArguments(
|
pair<string, map<ContractDefinition const*, vector<string>>> IRGenerator::evaluateConstructorArguments(
|
||||||
ContractDefinition const& _contract
|
ContractDefinition const& _contract
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
map<ContractDefinition const*, string> constructorParams;
|
map<ContractDefinition const*, vector<string>> constructorParams;
|
||||||
vector<pair<ContractDefinition const*, std::vector<ASTPointer<Expression>>const *>> baseConstructorArguments;
|
vector<pair<ContractDefinition const*, std::vector<ASTPointer<Expression>>const *>> baseConstructorArguments;
|
||||||
|
|
||||||
for (ASTPointer<InheritanceSpecifier> const& base: _contract.baseContracts())
|
for (ASTPointer<InheritanceSpecifier> const& base: _contract.baseContracts())
|
||||||
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
|
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
|
||||||
base->name().annotation().referencedDeclaration
|
base->name().annotation().referencedDeclaration
|
||||||
)->constructor(); baseConstructor && base->arguments())
|
)->constructor(); baseConstructor && base->arguments())
|
||||||
baseConstructorArguments.emplace_back(
|
baseConstructorArguments.emplace_back(
|
||||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||||
base->arguments()
|
base->arguments()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FunctionDefinition const* constructor = _contract.constructor())
|
if (FunctionDefinition const* constructor = _contract.constructor())
|
||||||
for (auto const& modifier: constructor->modifiers())
|
for (ASTPointer<ModifierInvocation> const& modifier: constructor->modifiers())
|
||||||
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
|
if (auto const* baseContract = dynamic_cast<ContractDefinition const*>(
|
||||||
modifier->name()->annotation().referencedDeclaration
|
modifier->name()->annotation().referencedDeclaration
|
||||||
)->constructor(); baseConstructor && modifier->arguments())
|
))
|
||||||
baseConstructorArguments.emplace_back(
|
if (
|
||||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
FunctionDefinition const* baseConstructor = baseContract->constructor();
|
||||||
modifier->arguments()
|
baseConstructor && modifier->arguments()
|
||||||
);
|
)
|
||||||
|
baseConstructorArguments.emplace_back(
|
||||||
|
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||||
|
modifier->arguments()
|
||||||
|
);
|
||||||
|
|
||||||
IRGeneratorForStatements generator{m_context, m_utils};
|
IRGeneratorForStatements generator{m_context, m_utils};
|
||||||
for (auto&& [baseContract, arguments]: baseConstructorArguments)
|
for (auto&& [baseContract, arguments]: baseConstructorArguments)
|
||||||
@ -327,11 +338,11 @@ pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstr
|
|||||||
{
|
{
|
||||||
vector<string> params;
|
vector<string> params;
|
||||||
for (size_t i = 0; i < arguments->size(); ++i)
|
for (size_t i = 0; i < arguments->size(); ++i)
|
||||||
params.emplace_back(generator.evaluateExpression(
|
params += generator.evaluateExpression(
|
||||||
*(arguments->at(i)),
|
*(arguments->at(i)),
|
||||||
*(baseContract->constructor()->parameters()[i]->type())
|
*(baseContract->constructor()->parameters()[i]->type())
|
||||||
).commaSeparatedList());
|
).stackSlots();
|
||||||
constructorParams[baseContract] = joinHumanReadable(params);
|
constructorParams[baseContract] = std::move(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,16 +363,16 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
|
|||||||
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
|
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
auto listAllParams = [&](
|
auto listAllParams = [&](
|
||||||
map<ContractDefinition const*, string> const& baseParams) -> string
|
map<ContractDefinition const*, vector<string>> const& baseParams) -> vector<string>
|
||||||
{
|
{
|
||||||
vector<string> params;
|
vector<string> params;
|
||||||
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
||||||
if (baseParams.count(contract))
|
if (baseParams.count(contract))
|
||||||
params.emplace_back(baseParams.at(contract));
|
params += baseParams.at(contract);
|
||||||
return joinHumanReadable(params);
|
return params;
|
||||||
};
|
};
|
||||||
|
|
||||||
map<ContractDefinition const*, string> baseConstructorParams;
|
map<ContractDefinition const*, vector<string>> baseConstructorParams;
|
||||||
for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i)
|
for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i)
|
||||||
{
|
{
|
||||||
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
|
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
|
||||||
@ -376,16 +387,16 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
|||||||
<userDefinedConstructorBody>
|
<userDefinedConstructorBody>
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
string params;
|
vector<string> params;
|
||||||
if (contract->constructor())
|
if (contract->constructor())
|
||||||
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
|
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
|
||||||
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
|
params += m_context.addLocalVariable(*varDecl).stackSlots();
|
||||||
t("params", params);
|
t("params", joinHumanReadable(params));
|
||||||
string baseParamsString = listAllParams(baseConstructorParams);
|
vector<string> baseParams = listAllParams(baseConstructorParams);
|
||||||
t("baseParams", baseParamsString);
|
t("baseParams", joinHumanReadable(baseParams));
|
||||||
t("comma", !params.empty() && !baseParamsString.empty() ? ", " : "");
|
t("comma", !params.empty() && !baseParams.empty() ? ", " : "");
|
||||||
t("functionName", implicitConstructorName(*contract));
|
t("functionName", implicitConstructorName(*contract));
|
||||||
pair<string, map<ContractDefinition const*, string>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
pair<string, map<ContractDefinition const*, vector<string>>> evaluatedArgs = evaluateConstructorArguments(*contract);
|
||||||
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
|
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
|
||||||
t("evalBaseArguments", evaluatedArgs.first);
|
t("evalBaseArguments", evaluatedArgs.first);
|
||||||
if (i < _contract.annotation().linearizedBaseContracts.size() - 1)
|
if (i < _contract.annotation().linearizedBaseContracts.size() - 1)
|
||||||
@ -393,7 +404,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
|||||||
t("hasNextConstructor", true);
|
t("hasNextConstructor", true);
|
||||||
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
|
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
|
||||||
t("nextConstructor", implicitConstructorName(*nextContract));
|
t("nextConstructor", implicitConstructorName(*nextContract));
|
||||||
t("nextParams", listAllParams(baseConstructorParams));
|
t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
t("hasNextConstructor", false);
|
t("hasNextConstructor", false);
|
||||||
@ -468,10 +479,10 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
{
|
{
|
||||||
// <functionName>
|
// <functionName>
|
||||||
<callValueCheck>
|
<callValueCheck>
|
||||||
<assignToParams> <abiDecode>(4, calldatasize())
|
<?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
|
||||||
<assignToRetParams> <function>(<params>)
|
<?+retParams>let <retParams> := </+retParams> <function>(<params>)
|
||||||
let memPos := <allocate>(0)
|
let memPos := <allocate>(0)
|
||||||
let memEnd := <abiEncode>(memPos <comma> <retParams>)
|
let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
|
||||||
return(memPos, sub(memEnd, memPos))
|
return(memPos, sub(memEnd, memPos))
|
||||||
}
|
}
|
||||||
</cases>
|
</cases>
|
||||||
@ -493,13 +504,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack();
|
unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack();
|
||||||
unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack();
|
unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack();
|
||||||
templ["assignToParams"] = paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := ";
|
|
||||||
templ["assignToRetParams"] = retVars == 0 ? "" : "let " + suffixedVariableNameList("ret_", 0, retVars) + " := ";
|
|
||||||
|
|
||||||
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
|
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
|
||||||
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
|
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
|
||||||
templ["params"] = suffixedVariableNameList("param_", 0, paramVars);
|
templ["params"] = suffixedVariableNameList("param_", 0, paramVars);
|
||||||
templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0);
|
templ["retParams"] = suffixedVariableNameList("ret_", 0, retVars);
|
||||||
|
|
||||||
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
|
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
|
||||||
templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
|
templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
|
||||||
@ -510,7 +519,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
|
|
||||||
templ["allocate"] = m_utils.allocationFunction();
|
templ["allocate"] = m_utils.allocationFunction();
|
||||||
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
|
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
|
||||||
templ["comma"] = retVars == 0 ? "" : ", ";
|
|
||||||
}
|
}
|
||||||
t("cases", functions);
|
t("cases", functions);
|
||||||
if (FunctionDefinition const* fallback = _contract.fallbackFunction())
|
if (FunctionDefinition const* fallback = _contract.fallbackFunction())
|
||||||
|
@ -81,9 +81,8 @@ private:
|
|||||||
/// Evaluates constructor's arguments for all base contracts (listed in inheritance specifiers) of
|
/// Evaluates constructor's arguments for all base contracts (listed in inheritance specifiers) of
|
||||||
/// @a _contract
|
/// @a _contract
|
||||||
/// @returns Pair of expressions needed to evaluate params and list of parameters in a map contract -> params
|
/// @returns Pair of expressions needed to evaluate params and list of parameters in a map contract -> params
|
||||||
std::pair<std::string, std::map<ContractDefinition const*, std::string>> evaluateConstructorArguments(
|
std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::string>>>
|
||||||
ContractDefinition const& _contract
|
evaluateConstructorArguments(ContractDefinition const& _contract);
|
||||||
);
|
|
||||||
|
|
||||||
/// Initializes state variables of
|
/// Initializes state variables of
|
||||||
/// @a _contract
|
/// @a _contract
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
#include <libsolutil/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <boost/range/adaptor/transformed.hpp>
|
#include <boost/range/adaptor/transformed.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -117,10 +116,12 @@ struct CopyTranslate: public yul::ASTCopier
|
|||||||
reference.isOffset == false && reference.isSlot == false,
|
reference.isOffset == false && reference.isSlot == false,
|
||||||
"Should not be called for offset/slot"
|
"Should not be called for offset/slot"
|
||||||
);
|
);
|
||||||
|
auto const& var = m_context.localVariable(*varDecl);
|
||||||
|
solAssert(var.type().sizeOnStack() == 1, "");
|
||||||
|
|
||||||
return yul::Identifier{
|
return yul::Identifier{
|
||||||
_identifier.location,
|
_identifier.location,
|
||||||
yul::YulString{m_context.localVariable(*varDecl).name()}
|
yul::YulString{var.commaSeparatedList()}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +179,28 @@ IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expre
|
|||||||
return variable;
|
return variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
|
||||||
|
{
|
||||||
|
string functionName = "constant_" + _constant.name() + "_" + to_string(_constant.id());
|
||||||
|
return m_context.functionCollector().createFunction(functionName, [&] {
|
||||||
|
Whiskers templ(R"(
|
||||||
|
function <functionName>() -> <ret> {
|
||||||
|
<code>
|
||||||
|
<ret> := <value>
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", functionName);
|
||||||
|
IRGeneratorForStatements generator(m_context, m_utils);
|
||||||
|
solAssert(_constant.value(), "");
|
||||||
|
Type const& constantType = *_constant.type();
|
||||||
|
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
|
||||||
|
templ("code", generator.code());
|
||||||
|
templ("ret", IRVariable("ret", constantType).commaSeparatedList());
|
||||||
|
|
||||||
|
return templ.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
|
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
|
||||||
{
|
{
|
||||||
if (Expression const* expression = _varDeclStatement.initialValue())
|
if (Expression const* expression = _varDeclStatement.initialValue())
|
||||||
@ -230,29 +253,53 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
|
|||||||
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
_assignment.rightHandSide().accept(*this);
|
_assignment.rightHandSide().accept(*this);
|
||||||
Type const* intermediateType = type(_assignment.rightHandSide()).closestTemporaryType(
|
|
||||||
&type(_assignment.leftHandSide())
|
|
||||||
);
|
|
||||||
IRVariable value = convert(_assignment.rightHandSide(), *intermediateType);
|
|
||||||
|
|
||||||
|
Token assignmentOperator = _assignment.assignmentOperator();
|
||||||
|
Token binaryOperator =
|
||||||
|
assignmentOperator == Token::Assign ?
|
||||||
|
assignmentOperator :
|
||||||
|
TokenTraits::AssignmentToBinaryOp(assignmentOperator);
|
||||||
|
|
||||||
|
Type const* rightIntermediateType =
|
||||||
|
TokenTraits::isShiftOp(binaryOperator) ?
|
||||||
|
type(_assignment.rightHandSide()).mobileType() :
|
||||||
|
type(_assignment.rightHandSide()).closestTemporaryType(
|
||||||
|
&type(_assignment.leftHandSide())
|
||||||
|
);
|
||||||
|
solAssert(rightIntermediateType, "");
|
||||||
|
IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType);
|
||||||
_assignment.leftHandSide().accept(*this);
|
_assignment.leftHandSide().accept(*this);
|
||||||
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||||
|
|
||||||
if (_assignment.assignmentOperator() != Token::Assign)
|
if (assignmentOperator != Token::Assign)
|
||||||
{
|
{
|
||||||
solAssert(type(_assignment.leftHandSide()) == *intermediateType, "");
|
solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
|
||||||
solAssert(intermediateType->isValueType(), "Compound operators only available for value types.");
|
solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types.");
|
||||||
|
|
||||||
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
|
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
|
||||||
m_code << value.name() << " := " << binaryOperation(
|
if (TokenTraits::isShiftOp(binaryOperator))
|
||||||
TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()),
|
{
|
||||||
*intermediateType,
|
solAssert(type(_assignment) == leftIntermediate.type(), "");
|
||||||
leftIntermediate.name(),
|
solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
|
||||||
value.name()
|
define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value);
|
||||||
);
|
|
||||||
|
writeToLValue(*m_currentLValue, IRVariable(_assignment));
|
||||||
|
m_currentLValue.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(type(_assignment.leftHandSide()) == *rightIntermediateType, "");
|
||||||
|
m_code << value.name() << " := " << binaryOperation(
|
||||||
|
binaryOperator,
|
||||||
|
*rightIntermediateType,
|
||||||
|
leftIntermediate.name(),
|
||||||
|
value.name()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeToLValue(*m_currentLValue, value);
|
writeToLValue(*m_currentLValue, value);
|
||||||
|
|
||||||
m_currentLValue.reset();
|
m_currentLValue.reset();
|
||||||
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
|
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
|
||||||
define(_assignment, value);
|
define(_assignment, value);
|
||||||
@ -477,12 +524,16 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (commonType->category() == Type::Category::RationalNumber)
|
||||||
|
{
|
||||||
|
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
|
||||||
|
return false; // skip sub-expressions
|
||||||
|
}
|
||||||
|
|
||||||
_binOp.leftExpression().accept(*this);
|
_binOp.leftExpression().accept(*this);
|
||||||
_binOp.rightExpression().accept(*this);
|
_binOp.rightExpression().accept(*this);
|
||||||
|
|
||||||
if (commonType->category() == Type::Category::RationalNumber)
|
if (TokenTraits::isCompareOp(op))
|
||||||
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
|
|
||||||
else if (TokenTraits::isCompareOp(op))
|
|
||||||
{
|
{
|
||||||
if (auto type = dynamic_cast<FunctionType const*>(commonType))
|
if (auto type = dynamic_cast<FunctionType const*>(commonType))
|
||||||
{
|
{
|
||||||
@ -496,9 +547,9 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
isSigned = type->isSigned();
|
isSigned = type->isSigned();
|
||||||
|
|
||||||
string args =
|
string args =
|
||||||
expressionAsType(_binOp.leftExpression(), *commonType) +
|
expressionAsType(_binOp.leftExpression(), *commonType, true) +
|
||||||
", " +
|
", " +
|
||||||
expressionAsType(_binOp.rightExpression(), *commonType);
|
expressionAsType(_binOp.rightExpression(), *commonType, true);
|
||||||
|
|
||||||
string expr;
|
string expr;
|
||||||
if (op == Token::Equal)
|
if (op == Token::Equal)
|
||||||
@ -517,6 +568,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
|||||||
solAssert(false, "Unknown comparison operator.");
|
solAssert(false, "Unknown comparison operator.");
|
||||||
define(_binOp) << expr << "\n";
|
define(_binOp) << expr << "\n";
|
||||||
}
|
}
|
||||||
|
else if (TokenTraits::isShiftOp(op))
|
||||||
|
{
|
||||||
|
IRVariable left = convert(_binOp.leftExpression(), *commonType);
|
||||||
|
IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType());
|
||||||
|
define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string left = expressionAsType(_binOp.leftExpression(), *commonType);
|
string left = expressionAsType(_binOp.leftExpression(), *commonType);
|
||||||
@ -584,13 +641,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
break;
|
break;
|
||||||
case FunctionType::Kind::Internal:
|
case FunctionType::Kind::Internal:
|
||||||
{
|
{
|
||||||
vector<string> args;
|
|
||||||
for (size_t i = 0; i < arguments.size(); ++i)
|
|
||||||
if (functionType->takesArbitraryParameters())
|
|
||||||
args.emplace_back(IRVariable(*arguments[i]).commaSeparatedList());
|
|
||||||
else
|
|
||||||
args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList());
|
|
||||||
|
|
||||||
optional<FunctionDefinition const*> functionDef;
|
optional<FunctionDefinition const*> functionDef;
|
||||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
||||||
{
|
{
|
||||||
@ -628,6 +678,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
solAssert(functionDef.has_value(), "");
|
solAssert(functionDef.has_value(), "");
|
||||||
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
|
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
|
||||||
|
|
||||||
|
vector<string> args;
|
||||||
|
for (size_t i = 0; i < arguments.size(); ++i)
|
||||||
|
if (functionType->takesArbitraryParameters())
|
||||||
|
args += IRVariable(*arguments[i]).stackSlots();
|
||||||
|
else
|
||||||
|
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
|
||||||
|
|
||||||
if (functionDef.value() != nullptr)
|
if (functionDef.value() != nullptr)
|
||||||
define(_functionCall) <<
|
define(_functionCall) <<
|
||||||
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
|
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
|
||||||
@ -649,10 +706,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
case FunctionType::Kind::External:
|
case FunctionType::Kind::External:
|
||||||
case FunctionType::Kind::DelegateCall:
|
case FunctionType::Kind::DelegateCall:
|
||||||
|
appendExternalFunctionCall(_functionCall, arguments);
|
||||||
|
break;
|
||||||
case FunctionType::Kind::BareCall:
|
case FunctionType::Kind::BareCall:
|
||||||
case FunctionType::Kind::BareDelegateCall:
|
case FunctionType::Kind::BareDelegateCall:
|
||||||
case FunctionType::Kind::BareStaticCall:
|
case FunctionType::Kind::BareStaticCall:
|
||||||
appendExternalFunctionCall(_functionCall, arguments);
|
appendBareCall(_functionCall, arguments);
|
||||||
break;
|
break;
|
||||||
case FunctionType::Kind::BareCallCode:
|
case FunctionType::Kind::BareCallCode:
|
||||||
solAssert(false, "Callcode has been removed.");
|
solAssert(false, "Callcode has been removed.");
|
||||||
@ -663,7 +722,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||||
|
|
||||||
vector<IRVariable> indexedArgs;
|
vector<IRVariable> indexedArgs;
|
||||||
string nonIndexedArgs;
|
vector<string> nonIndexedArgs;
|
||||||
TypePointers nonIndexedArgTypes;
|
TypePointers nonIndexedArgTypes;
|
||||||
TypePointers nonIndexedParamTypes;
|
TypePointers nonIndexedParamTypes;
|
||||||
if (!event.isAnonymous())
|
if (!event.isAnonymous())
|
||||||
@ -686,10 +745,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string vars = IRVariable(arg).commaSeparatedList();
|
nonIndexedArgs += IRVariable(arg).stackSlots();
|
||||||
if (!vars.empty())
|
|
||||||
// In reverse because abi_encode expects it like that.
|
|
||||||
nonIndexedArgs = ", " + move(vars) + nonIndexedArgs;
|
|
||||||
nonIndexedArgTypes.push_back(arg.annotation().type);
|
nonIndexedArgTypes.push_back(arg.annotation().type);
|
||||||
nonIndexedParamTypes.push_back(paramTypes[i]);
|
nonIndexedParamTypes.push_back(paramTypes[i]);
|
||||||
}
|
}
|
||||||
@ -704,7 +760,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
templ("end", m_context.newYulVariable());
|
templ("end", m_context.newYulVariable());
|
||||||
templ("freeMemory", freeMemory());
|
templ("freeMemory", freeMemory());
|
||||||
templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
|
templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
|
||||||
templ("nonIndexedArgs", nonIndexedArgs);
|
templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs));
|
||||||
templ("log", "log" + to_string(indexedArgs.size()));
|
templ("log", "log" + to_string(indexedArgs.size()));
|
||||||
templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) {
|
templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) {
|
||||||
return _arg.commaSeparatedList();
|
return _arg.commaSeparatedList();
|
||||||
@ -731,13 +787,134 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::ABIEncode:
|
||||||
|
case FunctionType::Kind::ABIEncodePacked:
|
||||||
|
case FunctionType::Kind::ABIEncodeWithSelector:
|
||||||
|
case FunctionType::Kind::ABIEncodeWithSignature:
|
||||||
|
{
|
||||||
|
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
|
||||||
|
solAssert(functionType->padArguments() != isPacked, "");
|
||||||
|
bool const hasSelectorOrSignature =
|
||||||
|
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
|
||||||
|
functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature;
|
||||||
|
|
||||||
|
TypePointers argumentTypes;
|
||||||
|
TypePointers targetTypes;
|
||||||
|
vector<string> argumentVars;
|
||||||
|
for (size_t i = 0; i < arguments.size(); ++i)
|
||||||
|
{
|
||||||
|
// ignore selector
|
||||||
|
if (hasSelectorOrSignature && i == 0)
|
||||||
|
continue;
|
||||||
|
argumentTypes.emplace_back(&type(*arguments[i]));
|
||||||
|
targetTypes.emplace_back(type(*arguments[i]).fullEncodingType(false, true, isPacked));
|
||||||
|
argumentVars += IRVariable(*arguments[i]).stackSlots();
|
||||||
|
}
|
||||||
|
|
||||||
|
string selector;
|
||||||
|
if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
|
||||||
|
{
|
||||||
|
// hash the signature
|
||||||
|
Type const& selectorType = type(*arguments.front());
|
||||||
|
if (auto const* stringType = dynamic_cast<StringLiteralType const*>(&selectorType))
|
||||||
|
{
|
||||||
|
FixedHash<4> hash(keccak256(stringType->value()));
|
||||||
|
selector = formatNumber(u256(FixedHash<4>::Arith(hash)) << (256 - 32));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Used to reset the free memory pointer later.
|
||||||
|
string freeMemoryPre = m_context.newYulVariable();
|
||||||
|
m_code << "let " << freeMemoryPre << " := " << freeMemory() << "\n";
|
||||||
|
IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory());
|
||||||
|
IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32));
|
||||||
|
|
||||||
|
define(hashVariable) <<
|
||||||
|
"keccak256(" <<
|
||||||
|
m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()) <<
|
||||||
|
"(" <<
|
||||||
|
array.commaSeparatedList() <<
|
||||||
|
"), " <<
|
||||||
|
m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) <<
|
||||||
|
"(" <<
|
||||||
|
array.commaSeparatedList() <<
|
||||||
|
"))\n";
|
||||||
|
IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4));
|
||||||
|
define(selectorVariable, hashVariable);
|
||||||
|
m_code << "mstore(" << to_string(CompilerUtils::freeMemoryPointer) << ", " << freeMemoryPre << ")\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector)
|
||||||
|
selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name();
|
||||||
|
|
||||||
|
Whiskers templ(R"(
|
||||||
|
let <data> := <allocateTemporary>()
|
||||||
|
let <mpos> := add(<data>, 0x20)
|
||||||
|
<?+selector>
|
||||||
|
mstore(<mpos>, <selector>)
|
||||||
|
<mpos> := add(<mpos>, 4)
|
||||||
|
</+selector>
|
||||||
|
let <mend> := <encode>(<mpos><arguments>)
|
||||||
|
mstore(<data>, sub(<mend>, add(<data>, 0x20)))
|
||||||
|
mstore(<freeMemPtr>, <roundUp>(<mend>))
|
||||||
|
)");
|
||||||
|
templ("data", IRVariable(_functionCall).part("mpos").name());
|
||||||
|
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
|
||||||
|
templ("mpos", m_context.newYulVariable());
|
||||||
|
templ("mend", m_context.newYulVariable());
|
||||||
|
templ("selector", selector);
|
||||||
|
templ("encode",
|
||||||
|
isPacked ?
|
||||||
|
m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) :
|
||||||
|
m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false)
|
||||||
|
);
|
||||||
|
templ("arguments", joinHumanReadablePrefixed(argumentVars));
|
||||||
|
templ("freeMemPtr", to_string(CompilerUtils::freeMemoryPointer));
|
||||||
|
templ("roundUp", m_utils.roundUpFunction());
|
||||||
|
|
||||||
|
m_code << templ.render();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case FunctionType::Kind::Revert:
|
case FunctionType::Kind::Revert:
|
||||||
{
|
{
|
||||||
solAssert(arguments.size() == parameterTypes.size(), "");
|
solAssert(arguments.size() == parameterTypes.size(), "");
|
||||||
if (arguments.empty())
|
if (arguments.empty())
|
||||||
m_code << "revert(0, 0)\n";
|
m_code << "revert(0, 0)\n";
|
||||||
else
|
else
|
||||||
solUnimplementedAssert(false, "");
|
{
|
||||||
|
solAssert(arguments.size() == 1, "");
|
||||||
|
|
||||||
|
if (m_context.revertStrings() == RevertStrings::Strip)
|
||||||
|
m_code << "revert(0, 0)\n";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(type(*arguments.front()).isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),"");
|
||||||
|
|
||||||
|
Whiskers templ(R"({
|
||||||
|
let <pos> := <allocateTemporary>()
|
||||||
|
mstore(<pos>, <hash>)
|
||||||
|
let <end> := <encode>(add(<pos>, 4) <argumentVars>)
|
||||||
|
revert(<pos>, sub(<end>, <pos>))
|
||||||
|
})");
|
||||||
|
templ("pos", m_context.newYulVariable());
|
||||||
|
templ("end", m_context.newYulVariable());
|
||||||
|
templ(
|
||||||
|
"hash",
|
||||||
|
(u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str()
|
||||||
|
);
|
||||||
|
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
|
||||||
|
templ(
|
||||||
|
"argumentVars",
|
||||||
|
joinHumanReadablePrefixed(IRVariable{*arguments.front()}.stackSlots())
|
||||||
|
);
|
||||||
|
templ("encode", m_context.abiFunctions().tupleEncoder(
|
||||||
|
{&type(*arguments.front())},
|
||||||
|
{TypeProvider::stringMemory()}
|
||||||
|
));
|
||||||
|
|
||||||
|
m_code << templ.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -760,6 +937,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
solAssert(arguments.size() == 1, "");
|
solAssert(arguments.size() == 1, "");
|
||||||
|
|
||||||
ArrayType const* arrayType = TypeProvider::bytesMemory();
|
ArrayType const* arrayType = TypeProvider::bytesMemory();
|
||||||
|
|
||||||
auto array = convert(*arguments[0], *arrayType);
|
auto array = convert(*arguments[0], *arrayType);
|
||||||
|
|
||||||
define(_functionCall) <<
|
define(_functionCall) <<
|
||||||
@ -905,11 +1083,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
);
|
);
|
||||||
|
|
||||||
TypePointers argumentTypes;
|
TypePointers argumentTypes;
|
||||||
string constructorParams;
|
vector<string> constructorParams;
|
||||||
for (ASTPointer<Expression const> const& arg: arguments)
|
for (ASTPointer<Expression const> const& arg: arguments)
|
||||||
{
|
{
|
||||||
argumentTypes.push_back(arg->annotation().type);
|
argumentTypes.push_back(arg->annotation().type);
|
||||||
constructorParams += ", " + IRVariable{*arg}.commaSeparatedList();
|
constructorParams += IRVariable{*arg}.stackSlots();
|
||||||
}
|
}
|
||||||
|
|
||||||
ContractDefinition const* contract =
|
ContractDefinition const* contract =
|
||||||
@ -935,9 +1113,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
|
||||||
t("object", m_context.creationObjectName(*contract));
|
t("object", m_context.creationObjectName(*contract));
|
||||||
t("abiEncode",
|
t("abiEncode",
|
||||||
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false)
|
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
|
||||||
);
|
);
|
||||||
t("constructorParams", constructorParams);
|
t("constructorParams", joinHumanReadablePrefixed(constructorParams));
|
||||||
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||||
t("saltSet", functionType->saltSet());
|
t("saltSet", functionType->saltSet());
|
||||||
if (functionType->saltSet())
|
if (functionType->saltSet())
|
||||||
@ -1126,7 +1304,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
solAssert(false, "Blockhash has been removed.");
|
solAssert(false, "Blockhash has been removed.");
|
||||||
else if (member == "creationCode" || member == "runtimeCode")
|
else if (member == "creationCode" || member == "runtimeCode")
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(false, "");
|
solUnimplementedAssert(member != "runtimeCode", "");
|
||||||
|
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
|
||||||
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
|
||||||
|
m_context.subObjectsCreated().insert(&contract);
|
||||||
|
m_code << Whiskers(R"(
|
||||||
|
let <size> := datasize("<objectName>")
|
||||||
|
let <result> := <allocationFunction>(add(<size>, 32))
|
||||||
|
mstore(<result>, <size>)
|
||||||
|
datacopy(add(<result>, 32), dataoffset("<objectName>"), <size>)
|
||||||
|
)")
|
||||||
|
("allocationFunction", m_utils.allocationFunction())
|
||||||
|
("size", m_context.newYulVariable())
|
||||||
|
("objectName", m_context.creationObjectName(contract))
|
||||||
|
("result", IRVariable(_memberAccess).commaSeparatedList()).render();
|
||||||
}
|
}
|
||||||
else if (member == "name")
|
else if (member == "name")
|
||||||
{
|
{
|
||||||
@ -1141,6 +1332,16 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
result ^= fromBigEndian<uint64_t>(function.first.ref());
|
||||||
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n";
|
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n";
|
||||||
}
|
}
|
||||||
|
else if (member == "min" || member == "max")
|
||||||
|
{
|
||||||
|
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
|
||||||
|
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
|
||||||
|
|
||||||
|
if (member == "min")
|
||||||
|
define(_memberAccess) << formatNumber(integerType->min()) << "\n";
|
||||||
|
else
|
||||||
|
define(_memberAccess) << formatNumber(integerType->max()) << "\n";
|
||||||
|
}
|
||||||
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
|
||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
@ -1312,14 +1513,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
solAssert(keyType.sizeOnStack() <= 1, "");
|
solAssert(keyType.sizeOnStack() <= 1, "");
|
||||||
|
|
||||||
string slot = m_context.newYulVariable();
|
string slot = m_context.newYulVariable();
|
||||||
Whiskers templ("let <slot> := <indexAccess>(<base> <key>)\n");
|
Whiskers templ("let <slot> := <indexAccess>(<base><?+key>,<key></+key>)\n");
|
||||||
templ("slot", slot);
|
templ("slot", slot);
|
||||||
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
|
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
|
||||||
templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
|
templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
|
||||||
if (keyType.sizeOnStack() == 0)
|
templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
|
||||||
templ("key", "");
|
|
||||||
else
|
|
||||||
templ("key", ", " + IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
|
|
||||||
m_code << templ.render();
|
m_code << templ.render();
|
||||||
setLValue(_indexAccess, IRLValue{
|
setLValue(_indexAccess, IRLValue{
|
||||||
*_indexAccess.annotation().type,
|
*_indexAccess.annotation().type,
|
||||||
@ -1542,11 +1740,9 @@ void IRGeneratorForStatements::handleVariableReference(
|
|||||||
Expression const& _referencingExpression
|
Expression const& _referencingExpression
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// TODO for the constant case, we have to be careful:
|
if (_variable.isStateVariable() && _variable.isConstant())
|
||||||
// If the value is visited twice, `defineExpression` is called twice on
|
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
|
||||||
// the same expression.
|
else if (_variable.isStateVariable() && _variable.immutable())
|
||||||
solUnimplementedAssert(!_variable.isConstant(), "");
|
|
||||||
if (_variable.isStateVariable() && _variable.immutable())
|
|
||||||
setLValue(_referencingExpression, IRLValue{
|
setLValue(_referencingExpression, IRLValue{
|
||||||
*_variable.annotation().type,
|
*_variable.annotation().type,
|
||||||
IRLValue::Immutable{&_variable}
|
IRLValue::Immutable{&_variable}
|
||||||
@ -1574,18 +1770,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||||
solAssert(
|
solAssert(!funType.takesArbitraryParameters(), "");
|
||||||
funType.takesArbitraryParameters() ||
|
solAssert(_arguments.size() == funType.parameterTypes().size(), "");
|
||||||
_arguments.size() == funType.parameterTypes().size(), ""
|
solAssert(!funType.isBareCall(), "");
|
||||||
);
|
|
||||||
solUnimplementedAssert(!funType.bound(), "");
|
|
||||||
FunctionType::Kind const funKind = funType.kind();
|
FunctionType::Kind const funKind = funType.kind();
|
||||||
|
|
||||||
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
solAssert(
|
||||||
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall,
|
||||||
|
"Can only be used for regular external calls."
|
||||||
|
);
|
||||||
|
|
||||||
bool const isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
|
solUnimplementedAssert(!funType.bound(), "");
|
||||||
bool const useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
|
|
||||||
|
bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall;
|
||||||
|
bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
|
||||||
|
|
||||||
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
|
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
|
||||||
|
|
||||||
@ -1594,12 +1792,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
for (auto const& arg: _arguments)
|
for (auto const& arg: _arguments)
|
||||||
{
|
{
|
||||||
argumentTypes.emplace_back(&type(*arg));
|
argumentTypes.emplace_back(&type(*arg));
|
||||||
if (IRVariable(*arg).type().sizeOnStack() > 0)
|
argumentStrings += IRVariable(*arg).stackSlots();
|
||||||
argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList());
|
|
||||||
}
|
}
|
||||||
string argumentString = argumentStrings.empty() ? ""s : (", " + joinHumanReadable(argumentStrings));
|
|
||||||
|
|
||||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
|
||||||
|
|
||||||
if (!m_context.evmVersion().canOverchargeGasForCall())
|
if (!m_context.evmVersion().canOverchargeGasForCall())
|
||||||
{
|
{
|
||||||
@ -1611,33 +1806,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
|
m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
|
||||||
|
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
<?checkExistence>
|
if iszero(extcodesize(<address>)) { revert(0, 0) }
|
||||||
if iszero(extcodesize(<address>)) { revert(0, 0) }
|
|
||||||
</checkExistence>
|
|
||||||
|
|
||||||
// storage for arguments and returned data
|
// storage for arguments and returned data
|
||||||
let <pos> := <freeMemory>
|
let <pos> := <freeMemory>
|
||||||
<?bareCall>
|
mstore(<pos>, <shl28>(<funId>))
|
||||||
<!bareCall>
|
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
|
||||||
mstore(<pos>, <shl28>(<funId>))
|
|
||||||
</bareCall>
|
|
||||||
let <end> := <encodeArgs>(
|
|
||||||
<?bareCall>
|
|
||||||
<pos>
|
|
||||||
<!bareCall>
|
|
||||||
add(<pos>, 4)
|
|
||||||
</bareCall>
|
|
||||||
<argumentString>
|
|
||||||
)
|
|
||||||
|
|
||||||
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
|
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
|
||||||
<?noTryCall>
|
<?noTryCall>
|
||||||
if iszero(<success>) { <forwardingRevert>() }
|
if iszero(<success>) { <forwardingRevert>() }
|
||||||
</noTryCall>
|
</noTryCall>
|
||||||
<?hasRetVars> let <retVars> </hasRetVars>
|
<?+retVars> let <retVars> </+retVars>
|
||||||
if <success> {
|
if <success> {
|
||||||
<?dynamicReturnSize>
|
<?dynamicReturnSize>
|
||||||
// copy dynamic return data out
|
// copy dynamic return data out
|
||||||
@ -1648,12 +1829,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
|
mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
|
||||||
|
|
||||||
// decode return parameters from external try-call into retVars
|
// decode return parameters from external try-call into retVars
|
||||||
<?hasRetVars> <retVars> := </hasRetVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
templ("pos", m_context.newYulVariable());
|
templ("pos", m_context.newYulVariable());
|
||||||
templ("end", m_context.newYulVariable());
|
templ("end", m_context.newYulVariable());
|
||||||
templ("bareCall", funType.isBareCall());
|
|
||||||
if (_functionCall.annotation().tryCall)
|
if (_functionCall.annotation().tryCall)
|
||||||
templ("success", m_context.trySuccessConditionVariable(_functionCall));
|
templ("success", m_context.trySuccessConditionVariable(_functionCall));
|
||||||
else
|
else
|
||||||
@ -1661,17 +1841,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
templ("freeMemory", freeMemory());
|
templ("freeMemory", freeMemory());
|
||||||
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
|
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
|
||||||
|
|
||||||
if (!funType.isBareCall())
|
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
|
||||||
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
|
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||||
|
|
||||||
if (funKind == FunctionType::Kind::ECRecover)
|
|
||||||
templ("address", "1");
|
|
||||||
else if (funKind == FunctionType::Kind::SHA256)
|
|
||||||
templ("address", "2");
|
|
||||||
else if (funKind == FunctionType::Kind::RIPEMD160)
|
|
||||||
templ("address", "3");
|
|
||||||
else
|
|
||||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
|
||||||
|
|
||||||
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
|
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
|
||||||
// This ensures it can catch badly formatted input from external calls.
|
// This ensures it can catch badly formatted input from external calls.
|
||||||
@ -1684,39 +1855,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
|
|
||||||
string const retVars = IRVariable(_functionCall).commaSeparatedList();
|
string const retVars = IRVariable(_functionCall).commaSeparatedList();
|
||||||
templ("retVars", retVars);
|
templ("retVars", retVars);
|
||||||
templ("hasRetVars", !retVars.empty());
|
|
||||||
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
|
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
|
||||||
|
|
||||||
templ("roundUp", m_utils.roundUpFunction());
|
templ("roundUp", m_utils.roundUpFunction());
|
||||||
templ("abiDecode", abi.tupleDecoder(returnInfo.returnTypes, true));
|
templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
|
||||||
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
|
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
|
||||||
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||||
|
|
||||||
templ("noTryCall", !_functionCall.annotation().tryCall);
|
templ("noTryCall", !_functionCall.annotation().tryCall);
|
||||||
|
|
||||||
// If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place.
|
|
||||||
// Move arguments to memory, will not update the free memory pointer (but will update the memory
|
|
||||||
// pointer on the stack).
|
|
||||||
bool encodeInPlace = funType.takesArbitraryParameters() || funType.isBareCall();
|
|
||||||
if (funType.kind() == FunctionType::Kind::ECRecover)
|
|
||||||
// This would be the only combination of padding and in-place encoding,
|
|
||||||
// but all parameters of ecrecover are value types anyway.
|
|
||||||
encodeInPlace = false;
|
|
||||||
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
|
||||||
|
|
||||||
solUnimplementedAssert(encodeInPlace == !funType.padArguments(), "");
|
solAssert(funType.padArguments(), "");
|
||||||
if (encodeInPlace)
|
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
||||||
{
|
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
|
||||||
solUnimplementedAssert(!encodeForLibraryCall, "");
|
|
||||||
templ("encodeArgs", abi.tupleEncoderPacked(argumentTypes, funType.parameterTypes()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
|
|
||||||
templ("argumentString", argumentString);
|
|
||||||
|
|
||||||
// Output data will replace input data, unless we have ECRecover (then, output
|
|
||||||
// area will be 32 bytes just before input area).
|
|
||||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
|
||||||
|
|
||||||
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
|
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
|
||||||
solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
|
solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
|
||||||
@ -1724,10 +1876,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
templ("hasValue", !isDelegateCall && !useStaticCall);
|
templ("hasValue", !isDelegateCall && !useStaticCall);
|
||||||
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||||
|
|
||||||
// Check that the target contract exists (has code) for non-low-level calls.
|
|
||||||
bool checkExistence = (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall);
|
|
||||||
templ("checkExistence", checkExistence);
|
|
||||||
|
|
||||||
if (funType.gasSet())
|
if (funType.gasSet())
|
||||||
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
|
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
|
||||||
else if (m_context.evmVersion().canOverchargeGasForCall())
|
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||||
@ -1740,8 +1888,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||||
if (funType.valueSet())
|
if (funType.valueSet())
|
||||||
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
||||||
if (!checkExistence)
|
|
||||||
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
|
|
||||||
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||||
}
|
}
|
||||||
// Order is important here, STATICCALL might overlap with DELEGATECALL.
|
// Order is important here, STATICCALL might overlap with DELEGATECALL.
|
||||||
@ -1754,8 +1900,103 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
|||||||
|
|
||||||
templ("forwardingRevert", m_utils.forwardingRevertFunction());
|
templ("forwardingRevert", m_utils.forwardingRevertFunction());
|
||||||
|
|
||||||
solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, "");
|
m_code << templ.render();
|
||||||
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
|
}
|
||||||
|
|
||||||
|
void IRGeneratorForStatements::appendBareCall(
|
||||||
|
FunctionCall const& _functionCall,
|
||||||
|
vector<ASTPointer<Expression const>> const& _arguments
|
||||||
|
)
|
||||||
|
{
|
||||||
|
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
|
||||||
|
solAssert(
|
||||||
|
!funType.bound() &&
|
||||||
|
!funType.takesArbitraryParameters() &&
|
||||||
|
_arguments.size() == 1 &&
|
||||||
|
funType.parameterTypes().size() == 1, ""
|
||||||
|
);
|
||||||
|
FunctionType::Kind const funKind = funType.kind();
|
||||||
|
|
||||||
|
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
|
||||||
|
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
|
||||||
|
solAssert(
|
||||||
|
funKind == FunctionType::Kind::BareCall ||
|
||||||
|
funKind == FunctionType::Kind::BareDelegateCall ||
|
||||||
|
funKind == FunctionType::Kind::BareStaticCall, ""
|
||||||
|
);
|
||||||
|
|
||||||
|
solAssert(!_functionCall.annotation().tryCall, "");
|
||||||
|
Whiskers templ(R"(
|
||||||
|
<?needsEncoding>
|
||||||
|
let <pos> := mload(<freeMemoryPointer>)
|
||||||
|
let <length> := sub(<encode>(<pos> <?+arg>,</+arg> <arg>), <pos>)
|
||||||
|
<!needsEncoding>
|
||||||
|
let <pos> := add(<arg>, 0x20)
|
||||||
|
let <length> := mload(<arg>)
|
||||||
|
</needsEncoding>
|
||||||
|
|
||||||
|
let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0)
|
||||||
|
<?+returndataVar>
|
||||||
|
let <returndataVar> := <extractReturndataFunction>()
|
||||||
|
</+returndataVar>
|
||||||
|
)");
|
||||||
|
|
||||||
|
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
|
||||||
|
templ("pos", m_context.newYulVariable());
|
||||||
|
templ("length", m_context.newYulVariable());
|
||||||
|
|
||||||
|
templ("arg", IRVariable(*_arguments.front()).commaSeparatedList());
|
||||||
|
Type const& argType = type(*_arguments.front());
|
||||||
|
if (argType == *TypeProvider::bytesMemory() || argType == *TypeProvider::stringMemory())
|
||||||
|
templ("needsEncoding", false);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
templ("needsEncoding", true);
|
||||||
|
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
|
||||||
|
templ("encode", abi.tupleEncoderPacked({&argType}, {TypeProvider::bytesMemory()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
templ("success", IRVariable(_functionCall).tupleComponent(0).name());
|
||||||
|
if (IRVariable(_functionCall).tupleComponent(1).type().category() == Type::Category::InaccessibleDynamic)
|
||||||
|
templ("returndataVar", "");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
templ("returndataVar", IRVariable(_functionCall).tupleComponent(1).part("mpos").name());
|
||||||
|
templ("extractReturndataFunction", m_utils.extractReturndataFunction());
|
||||||
|
}
|
||||||
|
|
||||||
|
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
||||||
|
|
||||||
|
if (funKind == FunctionType::Kind::BareCall)
|
||||||
|
{
|
||||||
|
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
|
||||||
|
templ("call", "call");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall.");
|
||||||
|
templ("value", "");
|
||||||
|
if (funKind == FunctionType::Kind::BareStaticCall)
|
||||||
|
templ("call", "staticcall");
|
||||||
|
else
|
||||||
|
templ("call", "delegatecall");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (funType.gasSet())
|
||||||
|
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
|
||||||
|
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||||
|
// Send all gas (requires tangerine whistle EVM)
|
||||||
|
templ("gas", "gas()");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// send all gas except the amount needed to execute "SUB" and "CALL"
|
||||||
|
// @todo this retains too much gas for now, needs to be fine-tuned.
|
||||||
|
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||||
|
if (funType.valueSet())
|
||||||
|
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
||||||
|
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
|
||||||
|
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
m_code << templ.render();
|
m_code << templ.render();
|
||||||
}
|
}
|
||||||
@ -1777,11 +2018,16 @@ IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to)
|
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup)
|
||||||
{
|
{
|
||||||
IRVariable from(_expression);
|
IRVariable from(_expression);
|
||||||
if (from.type() == _to)
|
if (from.type() == _to)
|
||||||
return from.commaSeparatedList();
|
{
|
||||||
|
if (_forceCleanup)
|
||||||
|
return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")";
|
||||||
|
else
|
||||||
|
return from.commaSeparatedList();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
||||||
}
|
}
|
||||||
@ -1860,6 +2106,10 @@ string IRGeneratorForStatements::binaryOperation(
|
|||||||
string const& _right
|
string const& _right
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
solAssert(
|
||||||
|
!TokenTraits::isShiftOp(_operator),
|
||||||
|
"Have to use specific shift operation function for shifts."
|
||||||
|
);
|
||||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
||||||
{
|
{
|
||||||
string fun;
|
string fun;
|
||||||
@ -1903,6 +2153,31 @@ string IRGeneratorForStatements::binaryOperation(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string IRGeneratorForStatements::shiftOperation(
|
||||||
|
langutil::Token _operator,
|
||||||
|
IRVariable const& _value,
|
||||||
|
IRVariable const& _amountToShift
|
||||||
|
)
|
||||||
|
{
|
||||||
|
IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type());
|
||||||
|
solAssert(amountType, "");
|
||||||
|
|
||||||
|
solAssert(_operator == Token::SHL || _operator == Token::SAR, "");
|
||||||
|
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
<shift>(<value>, <amount>)
|
||||||
|
)")
|
||||||
|
("shift",
|
||||||
|
_operator == Token::SHL ?
|
||||||
|
m_utils.typedShiftLeftFunction(_value.type(), *amountType) :
|
||||||
|
m_utils.typedShiftRightFunction(_value.type(), *amountType)
|
||||||
|
)
|
||||||
|
("value", _value.name())
|
||||||
|
("amount", _amountToShift.name())
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
|
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
|
||||||
{
|
{
|
||||||
langutil::Token const op = _binOp.getOperator();
|
langutil::Token const op = _binOp.getOperator();
|
||||||
@ -2021,13 +2296,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
|||||||
")\n";
|
")\n";
|
||||||
},
|
},
|
||||||
[&](IRLValue::Memory const& _memory) {
|
[&](IRLValue::Memory const& _memory) {
|
||||||
if (_memory.byteArrayElement)
|
if (_lvalue.type.isValueType())
|
||||||
define(result) <<
|
|
||||||
m_utils.cleanupFunction(_lvalue.type) <<
|
|
||||||
"(mload(" <<
|
|
||||||
_memory.address <<
|
|
||||||
"))\n";
|
|
||||||
else if (_lvalue.type.isValueType())
|
|
||||||
define(result) <<
|
define(result) <<
|
||||||
m_utils.readFromMemory(_lvalue.type) <<
|
m_utils.readFromMemory(_lvalue.type) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
|
@ -54,6 +54,10 @@ public:
|
|||||||
/// Calculates expression's value and returns variable where it was stored
|
/// Calculates expression's value and returns variable where it was stored
|
||||||
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
|
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
|
||||||
|
|
||||||
|
/// @returns the name of a function that computes the value of the given constant
|
||||||
|
/// and also generates the function.
|
||||||
|
std::string constantValueFunction(VariableDeclaration const& _constant);
|
||||||
|
|
||||||
void endVisit(VariableDeclarationStatement const& _variableDeclaration) override;
|
void endVisit(VariableDeclarationStatement const& _variableDeclaration) override;
|
||||||
bool visit(Conditional const& _conditional) override;
|
bool visit(Conditional const& _conditional) override;
|
||||||
bool visit(Assignment const& _assignment) override;
|
bool visit(Assignment const& _assignment) override;
|
||||||
@ -102,6 +106,13 @@ private:
|
|||||||
std::vector<ASTPointer<Expression const>> const& _arguments
|
std::vector<ASTPointer<Expression const>> const& _arguments
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Appends code for .call / .delegatecall / .staticcall.
|
||||||
|
/// All involved expressions have already been visited.
|
||||||
|
void appendBareCall(
|
||||||
|
FunctionCall const& _functionCall,
|
||||||
|
std::vector<ASTPointer<Expression const>> const& _arguments
|
||||||
|
);
|
||||||
|
|
||||||
/// @returns code that evaluates to the first unused memory slot (which does not have to
|
/// @returns code that evaluates to the first unused memory slot (which does not have to
|
||||||
/// be empty).
|
/// be empty).
|
||||||
static std::string freeMemory();
|
static std::string freeMemory();
|
||||||
@ -112,7 +123,8 @@ private:
|
|||||||
|
|
||||||
/// @returns a Yul expression representing the current value of @a _expression,
|
/// @returns a Yul expression representing the current value of @a _expression,
|
||||||
/// converted to type @a _to if it does not yet have that type.
|
/// converted to type @a _to if it does not yet have that type.
|
||||||
std::string expressionAsType(Expression const& _expression, Type const& _to);
|
/// If @a _forceCleanup is set to true, it also cleans the value, in case it already has type @a _to.
|
||||||
|
std::string expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup = false);
|
||||||
|
|
||||||
/// @returns an output stream that can be used to define @a _var using a function call or
|
/// @returns an output stream that can be used to define @a _var using a function call or
|
||||||
/// single stack slot expression.
|
/// single stack slot expression.
|
||||||
@ -142,6 +154,11 @@ private:
|
|||||||
std::string const& _right
|
std::string const& _right
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// @returns code to perform the given shift operation.
|
||||||
|
/// The operation itself will be performed in the type of the value,
|
||||||
|
/// while the amount to shift can have its own type.
|
||||||
|
std::string shiftOperation(langutil::Token _op, IRVariable const& _value, IRVariable const& _shiftAmount);
|
||||||
|
|
||||||
/// Assigns the value of @a _value to the lvalue @a _lvalue.
|
/// Assigns the value of @a _value to the lvalue @a _lvalue.
|
||||||
void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value);
|
void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value);
|
||||||
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue.
|
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue.
|
||||||
|
@ -54,7 +54,7 @@ IRVariable IRVariable::part(string const& _name) const
|
|||||||
solAssert(itemName.empty() || itemType, "");
|
solAssert(itemName.empty() || itemType, "");
|
||||||
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type};
|
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type};
|
||||||
}
|
}
|
||||||
solAssert(false, "Invalid stack item name.");
|
solAssert(false, "Invalid stack item name: " + _name);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> IRVariable::stackSlots() const
|
vector<string> IRVariable::stackSlots() const
|
||||||
@ -89,7 +89,7 @@ string IRVariable::name() const
|
|||||||
{
|
{
|
||||||
solAssert(m_type.sizeOnStack() == 1, "");
|
solAssert(m_type.sizeOnStack() == 1, "");
|
||||||
auto const& [itemName, type] = m_type.stackItems().front();
|
auto const& [itemName, type] = m_type.stackItems().front();
|
||||||
solAssert(!type, "");
|
solAssert(!type, "Expected null type for name " + itemName);
|
||||||
return suffixedName(itemName);
|
return suffixedName(itemName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,10 +69,11 @@ public:
|
|||||||
/// The returned IRVariable is itself typed with the type of the stack slot as defined
|
/// The returned IRVariable is itself typed with the type of the stack slot as defined
|
||||||
/// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
|
/// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
|
||||||
IRVariable part(std::string const& _slot) const;
|
IRVariable part(std::string const& _slot) const;
|
||||||
private:
|
|
||||||
/// @returns a vector containing the names of the stack slots of the variable.
|
/// @returns a vector containing the names of the stack slots of the variable.
|
||||||
std::vector<std::string> stackSlots() const;
|
std::vector<std::string> stackSlots() const;
|
||||||
|
|
||||||
|
private:
|
||||||
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix,
|
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix,
|
||||||
/// unless @a _suffix is empty, in which case the base name itself is returned.
|
/// unless @a _suffix is empty, in which case the base name itself is returned.
|
||||||
std::string suffixedName(std::string const& _suffix) const;
|
std::string suffixedName(std::string const& _suffix) const;
|
||||||
|
@ -21,8 +21,6 @@
|
|||||||
#include <libsolidity/formal/SymbolicState.h>
|
#include <libsolidity/formal/SymbolicState.h>
|
||||||
#include <libsolidity/formal/SymbolicTypes.h>
|
#include <libsolidity/formal/SymbolicTypes.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
@ -44,6 +42,7 @@ BMC::BMC(
|
|||||||
if (_enabledSolvers.some())
|
if (_enabledSolvers.some())
|
||||||
if (!_smtlib2Responses.empty())
|
if (!_smtlib2Responses.empty())
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
5622_error,
|
||||||
"SMT-LIB2 query responses were given in the auxiliary input, "
|
"SMT-LIB2 query responses were given in the auxiliary input, "
|
||||||
"but this Solidity binary uses an SMT solver (Z3/CVC4) directly."
|
"but this Solidity binary uses an SMT solver (Z3/CVC4) directly."
|
||||||
"These responses will be ignored."
|
"These responses will be ignored."
|
||||||
@ -74,6 +73,7 @@ void BMC::analyze(SourceUnit const& _source, set<Expression const*> _safeAsserti
|
|||||||
{
|
{
|
||||||
m_noSolverWarning = true;
|
m_noSolverWarning = true;
|
||||||
m_outerErrorReporter.warning(
|
m_outerErrorReporter.warning(
|
||||||
|
8084_error,
|
||||||
SourceLocation(),
|
SourceLocation(),
|
||||||
"BMC analysis was not possible since no integrated SMT solver (Z3 or CVC4) was found."
|
"BMC analysis was not possible since no integrated SMT solver (Z3 or CVC4) was found."
|
||||||
);
|
);
|
||||||
@ -467,6 +467,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall)
|
|||||||
inlineFunctionCall(_funCall);
|
inlineFunctionCall(_funCall);
|
||||||
else if (funType.kind() == FunctionType::Kind::Internal)
|
else if (funType.kind() == FunctionType::Kind::Internal)
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
5729_error,
|
||||||
_funCall.location(),
|
_funCall.location(),
|
||||||
"Assertion checker does not yet implement this type of function call."
|
"Assertion checker does not yet implement this type of function call."
|
||||||
);
|
);
|
||||||
@ -591,8 +592,7 @@ void BMC::checkConstantCondition(BMCVerificationTarget& _target)
|
|||||||
*_target.expression,
|
*_target.expression,
|
||||||
_target.constraints,
|
_target.constraints,
|
||||||
_target.value,
|
_target.value,
|
||||||
_target.callStack,
|
_target.callStack
|
||||||
"Condition is always $VALUE."
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -610,6 +610,8 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smt::Expression const&
|
|||||||
_target.callStack,
|
_target.callStack,
|
||||||
_target.modelExpressions,
|
_target.modelExpressions,
|
||||||
_target.expression->location(),
|
_target.expression->location(),
|
||||||
|
4144_error,
|
||||||
|
8312_error,
|
||||||
"Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")",
|
"Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")",
|
||||||
"<result>",
|
"<result>",
|
||||||
&_target.value
|
&_target.value
|
||||||
@ -630,6 +632,8 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smt::Expression const& _
|
|||||||
_target.callStack,
|
_target.callStack,
|
||||||
_target.modelExpressions,
|
_target.modelExpressions,
|
||||||
_target.expression->location(),
|
_target.expression->location(),
|
||||||
|
2661_error,
|
||||||
|
8065_error,
|
||||||
"Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")",
|
"Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")",
|
||||||
"<result>",
|
"<result>",
|
||||||
&_target.value
|
&_target.value
|
||||||
@ -644,6 +648,8 @@ void BMC::checkDivByZero(BMCVerificationTarget& _target)
|
|||||||
_target.callStack,
|
_target.callStack,
|
||||||
_target.modelExpressions,
|
_target.modelExpressions,
|
||||||
_target.expression->location(),
|
_target.expression->location(),
|
||||||
|
3046_error,
|
||||||
|
5272_error,
|
||||||
"Division by zero",
|
"Division by zero",
|
||||||
"<result>",
|
"<result>",
|
||||||
&_target.value
|
&_target.value
|
||||||
@ -658,6 +664,8 @@ void BMC::checkBalance(BMCVerificationTarget& _target)
|
|||||||
_target.callStack,
|
_target.callStack,
|
||||||
_target.modelExpressions,
|
_target.modelExpressions,
|
||||||
_target.expression->location(),
|
_target.expression->location(),
|
||||||
|
1236_error,
|
||||||
|
4010_error,
|
||||||
"Insufficient funds",
|
"Insufficient funds",
|
||||||
"address(this).balance"
|
"address(this).balance"
|
||||||
);
|
);
|
||||||
@ -672,6 +680,8 @@ void BMC::checkAssert(BMCVerificationTarget& _target)
|
|||||||
_target.callStack,
|
_target.callStack,
|
||||||
_target.modelExpressions,
|
_target.modelExpressions,
|
||||||
_target.expression->location(),
|
_target.expression->location(),
|
||||||
|
4661_error,
|
||||||
|
7812_error,
|
||||||
"Assertion violation"
|
"Assertion violation"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -702,9 +712,11 @@ void BMC::addVerificationTarget(
|
|||||||
|
|
||||||
void BMC::checkCondition(
|
void BMC::checkCondition(
|
||||||
smt::Expression _condition,
|
smt::Expression _condition,
|
||||||
vector<SMTEncoder::CallStackEntry> const& callStack,
|
vector<SMTEncoder::CallStackEntry> const& _callStack,
|
||||||
pair<vector<smt::Expression>, vector<string>> const& _modelExpressions,
|
pair<vector<smt::Expression>, vector<string>> const& _modelExpressions,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
|
ErrorId _errorHappens,
|
||||||
|
ErrorId _errorMightHappen,
|
||||||
string const& _description,
|
string const& _description,
|
||||||
string const& _additionalValueName,
|
string const& _additionalValueName,
|
||||||
smt::Expression const* _additionalValue
|
smt::Expression const* _additionalValue
|
||||||
@ -716,7 +728,7 @@ void BMC::checkCondition(
|
|||||||
vector<smt::Expression> expressionsToEvaluate;
|
vector<smt::Expression> expressionsToEvaluate;
|
||||||
vector<string> expressionNames;
|
vector<string> expressionNames;
|
||||||
tie(expressionsToEvaluate, expressionNames) = _modelExpressions;
|
tie(expressionsToEvaluate, expressionNames) = _modelExpressions;
|
||||||
if (callStack.size())
|
if (_callStack.size())
|
||||||
if (_additionalValue)
|
if (_additionalValue)
|
||||||
{
|
{
|
||||||
expressionsToEvaluate.emplace_back(*_additionalValue);
|
expressionsToEvaluate.emplace_back(*_additionalValue);
|
||||||
@ -747,7 +759,7 @@ void BMC::checkCondition(
|
|||||||
{
|
{
|
||||||
std::ostringstream message;
|
std::ostringstream message;
|
||||||
message << _description << " happens here";
|
message << _description << " happens here";
|
||||||
if (callStack.size())
|
if (_callStack.size())
|
||||||
{
|
{
|
||||||
std::ostringstream modelMessage;
|
std::ostringstream modelMessage;
|
||||||
modelMessage << " for:\n";
|
modelMessage << " for:\n";
|
||||||
@ -760,30 +772,31 @@ void BMC::checkCondition(
|
|||||||
for (auto const& eval: sortedModel)
|
for (auto const& eval: sortedModel)
|
||||||
modelMessage << " " << eval.first << " = " << eval.second << "\n";
|
modelMessage << " " << eval.first << " = " << eval.second << "\n";
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
_errorHappens,
|
||||||
_location,
|
_location,
|
||||||
message.str(),
|
message.str(),
|
||||||
SecondarySourceLocation().append(modelMessage.str(), SourceLocation{})
|
SecondarySourceLocation().append(modelMessage.str(), SourceLocation{})
|
||||||
.append(SMTEncoder::callStackMessage(callStack))
|
.append(SMTEncoder::callStackMessage(_callStack))
|
||||||
.append(move(secondaryLocation))
|
.append(move(secondaryLocation))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
message << ".";
|
message << ".";
|
||||||
m_errorReporter.warning(_location, message.str(), secondaryLocation);
|
m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case smt::CheckResult::UNSATISFIABLE:
|
case smt::CheckResult::UNSATISFIABLE:
|
||||||
break;
|
break;
|
||||||
case smt::CheckResult::UNKNOWN:
|
case smt::CheckResult::UNKNOWN:
|
||||||
m_errorReporter.warning(_location, _description + " might happen here.", secondaryLocation);
|
m_errorReporter.warning(_errorMightHappen, _location, _description + " might happen here.", secondaryLocation);
|
||||||
break;
|
break;
|
||||||
case smt::CheckResult::CONFLICTING:
|
case smt::CheckResult::CONFLICTING:
|
||||||
m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||||
break;
|
break;
|
||||||
case smt::CheckResult::ERROR:
|
case smt::CheckResult::ERROR:
|
||||||
m_errorReporter.warning(_location, "Error trying to invoke SMT solver.");
|
m_errorReporter.warning(1823_error, _location, "Error trying to invoke SMT solver.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,8 +807,7 @@ void BMC::checkBooleanNotConstant(
|
|||||||
Expression const& _condition,
|
Expression const& _condition,
|
||||||
smt::Expression const& _constraints,
|
smt::Expression const& _constraints,
|
||||||
smt::Expression const& _value,
|
smt::Expression const& _value,
|
||||||
vector<SMTEncoder::CallStackEntry> const& _callStack,
|
vector<SMTEncoder::CallStackEntry> const& _callStack
|
||||||
string const& _description
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Do not check for const-ness if this is a constant.
|
// Do not check for const-ness if this is a constant.
|
||||||
@ -813,9 +825,9 @@ void BMC::checkBooleanNotConstant(
|
|||||||
m_interface->pop();
|
m_interface->pop();
|
||||||
|
|
||||||
if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR)
|
if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR)
|
||||||
m_errorReporter.warning(_condition.location(), "Error trying to invoke SMT solver.");
|
m_errorReporter.warning(8592_error, _condition.location(), "Error trying to invoke SMT solver.");
|
||||||
else if (positiveResult == smt::CheckResult::CONFLICTING || negatedResult == smt::CheckResult::CONFLICTING)
|
else if (positiveResult == smt::CheckResult::CONFLICTING || negatedResult == smt::CheckResult::CONFLICTING)
|
||||||
m_errorReporter.warning(_condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
m_errorReporter.warning(3356_error, _condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||||
else if (positiveResult == smt::CheckResult::SATISFIABLE && negatedResult == smt::CheckResult::SATISFIABLE)
|
else if (positiveResult == smt::CheckResult::SATISFIABLE && negatedResult == smt::CheckResult::SATISFIABLE)
|
||||||
{
|
{
|
||||||
// everything fine.
|
// everything fine.
|
||||||
@ -825,24 +837,25 @@ void BMC::checkBooleanNotConstant(
|
|||||||
// can't do anything.
|
// can't do anything.
|
||||||
}
|
}
|
||||||
else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE)
|
else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE)
|
||||||
m_errorReporter.warning(_condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
|
m_errorReporter.warning(2512_error, _condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string value;
|
string description;
|
||||||
if (positiveResult == smt::CheckResult::SATISFIABLE)
|
if (positiveResult == smt::CheckResult::SATISFIABLE)
|
||||||
{
|
{
|
||||||
solAssert(negatedResult == smt::CheckResult::UNSATISFIABLE, "");
|
solAssert(negatedResult == smt::CheckResult::UNSATISFIABLE, "");
|
||||||
value = "true";
|
description = "Condition is always true.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(positiveResult == smt::CheckResult::UNSATISFIABLE, "");
|
solAssert(positiveResult == smt::CheckResult::UNSATISFIABLE, "");
|
||||||
solAssert(negatedResult == smt::CheckResult::SATISFIABLE, "");
|
solAssert(negatedResult == smt::CheckResult::SATISFIABLE, "");
|
||||||
value = "false";
|
description = "Condition is always false.";
|
||||||
}
|
}
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
6838_error,
|
||||||
_condition.location(),
|
_condition.location(),
|
||||||
boost::algorithm::replace_all_copy(_description, "$VALUE", value),
|
description,
|
||||||
SMTEncoder::callStackMessage(_callStack)
|
SMTEncoder::callStackMessage(_callStack)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -862,7 +875,7 @@ BMC::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _expression
|
|||||||
string description("Error querying SMT solver");
|
string description("Error querying SMT solver");
|
||||||
if (_e.comment())
|
if (_e.comment())
|
||||||
description += ": " + *_e.comment();
|
description += ": " + *_e.comment();
|
||||||
m_errorReporter.warning(description);
|
m_errorReporter.warning(8140_error, description);
|
||||||
result = smt::CheckResult::ERROR;
|
result = smt::CheckResult::ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ using solidity::util::h256;
|
|||||||
namespace solidity::langutil
|
namespace solidity::langutil
|
||||||
{
|
{
|
||||||
class ErrorReporter;
|
class ErrorReporter;
|
||||||
|
struct ErrorId;
|
||||||
struct SourceLocation;
|
struct SourceLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,22 +145,22 @@ private:
|
|||||||
/// Check that a condition can be satisfied.
|
/// Check that a condition can be satisfied.
|
||||||
void checkCondition(
|
void checkCondition(
|
||||||
smt::Expression _condition,
|
smt::Expression _condition,
|
||||||
std::vector<CallStackEntry> const& callStack,
|
std::vector<CallStackEntry> const& _callStack,
|
||||||
std::pair<std::vector<smt::Expression>, std::vector<std::string>> const& _modelExpressions,
|
std::pair<std::vector<smt::Expression>, std::vector<std::string>> const& _modelExpressions,
|
||||||
langutil::SourceLocation const& _location,
|
langutil::SourceLocation const& _location,
|
||||||
|
langutil::ErrorId _errorHappens,
|
||||||
|
langutil::ErrorId _errorMightHappen,
|
||||||
std::string const& _description,
|
std::string const& _description,
|
||||||
std::string const& _additionalValueName = "",
|
std::string const& _additionalValueName = "",
|
||||||
smt::Expression const* _additionalValue = nullptr
|
smt::Expression const* _additionalValue = nullptr
|
||||||
);
|
);
|
||||||
/// Checks that a boolean condition is not constant. Do not warn if the expression
|
/// Checks that a boolean condition is not constant. Do not warn if the expression
|
||||||
/// is a literal constant.
|
/// is a literal constant.
|
||||||
/// @param _description the warning string, $VALUE will be replaced by the constant value.
|
|
||||||
void checkBooleanNotConstant(
|
void checkBooleanNotConstant(
|
||||||
Expression const& _condition,
|
Expression const& _condition,
|
||||||
smt::Expression const& _constraints,
|
smt::Expression const& _constraints,
|
||||||
smt::Expression const& _value,
|
smt::Expression const& _value,
|
||||||
std::vector<CallStackEntry> const& _callStack,
|
std::vector<CallStackEntry> const& _callStack
|
||||||
std::string const& _description
|
|
||||||
);
|
);
|
||||||
std::pair<smt::CheckResult, std::vector<std::string>>
|
std::pair<smt::CheckResult, std::vector<std::string>>
|
||||||
checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
||||||
|
@ -965,10 +965,10 @@ pair<smt::CheckResult, vector<string>> CHC::query(smt::Expression const& _query,
|
|||||||
case smt::CheckResult::UNKNOWN:
|
case smt::CheckResult::UNKNOWN:
|
||||||
break;
|
break;
|
||||||
case smt::CheckResult::CONFLICTING:
|
case smt::CheckResult::CONFLICTING:
|
||||||
m_outerErrorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
|
||||||
break;
|
break;
|
||||||
case smt::CheckResult::ERROR:
|
case smt::CheckResult::ERROR:
|
||||||
m_outerErrorReporter.warning(_location, "Error trying to invoke SMT solver.");
|
m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return {result, values};
|
return {result, values};
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem/operations.hpp>
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -266,6 +266,7 @@ void SMTEncoder::endVisit(FunctionDefinition const&)
|
|||||||
bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
|
bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
7737_error,
|
||||||
_inlineAsm.location(),
|
_inlineAsm.location(),
|
||||||
"Assertion checker does not support inline assembly."
|
"Assertion checker does not support inline assembly."
|
||||||
);
|
);
|
||||||
@ -325,6 +326,7 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
7186_error,
|
||||||
_varDecl.location(),
|
_varDecl.location(),
|
||||||
"Assertion checker does not yet implement such variable declarations."
|
"Assertion checker does not yet implement such variable declarations."
|
||||||
);
|
);
|
||||||
@ -350,6 +352,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
|
|||||||
m_context.newValue(*varDecl);
|
m_context.newValue(*varDecl);
|
||||||
|
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
9149_error,
|
||||||
_assignment.location(),
|
_assignment.location(),
|
||||||
"Assertion checker does not yet implement this assignment operator."
|
"Assertion checker does not yet implement this assignment operator."
|
||||||
);
|
);
|
||||||
@ -409,6 +412,7 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
|
|||||||
|
|
||||||
if (_tuple.isInlineArray())
|
if (_tuple.isInlineArray())
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
2177_error,
|
||||||
_tuple.location(),
|
_tuple.location(),
|
||||||
"Assertion checker does not yet implement inline arrays."
|
"Assertion checker does not yet implement inline arrays."
|
||||||
);
|
);
|
||||||
@ -493,6 +497,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
1950_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement such increments / decrements."
|
"Assertion checker does not yet implement such increments / decrements."
|
||||||
);
|
);
|
||||||
@ -522,6 +527,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
|||||||
arrayIndexAssignment(_op.subExpression(), symbVar->currentValue());
|
arrayIndexAssignment(_op.subExpression(), symbVar->currentValue());
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
2683_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement \"delete\" for this expression."
|
"Assertion checker does not yet implement \"delete\" for this expression."
|
||||||
);
|
);
|
||||||
@ -530,6 +536,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
3682_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement this operator."
|
"Assertion checker does not yet implement this operator."
|
||||||
);
|
);
|
||||||
@ -568,6 +575,7 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
|
|||||||
compareOperation(_op);
|
compareOperation(_op);
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
3876_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement this operator."
|
"Assertion checker does not yet implement this operator."
|
||||||
);
|
);
|
||||||
@ -580,6 +588,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
|||||||
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
4639_error,
|
||||||
_funCall.location(),
|
_funCall.location(),
|
||||||
"Assertion checker does not yet implement this expression."
|
"Assertion checker does not yet implement this expression."
|
||||||
);
|
);
|
||||||
@ -639,6 +648,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
4588_error,
|
||||||
_funCall.location(),
|
_funCall.location(),
|
||||||
"Assertion checker does not yet implement this type of function call."
|
"Assertion checker does not yet implement this type of function call."
|
||||||
);
|
);
|
||||||
@ -771,6 +781,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
5084_error,
|
||||||
_funCall.location(),
|
_funCall.location(),
|
||||||
"Type conversion is not yet fully supported and might yield false positives."
|
"Type conversion is not yet fully supported and might yield false positives."
|
||||||
);
|
);
|
||||||
@ -800,6 +811,7 @@ void SMTEncoder::endVisit(Literal const& _literal)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
7885_error,
|
||||||
_literal.location(),
|
_literal.location(),
|
||||||
"Assertion checker does not yet support the type of this literal (" +
|
"Assertion checker does not yet support the type of this literal (" +
|
||||||
_literal.annotation().type->toString() +
|
_literal.annotation().type->toString() +
|
||||||
@ -850,6 +862,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
|||||||
accessedName = identifier->name();
|
accessedName = identifier->name();
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
9551_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"Assertion checker does not yet support this expression."
|
"Assertion checker does not yet support this expression."
|
||||||
);
|
);
|
||||||
@ -879,6 +892,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
7650_error,
|
||||||
_memberAccess.location(),
|
_memberAccess.location(),
|
||||||
"Assertion checker does not yet support this expression."
|
"Assertion checker does not yet support this expression."
|
||||||
);
|
);
|
||||||
@ -903,6 +917,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
|||||||
if (varDecl->type()->category() == Type::Category::FixedBytes)
|
if (varDecl->type()->category() == Type::Category::FixedBytes)
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
7989_error,
|
||||||
_indexAccess.location(),
|
_indexAccess.location(),
|
||||||
"Assertion checker does not yet support index accessing fixed bytes."
|
"Assertion checker does not yet support index accessing fixed bytes."
|
||||||
);
|
);
|
||||||
@ -917,6 +932,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
9118_error,
|
||||||
_indexAccess.location(),
|
_indexAccess.location(),
|
||||||
"Assertion checker does not yet implement this expression."
|
"Assertion checker does not yet implement this expression."
|
||||||
);
|
);
|
||||||
@ -940,6 +956,7 @@ void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess)
|
|||||||
{
|
{
|
||||||
createExpr(_indexRangeAccess);
|
createExpr(_indexRangeAccess);
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
2923_error,
|
||||||
_indexRangeAccess.location(),
|
_indexRangeAccess.location(),
|
||||||
"Assertion checker does not yet implement this expression."
|
"Assertion checker does not yet implement this expression."
|
||||||
);
|
);
|
||||||
@ -1019,6 +1036,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
9056_error,
|
||||||
_expr.location(),
|
_expr.location(),
|
||||||
"Assertion checker does not yet implement this expression."
|
"Assertion checker does not yet implement this expression."
|
||||||
);
|
);
|
||||||
@ -1034,6 +1052,7 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex
|
|||||||
bool abstract = m_context.createGlobalSymbol(_name, _expr);
|
bool abstract = m_context.createGlobalSymbol(_name, _expr);
|
||||||
if (abstract)
|
if (abstract)
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
1695_error,
|
||||||
_expr.location(),
|
_expr.location(),
|
||||||
"Assertion checker does not yet support this global variable."
|
"Assertion checker does not yet support this global variable."
|
||||||
);
|
);
|
||||||
@ -1087,6 +1106,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
5188_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement this operator."
|
"Assertion checker does not yet implement this operator."
|
||||||
);
|
);
|
||||||
@ -1094,6 +1114,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
9011_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement this operator for type " + type->richIdentifier() + "."
|
"Assertion checker does not yet implement this operator for type " + type->richIdentifier() + "."
|
||||||
);
|
);
|
||||||
@ -1185,6 +1206,7 @@ void SMTEncoder::compareOperation(BinaryOperation const& _op)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
7229_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons"
|
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons"
|
||||||
);
|
);
|
||||||
@ -1213,6 +1235,7 @@ void SMTEncoder::booleanOperation(BinaryOperation const& _op)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
3263_error,
|
||||||
_op.location(),
|
_op.location(),
|
||||||
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations"
|
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations"
|
||||||
);
|
);
|
||||||
@ -1245,6 +1268,7 @@ void SMTEncoder::assignment(
|
|||||||
m_context.newValue(*varDecl);
|
m_context.newValue(*varDecl);
|
||||||
|
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
6191_error,
|
||||||
_location,
|
_location,
|
||||||
"Assertion checker does not yet implement type " + _type->toString()
|
"Assertion checker does not yet implement type " + _type->toString()
|
||||||
);
|
);
|
||||||
@ -1272,6 +1296,7 @@ void SMTEncoder::assignment(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
8182_error,
|
||||||
_location,
|
_location,
|
||||||
"Assertion checker does not yet implement such assignments."
|
"Assertion checker does not yet implement such assignments."
|
||||||
);
|
);
|
||||||
@ -1482,6 +1507,7 @@ bool SMTEncoder::createVariable(VariableDeclaration const& _varDecl)
|
|||||||
if (abstract)
|
if (abstract)
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
8115_error,
|
||||||
_varDecl.location(),
|
_varDecl.location(),
|
||||||
"Assertion checker does not yet support the type of this variable."
|
"Assertion checker does not yet support the type of this variable."
|
||||||
);
|
);
|
||||||
@ -1494,7 +1520,7 @@ smt::Expression SMTEncoder::expr(Expression const& _e, TypePointer _targetType)
|
|||||||
{
|
{
|
||||||
if (!m_context.knownExpression(_e))
|
if (!m_context.knownExpression(_e))
|
||||||
{
|
{
|
||||||
m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." );
|
m_errorReporter.warning(6031_error, _e.location(), "Internal error: Expression undefined for SMT solver." );
|
||||||
createExpr(_e);
|
createExpr(_e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1506,6 +1532,7 @@ void SMTEncoder::createExpr(Expression const& _e)
|
|||||||
bool abstract = m_context.createExpression(_e);
|
bool abstract = m_context.createExpression(_e);
|
||||||
if (abstract)
|
if (abstract)
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
8364_error,
|
||||||
_e.location(),
|
_e.location(),
|
||||||
"Assertion checker does not yet implement type " + _e.annotation().type->toString()
|
"Assertion checker does not yet implement type " + _e.annotation().type->toString()
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/string/join.hpp>
|
#include <boost/algorithm/string/join.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem/operations.hpp>
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
@ -73,9 +73,10 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
|||||||
);
|
);
|
||||||
abi.emplace(std::move(method));
|
abi.emplace(std::move(method));
|
||||||
}
|
}
|
||||||
if (_contractDef.constructor())
|
FunctionDefinition const* constructor = _contractDef.constructor();
|
||||||
|
if (constructor && constructor->visibility() >= Visibility::Public)
|
||||||
{
|
{
|
||||||
FunctionType constrType(*_contractDef.constructor());
|
FunctionType constrType(*constructor);
|
||||||
FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
|
FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
|
||||||
solAssert(!!externalFunctionType, "");
|
solAssert(!!externalFunctionType, "");
|
||||||
Json::Value method;
|
Json::Value method;
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -239,7 +239,7 @@ bool CompilerStack::parse()
|
|||||||
m_errorReporter.clear();
|
m_errorReporter.clear();
|
||||||
|
|
||||||
if (SemVerVersion{string(VersionString)}.isPrerelease())
|
if (SemVerVersion{string(VersionString)}.isPrerelease())
|
||||||
m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production.");
|
m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production.");
|
||||||
|
|
||||||
Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery};
|
Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery};
|
||||||
|
|
||||||
@ -949,6 +949,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_errorReporter.parserError(
|
m_errorReporter.parserError(
|
||||||
|
6275_error,
|
||||||
import->location(),
|
import->location(),
|
||||||
string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage)
|
string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage)
|
||||||
);
|
);
|
||||||
@ -1110,6 +1111,7 @@ void CompilerStack::compileContract(
|
|||||||
compiledContract.runtimeObject.bytecode.size() > 0x6000
|
compiledContract.runtimeObject.bytecode.size() > 0x6000
|
||||||
)
|
)
|
||||||
m_errorReporter.warning(
|
m_errorReporter.warning(
|
||||||
|
5574_error,
|
||||||
_contract.location(),
|
_contract.location(),
|
||||||
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||||
"This contract may not be deployable on mainnet. "
|
"This contract may not be deployable on mainnet. "
|
||||||
|
@ -103,16 +103,14 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
|
|||||||
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
|
||||||
{
|
{
|
||||||
Json::Value method(devDocumentation(fun->annotation().docTags));
|
Json::Value method(devDocumentation(fun->annotation().docTags));
|
||||||
|
// add the function, only if we have any documentation to add
|
||||||
|
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
||||||
|
|
||||||
|
if (!jsonReturn.empty())
|
||||||
|
method["returns"] = jsonReturn;
|
||||||
|
|
||||||
if (!method.empty())
|
if (!method.empty())
|
||||||
{
|
|
||||||
// add the function, only if we have any documentation to add
|
|
||||||
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
|
|
||||||
|
|
||||||
if (!jsonReturn.empty())
|
|
||||||
method["returns"] = jsonReturn;
|
|
||||||
|
|
||||||
methods[it.second->externalSignature()] = method;
|
methods[it.second->externalSignature()] = method;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,8 +31,7 @@
|
|||||||
#include <libsolutil/JSON.h>
|
#include <libsolutil/JSON.h>
|
||||||
#include <libsolutil/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
|
|
||||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -491,7 +490,7 @@ std::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelect
|
|||||||
}
|
}
|
||||||
/// Validates the optimizer settings and returns them in a parsed object.
|
/// Validates the optimizer settings and returns them in a parsed object.
|
||||||
/// On error returns the json-formatted error message.
|
/// On error returns the json-formatted error message.
|
||||||
boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
|
std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
|
||||||
{
|
{
|
||||||
if (auto result = checkOptimizerKeys(_jsonInput))
|
if (auto result = checkOptimizerKeys(_jsonInput))
|
||||||
return *result;
|
return *result;
|
||||||
@ -552,7 +551,7 @@ boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Valu
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
|
std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
|
||||||
{
|
{
|
||||||
InputsAndSettings ret;
|
InputsAndSettings ret;
|
||||||
|
|
||||||
@ -740,10 +739,10 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
|
|||||||
if (settings.isMember("optimizer"))
|
if (settings.isMember("optimizer"))
|
||||||
{
|
{
|
||||||
auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]);
|
auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]);
|
||||||
if (optimiserSettings.type() == typeid(Json::Value))
|
if (std::holds_alternative<Json::Value>(optimiserSettings))
|
||||||
return boost::get<Json::Value>(std::move(optimiserSettings)); // was an error
|
return std::get<Json::Value>(std::move(optimiserSettings)); // was an error
|
||||||
else
|
else
|
||||||
ret.optimiserSettings = boost::get<OptimiserSettings>(std::move(optimiserSettings));
|
ret.optimiserSettings = std::get<OptimiserSettings>(std::move(optimiserSettings));
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
||||||
@ -1155,9 +1154,9 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto parsed = parseInput(_input);
|
auto parsed = parseInput(_input);
|
||||||
if (parsed.type() == typeid(Json::Value))
|
if (std::holds_alternative<Json::Value>(parsed))
|
||||||
return boost::get<Json::Value>(std::move(parsed));
|
return std::get<Json::Value>(std::move(parsed));
|
||||||
InputsAndSettings settings = boost::get<InputsAndSettings>(std::move(parsed));
|
InputsAndSettings settings = std::get<InputsAndSettings>(std::move(parsed));
|
||||||
if (settings.language == "Solidity")
|
if (settings.language == "Solidity")
|
||||||
return compileSolidity(std::move(settings));
|
return compileSolidity(std::move(settings));
|
||||||
else if (settings.language == "Yul")
|
else if (settings.language == "Yul")
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
#include <libsolidity/interface/CompilerStack.h>
|
#include <libsolidity/interface/CompilerStack.h>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
namespace solidity::frontend
|
namespace solidity::frontend
|
||||||
{
|
{
|
||||||
@ -73,7 +73,7 @@ private:
|
|||||||
|
|
||||||
/// Parses the input json (and potentially invokes the read callback) and either returns
|
/// Parses the input json (and potentially invokes the read callback) and either returns
|
||||||
/// it in condensed form or an error as a json object.
|
/// it in condensed form or an error as a json object.
|
||||||
boost::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
|
std::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
|
||||||
|
|
||||||
Json::Value compileSolidity(InputsAndSettings _inputsAndSettings);
|
Json::Value compileSolidity(InputsAndSettings _inputsAndSettings);
|
||||||
Json::Value compileYul(InputsAndSettings _inputsAndSettings);
|
Json::Value compileYul(InputsAndSettings _inputsAndSettings);
|
||||||
|
@ -195,5 +195,5 @@ void DocStringParser::newTag(string const& _tagName)
|
|||||||
void DocStringParser::appendError(string const& _description)
|
void DocStringParser::appendError(string const& _description)
|
||||||
{
|
{
|
||||||
m_errorsOccurred = true;
|
m_errorsOccurred = true;
|
||||||
m_errorReporter->docstringParsingError(_description);
|
m_errorReporter->docstringParsingError(9440_error, _description);
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
|||||||
nodes.push_back(parseEnumDefinition());
|
nodes.push_back(parseEnumDefinition());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fatalParserError(string("Expected pragma, import directive or contract/interface/library/struct/enum definition."));
|
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
solAssert(m_recursionDepth == 0, "");
|
solAssert(m_recursionDepth == 0, "");
|
||||||
@ -128,9 +128,10 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> c
|
|||||||
// so we don't need to report anything here.
|
// so we don't need to report anything here.
|
||||||
if (!m_parserErrorRecovery)
|
if (!m_parserErrorRecovery)
|
||||||
m_errorReporter.fatalParserError(
|
m_errorReporter.fatalParserError(
|
||||||
|
5333_error,
|
||||||
_location,
|
_location,
|
||||||
"Source file requires different compiler version (current compiler is " +
|
"Source file requires different compiler version (current compiler is " +
|
||||||
string(VersionString) + " - note that nightly builds are considered to be "
|
string(VersionString) + ") - note that nightly builds are considered to be "
|
||||||
"strictly less than the released version"
|
"strictly less than the released version"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -162,7 +163,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
|
|||||||
{
|
{
|
||||||
Token token = m_scanner->currentToken();
|
Token token = m_scanner->currentToken();
|
||||||
if (token == Token::Illegal)
|
if (token == Token::Illegal)
|
||||||
parserError("Token incompatible with Solidity parser as part of pragma directive.");
|
parserError(6281_error, "Token incompatible with Solidity parser as part of pragma directive.");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string literal = m_scanner->currentLiteral();
|
string literal = m_scanner->currentLiteral();
|
||||||
@ -240,18 +241,18 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
|
|||||||
unitAlias = expectIdentifierToken();
|
unitAlias = expectIdentifierToken();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fatalParserError("Expected string literal (path), \"*\" or alias list.");
|
fatalParserError(9478_error, "Expected string literal (path), \"*\" or alias list.");
|
||||||
// "from" is not a keyword but parsed as an identifier because of backwards
|
// "from" is not a keyword but parsed as an identifier because of backwards
|
||||||
// compatibility and because it is a really common word.
|
// compatibility and because it is a really common word.
|
||||||
if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
|
if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
|
||||||
fatalParserError("Expected \"from\".");
|
fatalParserError(8208_error, "Expected \"from\".");
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
if (m_scanner->currentToken() != Token::StringLiteral)
|
if (m_scanner->currentToken() != Token::StringLiteral)
|
||||||
fatalParserError("Expected import path.");
|
fatalParserError(6845_error, "Expected import path.");
|
||||||
path = getLiteralAndAdvance();
|
path = getLiteralAndAdvance();
|
||||||
}
|
}
|
||||||
if (path->empty())
|
if (path->empty())
|
||||||
fatalParserError("Import path cannot be empty.");
|
fatalParserError(6326_error, "Import path cannot be empty.");
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expectToken(Token::Semicolon);
|
expectToken(Token::Semicolon);
|
||||||
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
|
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
|
||||||
@ -278,7 +279,7 @@ std::pair<ContractKind, bool> Parser::parseContractKind()
|
|||||||
kind = ContractKind::Library;
|
kind = ContractKind::Library;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
parserError("Expected keyword \"contract\", \"interface\" or \"library\".");
|
parserError(3515_error, "Expected keyword \"contract\", \"interface\" or \"library\".");
|
||||||
return std::make_pair(ContractKind::Contract, abstract);
|
return std::make_pair(ContractKind::Contract, abstract);
|
||||||
}
|
}
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
@ -343,7 +344,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
|||||||
else if (currentTokenValue == Token::Using)
|
else if (currentTokenValue == Token::Using)
|
||||||
subNodes.push_back(parseUsingDirective());
|
subNodes.push_back(parseUsingDirective());
|
||||||
else
|
else
|
||||||
fatalParserError(string("Function, variable, struct or modifier declaration expected."));
|
fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
@ -462,6 +463,7 @@ StateMutability Parser::parseStateMutability()
|
|||||||
case Token::Constant:
|
case Token::Constant:
|
||||||
stateMutability = StateMutability::View;
|
stateMutability = StateMutability::View;
|
||||||
parserError(
|
parserError(
|
||||||
|
7698_error,
|
||||||
"The state mutability modifier \"constant\" was removed in version 0.5.0. "
|
"The state mutability modifier \"constant\" was removed in version 0.5.0. "
|
||||||
"Use \"view\" or \"pure\" instead."
|
"Use \"view\" or \"pure\" instead."
|
||||||
);
|
);
|
||||||
@ -494,11 +496,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
|||||||
// Detect this and return early.
|
// Detect this and return early.
|
||||||
if (_isStateVariable && (result.visibility == Visibility::External || result.visibility == Visibility::Internal))
|
if (_isStateVariable && (result.visibility == Visibility::External || result.visibility == Visibility::Internal))
|
||||||
break;
|
break;
|
||||||
parserError(string(
|
parserError(
|
||||||
|
9439_error,
|
||||||
"Visibility already specified as \"" +
|
"Visibility already specified as \"" +
|
||||||
Declaration::visibilityToString(result.visibility) +
|
Declaration::visibilityToString(result.visibility) +
|
||||||
"\"."
|
"\"."
|
||||||
));
|
);
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -508,11 +511,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
|||||||
{
|
{
|
||||||
if (result.stateMutability != StateMutability::NonPayable)
|
if (result.stateMutability != StateMutability::NonPayable)
|
||||||
{
|
{
|
||||||
parserError(string(
|
parserError(
|
||||||
|
9680_error,
|
||||||
"State mutability already specified as \"" +
|
"State mutability already specified as \"" +
|
||||||
stateMutabilityToString(result.stateMutability) +
|
stateMutabilityToString(result.stateMutability) +
|
||||||
"\"."
|
"\"."
|
||||||
));
|
);
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -521,14 +525,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
|
|||||||
else if (!_isStateVariable && token == Token::Override)
|
else if (!_isStateVariable && token == Token::Override)
|
||||||
{
|
{
|
||||||
if (result.overrides)
|
if (result.overrides)
|
||||||
parserError("Override already specified.");
|
parserError(1827_error, "Override already specified.");
|
||||||
|
|
||||||
result.overrides = parseOverrideSpecifier();
|
result.overrides = parseOverrideSpecifier();
|
||||||
}
|
}
|
||||||
else if (!_isStateVariable && token == Token::Virtual)
|
else if (!_isStateVariable && token == Token::Virtual)
|
||||||
{
|
{
|
||||||
if (result.isVirtual)
|
if (result.isVirtual)
|
||||||
parserError("Virtual already specified.");
|
parserError(6879_error, "Virtual already specified.");
|
||||||
|
|
||||||
result.isVirtual = true;
|
result.isVirtual = true;
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
@ -576,9 +580,9 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
|
|||||||
"the \"function\" keyword to define it."
|
"the \"function\" keyword to define it."
|
||||||
};
|
};
|
||||||
if (m_scanner->currentToken() == Token::Constructor)
|
if (m_scanner->currentToken() == Token::Constructor)
|
||||||
parserError(message);
|
parserError(3323_error, message);
|
||||||
else
|
else
|
||||||
parserWarning(message);
|
parserWarning(3445_error, message);
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -659,10 +663,10 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
|
|||||||
break;
|
break;
|
||||||
expectToken(Token::Comma);
|
expectToken(Token::Comma);
|
||||||
if (m_scanner->currentToken() != Token::Identifier)
|
if (m_scanner->currentToken() != Token::Identifier)
|
||||||
fatalParserError(string("Expected identifier after ','"));
|
fatalParserError(1612_error, "Expected identifier after ','");
|
||||||
}
|
}
|
||||||
if (members.empty())
|
if (members.empty())
|
||||||
parserError({"enum with no members is not allowed."});
|
parserError(3147_error, "enum with no members is not allowed.");
|
||||||
|
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expectToken(Token::RBrace);
|
expectToken(Token::RBrace);
|
||||||
@ -689,6 +693,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
|
|
||||||
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
||||||
fatalParserError(
|
fatalParserError(
|
||||||
|
2915_error,
|
||||||
"Expected a state variable declaration. If you intended this as a fallback function "
|
"Expected a state variable declaration. If you intended this as a fallback function "
|
||||||
"or a function to handle plain ether transactions, use the \"fallback\" keyword "
|
"or a function to handle plain ether transactions, use the \"fallback\" keyword "
|
||||||
"or the \"receive\" keyword instead."
|
"or the \"receive\" keyword instead."
|
||||||
@ -709,11 +714,12 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
if (visibility != Visibility::Default)
|
if (visibility != Visibility::Default)
|
||||||
{
|
{
|
||||||
parserError(string(
|
parserError(
|
||||||
|
4110_error,
|
||||||
"Visibility already specified as \"" +
|
"Visibility already specified as \"" +
|
||||||
Declaration::visibilityToString(visibility) +
|
Declaration::visibilityToString(visibility) +
|
||||||
"\"."
|
"\"."
|
||||||
));
|
);
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -722,7 +728,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
else if (_options.isStateVariable && token == Token::Override)
|
else if (_options.isStateVariable && token == Token::Override)
|
||||||
{
|
{
|
||||||
if (overrides)
|
if (overrides)
|
||||||
parserError("Override already specified.");
|
parserError(9125_error, "Override already specified.");
|
||||||
|
|
||||||
overrides = parseOverrideSpecifier();
|
overrides = parseOverrideSpecifier();
|
||||||
}
|
}
|
||||||
@ -734,6 +740,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
{
|
{
|
||||||
if (mutability != VariableDeclaration::Mutability::Mutable)
|
if (mutability != VariableDeclaration::Mutability::Mutable)
|
||||||
parserError(
|
parserError(
|
||||||
|
3109_error,
|
||||||
string("Mutability already set to ") +
|
string("Mutability already set to ") +
|
||||||
(mutability == VariableDeclaration::Mutability::Constant ? "\"constant\"" : "\"immutable\"")
|
(mutability == VariableDeclaration::Mutability::Constant ? "\"constant\"" : "\"immutable\"")
|
||||||
);
|
);
|
||||||
@ -745,9 +752,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
|
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
|
||||||
{
|
{
|
||||||
if (location != VariableDeclaration::Location::Unspecified)
|
if (location != VariableDeclaration::Location::Unspecified)
|
||||||
parserError(string("Location already specified."));
|
parserError(3548_error, "Location already specified.");
|
||||||
else if (!type)
|
else if (!type)
|
||||||
parserError(string("Location specifier needs explicit type name."));
|
parserError(7439_error, "Location specifier needs explicit type name.");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (token)
|
switch (token)
|
||||||
@ -836,13 +843,13 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
|
|||||||
if (m_scanner->currentToken() == Token::Override)
|
if (m_scanner->currentToken() == Token::Override)
|
||||||
{
|
{
|
||||||
if (overrides)
|
if (overrides)
|
||||||
parserError("Override already specified.");
|
parserError(9102_error, "Override already specified.");
|
||||||
overrides = parseOverrideSpecifier();
|
overrides = parseOverrideSpecifier();
|
||||||
}
|
}
|
||||||
else if (m_scanner->currentToken() == Token::Virtual)
|
else if (m_scanner->currentToken() == Token::Virtual)
|
||||||
{
|
{
|
||||||
if (isVirtual)
|
if (isVirtual)
|
||||||
parserError("Virtual already specified.");
|
parserError(2662_error, "Virtual already specified.");
|
||||||
|
|
||||||
isVirtual = true;
|
isVirtual = true;
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
@ -990,7 +997,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parserError("State mutability can only be specified for address types.");
|
parserError(9106_error, "State mutability can only be specified for address types.");
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -999,7 +1006,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
|||||||
else if (token == Token::Var)
|
else if (token == Token::Var)
|
||||||
{
|
{
|
||||||
if (!_allowVar)
|
if (!_allowVar)
|
||||||
parserError(string("Expected explicit type name."));
|
parserError(7059_error, "Expected explicit type name.");
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
else if (token == Token::Function)
|
else if (token == Token::Function)
|
||||||
@ -1009,7 +1016,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
|||||||
else if (token == Token::Identifier)
|
else if (token == Token::Identifier)
|
||||||
type = parseUserDefinedTypeName();
|
type = parseUserDefinedTypeName();
|
||||||
else
|
else
|
||||||
fatalParserError(string("Expected type name"));
|
fatalParserError(3546_error, "Expected type name");
|
||||||
|
|
||||||
if (type)
|
if (type)
|
||||||
// Parse "[...]" postfixes for arrays.
|
// Parse "[...]" postfixes for arrays.
|
||||||
@ -1052,7 +1059,7 @@ ASTPointer<Mapping> Parser::parseMapping()
|
|||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fatalParserError(string("Expected elementary type name or identifier for mapping key type"));
|
fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type");
|
||||||
expectToken(Token::Arrow);
|
expectToken(Token::Arrow);
|
||||||
bool const allowVar = false;
|
bool const allowVar = false;
|
||||||
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
|
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
|
||||||
@ -1078,7 +1085,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
|
|||||||
while (m_scanner->currentToken() != Token::RParen)
|
while (m_scanner->currentToken() != Token::RParen)
|
||||||
{
|
{
|
||||||
if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen)
|
if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen)
|
||||||
fatalParserError("Unexpected trailing comma in parameter list.");
|
fatalParserError(7591_error, "Unexpected trailing comma in parameter list.");
|
||||||
expectToken(Token::Comma);
|
expectToken(Token::Comma);
|
||||||
parameters.push_back(parseVariableDeclaration(options));
|
parameters.push_back(parseVariableDeclaration(options));
|
||||||
}
|
}
|
||||||
@ -1213,7 +1220,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
|||||||
if (m_scanner->currentToken() == Token::StringLiteral)
|
if (m_scanner->currentToken() == Token::StringLiteral)
|
||||||
{
|
{
|
||||||
if (m_scanner->currentLiteral() != "evmasm")
|
if (m_scanner->currentLiteral() != "evmasm")
|
||||||
fatalParserError("Only \"evmasm\" supported.");
|
fatalParserError(4531_error, "Only \"evmasm\" supported.");
|
||||||
// This can be used in the future to set the dialect.
|
// This can be used in the future to set the dialect.
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
@ -1376,7 +1383,7 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
|
|||||||
ASTNodeFactory eventCallNodeFactory(*this);
|
ASTNodeFactory eventCallNodeFactory(*this);
|
||||||
|
|
||||||
if (m_scanner->currentToken() != Token::Identifier)
|
if (m_scanner->currentToken() != Token::Identifier)
|
||||||
fatalParserError("Expected event name or path.");
|
fatalParserError(5620_error, "Expected event name or path.");
|
||||||
|
|
||||||
IndexAccessedPath iap;
|
IndexAccessedPath iap;
|
||||||
while (true)
|
while (true)
|
||||||
@ -1844,7 +1851,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
if (m_scanner->currentToken() == Token::Illegal)
|
if (m_scanner->currentToken() == Token::Illegal)
|
||||||
fatalParserError(to_string(m_scanner->currentError()));
|
fatalParserError(5428_error, to_string(m_scanner->currentError()));
|
||||||
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
|
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1875,7 +1882,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|||||||
if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken)
|
if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken)
|
||||||
components.push_back(parseExpression());
|
components.push_back(parseExpression());
|
||||||
else if (isArray)
|
else if (isArray)
|
||||||
parserError("Expected expression (inline array elements cannot be omitted).");
|
parserError(4799_error, "Expected expression (inline array elements cannot be omitted).");
|
||||||
else
|
else
|
||||||
components.push_back(ASTPointer<Expression>());
|
components.push_back(ASTPointer<Expression>());
|
||||||
|
|
||||||
@ -1890,7 +1897,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Token::Illegal:
|
case Token::Illegal:
|
||||||
fatalParserError(to_string(m_scanner->currentError()));
|
fatalParserError(8936_error, to_string(m_scanner->currentError()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (TokenTraits::isElementaryTypeName(token))
|
if (TokenTraits::isElementaryTypeName(token))
|
||||||
@ -1906,7 +1913,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
|
|||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fatalParserError(string("Expected primary expression."));
|
fatalParserError(6933_error, "Expected primary expression.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return expression;
|
return expression;
|
||||||
@ -1964,7 +1971,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
|
|||||||
m_scanner->peekNextToken() == Token::RBrace
|
m_scanner->peekNextToken() == Token::RBrace
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
parserError("Unexpected trailing comma.");
|
parserError(2074_error, "Unexpected trailing comma.");
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2083,7 +2090,7 @@ ASTPointer<TypeName> Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces
|
|||||||
for (auto const& lengthExpression: _iap.indices)
|
for (auto const& lengthExpression: _iap.indices)
|
||||||
{
|
{
|
||||||
if (lengthExpression.end)
|
if (lengthExpression.end)
|
||||||
parserError(lengthExpression.location, "Expected array length expression.");
|
parserError(5464_error, lengthExpression.location, "Expected array length expression.");
|
||||||
nodeFactory.setLocation(lengthExpression.location);
|
nodeFactory.setLocation(lengthExpression.location);
|
||||||
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.start);
|
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.start);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <libsolutil/Common.h>
|
#include <libsolutil/Common.h>
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -527,7 +527,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
|||||||
// Similarly we assume bitwise shifting and create2 go together.
|
// Similarly we assume bitwise shifting and create2 go together.
|
||||||
yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
|
yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
|
||||||
|
|
||||||
auto errorForVM = [=](string const& vmKindMessage) {
|
auto errorForVM = [&](string const& vmKindMessage) {
|
||||||
typeError(
|
typeError(
|
||||||
_location,
|
_location,
|
||||||
"The \"" +
|
"The \"" +
|
||||||
@ -583,6 +583,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
m_errorReporter.error(
|
m_errorReporter.error(
|
||||||
|
4316_error,
|
||||||
Error::Type::SyntaxError,
|
Error::Type::SyntaxError,
|
||||||
_location,
|
_location,
|
||||||
"Jump instructions and labels are low-level EVM features that can lead to "
|
"Jump instructions and labels are low-level EVM features that can lead to "
|
||||||
@ -599,13 +600,13 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
|
|||||||
|
|
||||||
void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description)
|
void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(_location, _description);
|
m_errorReporter.typeError(7569_error, _location, _description);
|
||||||
m_success = false;
|
m_success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description)
|
void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.declarationError(_location, _description);
|
m_errorReporter.declarationError(9595_error, _location, _description);
|
||||||
m_success = false;
|
m_success = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
* Converts inline assembly AST to JSON format
|
* Converts inline assembly AST to JSON format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <libyul/AsmDataForward.h>
|
#include <libyul/AsmDataForward.h>
|
||||||
#include <liblangutil/SourceLocation.h>
|
#include <liblangutil/SourceLocation.h>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
@ -122,11 +122,11 @@ Statement Parser::parseStatement()
|
|||||||
if (currentToken() == Token::Default)
|
if (currentToken() == Token::Default)
|
||||||
_switch.cases.emplace_back(parseCase());
|
_switch.cases.emplace_back(parseCase());
|
||||||
if (currentToken() == Token::Default)
|
if (currentToken() == Token::Default)
|
||||||
fatalParserError("Only one default case allowed.");
|
fatalParserError(6931_error, "Only one default case allowed.");
|
||||||
else if (currentToken() == Token::Case)
|
else if (currentToken() == Token::Case)
|
||||||
fatalParserError("Case not allowed after default case.");
|
fatalParserError(4904_error, "Case not allowed after default case.");
|
||||||
if (_switch.cases.empty())
|
if (_switch.cases.empty())
|
||||||
fatalParserError("Switch statement without any cases.");
|
fatalParserError(2418_error, "Switch statement without any cases.");
|
||||||
_switch.location.end = _switch.cases.back().body.location.end;
|
_switch.location.end = _switch.cases.back().body.location.end;
|
||||||
return Statement{move(_switch)};
|
return Statement{move(_switch)};
|
||||||
}
|
}
|
||||||
@ -151,7 +151,7 @@ Statement Parser::parseStatement()
|
|||||||
{
|
{
|
||||||
Statement stmt{createWithLocation<Leave>()};
|
Statement stmt{createWithLocation<Leave>()};
|
||||||
if (!m_insideFunction)
|
if (!m_insideFunction)
|
||||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"leave\" can only be used inside a function.");
|
m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function.");
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
@ -184,6 +184,7 @@ Statement Parser::parseStatement()
|
|||||||
auto const token = currentToken() == Token::Comma ? "," : ":=";
|
auto const token = currentToken() == Token::Comma ? "," : ":=";
|
||||||
|
|
||||||
fatalParserError(
|
fatalParserError(
|
||||||
|
2856_error,
|
||||||
std::string("Variable name must precede \"") +
|
std::string("Variable name must precede \"") +
|
||||||
token +
|
token +
|
||||||
"\"" +
|
"\"" +
|
||||||
@ -194,7 +195,7 @@ Statement Parser::parseStatement()
|
|||||||
auto const& identifier = std::get<Identifier>(elementary);
|
auto const& identifier = std::get<Identifier>(elementary);
|
||||||
|
|
||||||
if (m_dialect.builtin(identifier.name))
|
if (m_dialect.builtin(identifier.name))
|
||||||
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
|
fatalParserError(6272_error, "Cannot assign to builtin function \"" + identifier.name.str() + "\".");
|
||||||
|
|
||||||
variableNames.emplace_back(identifier);
|
variableNames.emplace_back(identifier);
|
||||||
|
|
||||||
@ -218,7 +219,7 @@ Statement Parser::parseStatement()
|
|||||||
return Statement{std::move(assignment)};
|
return Statement{std::move(assignment)};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fatalParserError("Call or assignment expected.");
|
fatalParserError(6913_error, "Call or assignment expected.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +251,7 @@ Case Parser::parseCase()
|
|||||||
advance();
|
advance();
|
||||||
ElementaryOperation literal = parseElementaryOperation();
|
ElementaryOperation literal = parseElementaryOperation();
|
||||||
if (!holds_alternative<Literal>(literal))
|
if (!holds_alternative<Literal>(literal))
|
||||||
fatalParserError("Literal expected.");
|
fatalParserError(4805_error, "Literal expected.");
|
||||||
_case.value = make_unique<Literal>(std::get<Literal>(std::move(literal)));
|
_case.value = make_unique<Literal>(std::get<Literal>(std::move(literal)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -325,6 +326,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
|||||||
case Token::Bool:
|
case Token::Bool:
|
||||||
case Token::Address:
|
case Token::Address:
|
||||||
case Token::Var:
|
case Token::Var:
|
||||||
|
case Token::In:
|
||||||
{
|
{
|
||||||
YulString literal{currentLiteral()};
|
YulString literal{currentLiteral()};
|
||||||
if (m_dialect.builtin(literal))
|
if (m_dialect.builtin(literal))
|
||||||
@ -352,7 +354,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
|||||||
break;
|
break;
|
||||||
case Token::Number:
|
case Token::Number:
|
||||||
if (!isValidNumberLiteral(currentLiteral()))
|
if (!isValidNumberLiteral(currentLiteral()))
|
||||||
fatalParserError("Invalid number literal.");
|
fatalParserError(4828_error, "Invalid number literal.");
|
||||||
kind = LiteralKind::Number;
|
kind = LiteralKind::Number;
|
||||||
break;
|
break;
|
||||||
case Token::TrueLiteral:
|
case Token::TrueLiteral:
|
||||||
@ -381,7 +383,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
fatalParserError("Literal or identifier expected.");
|
fatalParserError(1856_error, "Literal or identifier expected.");
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -416,6 +418,7 @@ FunctionDefinition Parser::parseFunctionDefinition()
|
|||||||
|
|
||||||
if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre)
|
if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre)
|
||||||
m_errorReporter.syntaxError(
|
m_errorReporter.syntaxError(
|
||||||
|
3441_error,
|
||||||
currentLocation(),
|
currentLocation(),
|
||||||
"Functions cannot be defined inside a for-loop init block."
|
"Functions cannot be defined inside a for-loop init block."
|
||||||
);
|
);
|
||||||
@ -470,7 +473,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
|
|||||||
else if (holds_alternative<FunctionCall>(_initialOp))
|
else if (holds_alternative<FunctionCall>(_initialOp))
|
||||||
ret = std::move(std::get<FunctionCall>(_initialOp));
|
ret = std::move(std::get<FunctionCall>(_initialOp));
|
||||||
else
|
else
|
||||||
fatalParserError("Function name expected.");
|
fatalParserError(9980_error, "Function name expected.");
|
||||||
|
|
||||||
expectToken(Token::LParen);
|
expectToken(Token::LParen);
|
||||||
if (currentToken() != Token::RParen)
|
if (currentToken() != Token::RParen)
|
||||||
@ -515,6 +518,7 @@ YulString Parser::expectAsmIdentifier()
|
|||||||
case Token::Bool:
|
case Token::Bool:
|
||||||
case Token::Identifier:
|
case Token::Identifier:
|
||||||
case Token::Var:
|
case Token::Var:
|
||||||
|
case Token::In:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
expectToken(Token::Identifier);
|
expectToken(Token::Identifier);
|
||||||
@ -522,7 +526,7 @@ YulString Parser::expectAsmIdentifier()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_dialect.builtin(name))
|
if (m_dialect.builtin(name))
|
||||||
fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
|
fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
|
||||||
advance();
|
advance();
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -532,13 +536,13 @@ void Parser::checkBreakContinuePosition(string const& _which)
|
|||||||
switch (m_currentForLoopComponent)
|
switch (m_currentForLoopComponent)
|
||||||
{
|
{
|
||||||
case ForLoopComponent::None:
|
case ForLoopComponent::None:
|
||||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body.");
|
m_errorReporter.syntaxError(2592_error, currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body.");
|
||||||
break;
|
break;
|
||||||
case ForLoopComponent::ForLoopPre:
|
case ForLoopComponent::ForLoopPre:
|
||||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed.");
|
m_errorReporter.syntaxError(9615_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed.");
|
||||||
break;
|
break;
|
||||||
case ForLoopComponent::ForLoopPost:
|
case ForLoopComponent::ForLoopPost:
|
||||||
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed.");
|
m_errorReporter.syntaxError(2461_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed.");
|
||||||
break;
|
break;
|
||||||
case ForLoopComponent::ForLoopBody:
|
case ForLoopComponent::ForLoopBody:
|
||||||
break;
|
break;
|
||||||
|
@ -29,8 +29,6 @@
|
|||||||
|
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@ -141,6 +139,7 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const&
|
|||||||
{
|
{
|
||||||
//@TODO secondary location
|
//@TODO secondary location
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
1395_error,
|
||||||
_location,
|
_location,
|
||||||
"Variable name " + _name.name.str() + " already taken in this scope."
|
"Variable name " + _name.name.str() + " already taken in this scope."
|
||||||
);
|
);
|
||||||
@ -161,6 +160,7 @@ bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef)
|
|||||||
{
|
{
|
||||||
//@TODO secondary location
|
//@TODO secondary location
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
|
6052_error,
|
||||||
_funDef.location,
|
_funDef.location,
|
||||||
"Function name " + _funDef.name.str() + " already taken in this scope."
|
"Function name " + _funDef.name.str() + " already taken in this scope."
|
||||||
);
|
);
|
||||||
|
@ -66,7 +66,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
|
|||||||
RecursionGuard guard(*this);
|
RecursionGuard guard(*this);
|
||||||
|
|
||||||
if (currentToken() != Token::Identifier || currentLiteral() != "object")
|
if (currentToken() != Token::Identifier || currentLiteral() != "object")
|
||||||
fatalParserError("Expected keyword \"object\".");
|
fatalParserError(4294_error, "Expected keyword \"object\".");
|
||||||
advance();
|
advance();
|
||||||
|
|
||||||
shared_ptr<Object> ret = make_shared<Object>();
|
shared_ptr<Object> ret = make_shared<Object>();
|
||||||
@ -83,7 +83,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
|
|||||||
else if (currentToken() == Token::Identifier && currentLiteral() == "data")
|
else if (currentToken() == Token::Identifier && currentLiteral() == "data")
|
||||||
parseData(*ret);
|
parseData(*ret);
|
||||||
else
|
else
|
||||||
fatalParserError("Expected keyword \"data\" or \"object\" or \"}\".");
|
fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\".");
|
||||||
}
|
}
|
||||||
if (_containingObject)
|
if (_containingObject)
|
||||||
addNamedSubObject(*_containingObject, ret->name, ret);
|
addNamedSubObject(*_containingObject, ret->name, ret);
|
||||||
@ -96,7 +96,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
|
|||||||
shared_ptr<Block> ObjectParser::parseCode()
|
shared_ptr<Block> ObjectParser::parseCode()
|
||||||
{
|
{
|
||||||
if (currentToken() != Token::Identifier || currentLiteral() != "code")
|
if (currentToken() != Token::Identifier || currentLiteral() != "code")
|
||||||
fatalParserError("Expected keyword \"code\".");
|
fatalParserError(4846_error, "Expected keyword \"code\".");
|
||||||
advance();
|
advance();
|
||||||
|
|
||||||
return parseBlock();
|
return parseBlock();
|
||||||
@ -133,11 +133,11 @@ YulString ObjectParser::parseUniqueName(Object const* _containingObject)
|
|||||||
expectToken(Token::StringLiteral, false);
|
expectToken(Token::StringLiteral, false);
|
||||||
YulString name{currentLiteral()};
|
YulString name{currentLiteral()};
|
||||||
if (name.empty())
|
if (name.empty())
|
||||||
parserError("Object name cannot be empty.");
|
parserError(3287_error, "Object name cannot be empty.");
|
||||||
else if (_containingObject && _containingObject->name == name)
|
else if (_containingObject && _containingObject->name == name)
|
||||||
parserError("Object name cannot be the same as the name of the containing object.");
|
parserError(8311_error, "Object name cannot be the same as the name of the containing object.");
|
||||||
else if (_containingObject && _containingObject->subIndexByName.count(name))
|
else if (_containingObject && _containingObject->subIndexByName.count(name))
|
||||||
parserError("Object name \"" + name.str() + "\" already exists inside the containing object.");
|
parserError(8794_error, "Object name \"" + name.str() + "\" already exists inside the containing object.");
|
||||||
advance();
|
advance();
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,6 @@ using namespace solidity;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
|
|
||||||
using boost::split;
|
|
||||||
using boost::is_any_of;
|
|
||||||
|
|
||||||
string solidity::yul::reindent(string const& _code)
|
string solidity::yul::reindent(string const& _code)
|
||||||
{
|
{
|
||||||
int constexpr indentationWidth = 4;
|
int constexpr indentationWidth = 4;
|
||||||
@ -55,7 +52,7 @@ string solidity::yul::reindent(string const& _code)
|
|||||||
};
|
};
|
||||||
|
|
||||||
vector<string> lines;
|
vector<string> lines;
|
||||||
split(lines, _code, is_any_of("\n"));
|
boost::split(lines, _code, boost::is_any_of("\n"));
|
||||||
for (string& line: lines)
|
for (string& line: lines)
|
||||||
boost::trim(line);
|
boost::trim(line);
|
||||||
|
|
||||||
|
@ -85,11 +85,11 @@ void VariableReferenceCounter::operator()(Block const& _block)
|
|||||||
void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
|
void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
|
||||||
{
|
{
|
||||||
m_scope->lookup(_variableName, GenericVisitor{
|
m_scope->lookup(_variableName, GenericVisitor{
|
||||||
[=](Scope::Variable const& _var)
|
[&](Scope::Variable const& _var)
|
||||||
{
|
{
|
||||||
++m_context.variableReferences[&_var];
|
++m_context.variableReferences[&_var];
|
||||||
},
|
},
|
||||||
[=](Scope::Function const&) { }
|
[](Scope::Function const&) { }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,11 +184,13 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
m_assembly.setSourceLocation(_varDecl.location);
|
||||||
int variablesLeft = numVariables;
|
int variablesLeft = numVariables;
|
||||||
while (variablesLeft--)
|
while (variablesLeft--)
|
||||||
m_assembly.appendConstant(u256(0));
|
m_assembly.appendConstant(u256(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_assembly.setSourceLocation(_varDecl.location);
|
||||||
bool atTopOfStack = true;
|
bool atTopOfStack = true;
|
||||||
for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex)
|
for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex)
|
||||||
{
|
{
|
||||||
@ -203,7 +205,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
|||||||
if (atTopOfStack)
|
if (atTopOfStack)
|
||||||
{
|
{
|
||||||
m_context->variableStackHeights.erase(&var);
|
m_context->variableStackHeights.erase(&var);
|
||||||
m_assembly.setSourceLocation(_varDecl.location);
|
|
||||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -216,7 +217,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
|||||||
int slot = *m_unusedStackSlots.begin();
|
int slot = *m_unusedStackSlots.begin();
|
||||||
m_unusedStackSlots.erase(m_unusedStackSlots.begin());
|
m_unusedStackSlots.erase(m_unusedStackSlots.begin());
|
||||||
m_context->variableStackHeights[&var] = slot;
|
m_context->variableStackHeights[&var] = slot;
|
||||||
m_assembly.setSourceLocation(_varDecl.location);
|
|
||||||
if (int heightDiff = variableHeightDiff(var, varName, true))
|
if (int heightDiff = variableHeightDiff(var, varName, true))
|
||||||
m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1));
|
m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1));
|
||||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||||
@ -272,7 +272,7 @@ void CodeTransform::operator()(FunctionCall const& _call)
|
|||||||
|
|
||||||
Scope::Function* function = nullptr;
|
Scope::Function* function = nullptr;
|
||||||
yulAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{
|
yulAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{
|
||||||
[=](Scope::Variable&) { yulAssert(false, "Expected function name."); },
|
[](Scope::Variable&) { yulAssert(false, "Expected function name."); },
|
||||||
[&](Scope::Function& _function) { function = &_function; }
|
[&](Scope::Function& _function) { function = &_function; }
|
||||||
}), "Function name not found.");
|
}), "Function name not found.");
|
||||||
yulAssert(function, "");
|
yulAssert(function, "");
|
||||||
@ -296,7 +296,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
|
|||||||
// First search internals, then externals.
|
// First search internals, then externals.
|
||||||
yulAssert(m_scope, "");
|
yulAssert(m_scope, "");
|
||||||
if (m_scope->lookup(_identifier.name, GenericVisitor{
|
if (m_scope->lookup(_identifier.name, GenericVisitor{
|
||||||
[=](Scope::Variable& _var)
|
[&](Scope::Variable& _var)
|
||||||
{
|
{
|
||||||
// TODO: opportunity for optimization: Do not DUP if this is the last reference
|
// TODO: opportunity for optimization: Do not DUP if this is the last reference
|
||||||
// to the top most element of the stack
|
// to the top most element of the stack
|
||||||
@ -307,7 +307,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
|
|||||||
m_assembly.appendConstant(u256(0));
|
m_assembly.appendConstant(u256(0));
|
||||||
decreaseReference(_identifier.name, _var);
|
decreaseReference(_identifier.name, _var);
|
||||||
},
|
},
|
||||||
[=](Scope::Function&)
|
[](Scope::Function&)
|
||||||
{
|
{
|
||||||
yulAssert(false, "Function not removed during desugaring.");
|
yulAssert(false, "Function not removed during desugaring.");
|
||||||
}
|
}
|
||||||
|
@ -1232,7 +1232,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
|
|||||||
|
|
||||||
FunctionHoister::run(context, ast);
|
FunctionHoister::run(context, ast);
|
||||||
FunctionGrouper::run(context, ast);
|
FunctionGrouper::run(context, ast);
|
||||||
MainFunction{}(ast);
|
MainFunction::run(context, ast);
|
||||||
ForLoopConditionIntoBody::run(context, ast);
|
ForLoopConditionIntoBody::run(context, ast);
|
||||||
ExpressionSplitter::run(context, ast);
|
ExpressionSplitter::run(context, ast);
|
||||||
WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser);
|
WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser);
|
||||||
|
@ -29,9 +29,6 @@
|
|||||||
|
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
#include <boost/range/adaptor/transformed.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
#include <boost/range/algorithm_ext/erase.hpp>
|
#include <boost/range/algorithm_ext/erase.hpp>
|
||||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
@ -60,8 +59,9 @@ void removeEmptyDefaultFromSwitch(Switch& _switchStmt)
|
|||||||
|
|
||||||
void removeEmptyCasesFromSwitch(Switch& _switchStmt)
|
void removeEmptyCasesFromSwitch(Switch& _switchStmt)
|
||||||
{
|
{
|
||||||
bool hasDefault = boost::algorithm::any_of(
|
bool hasDefault = std::any_of(
|
||||||
_switchStmt.cases,
|
_switchStmt.cases.begin(),
|
||||||
|
_switchStmt.cases.end(),
|
||||||
[](Case const& _case) { return !_case.value; }
|
[](Case const& _case) { return !_case.value; }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -31,8 +31,6 @@
|
|||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
|
@ -34,8 +34,6 @@
|
|||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
|
@ -4,6 +4,7 @@ planned state of the optimiser.
|
|||||||
|
|
||||||
Table of Contents:
|
Table of Contents:
|
||||||
|
|
||||||
|
- [Selecting optimisations](#selecting-optimisations)
|
||||||
- [Preprocessing](#preprocessing)
|
- [Preprocessing](#preprocessing)
|
||||||
- [Pseudo-SSA Transformation](#pseudo-ssa-transformation)
|
- [Pseudo-SSA Transformation](#pseudo-ssa-transformation)
|
||||||
- [Tools](#tools)
|
- [Tools](#tools)
|
||||||
@ -33,6 +34,17 @@ the following transformation steps are the main components:
|
|||||||
- [Redundant Assign Eliminator](#redundant-assign-eliminator)
|
- [Redundant Assign Eliminator](#redundant-assign-eliminator)
|
||||||
- [Full Function Inliner](#full-function-inliner)
|
- [Full Function Inliner](#full-function-inliner)
|
||||||
|
|
||||||
|
## Selecting optimisations
|
||||||
|
|
||||||
|
By default the optimiser applies its predefined sequence of optimisation steps to the generated assembly.
|
||||||
|
You can override this sequence and supply your own using the `--yul-optimizations` option:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
|
||||||
|
```
|
||||||
|
|
||||||
|
There's a [table listing available abbreviations in the optimiser docs](/docs/yul.rst#optimization-step-sequence).
|
||||||
|
|
||||||
## Preprocessing
|
## Preprocessing
|
||||||
|
|
||||||
The preprocessing components perform transformations to get the program
|
The preprocessing components perform transformations to get the program
|
||||||
|
@ -306,7 +306,7 @@ void RedundantAssignEliminator::finalize(YulString _variable, RedundantAssignEli
|
|||||||
|
|
||||||
void AssignmentRemover::operator()(Block& _block)
|
void AssignmentRemover::operator()(Block& _block)
|
||||||
{
|
{
|
||||||
boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool {
|
boost::range::remove_erase_if(_block.statements, [&](Statement const& _statement) -> bool {
|
||||||
return holds_alternative<Assignment>(_statement) && m_toRemove.count(&std::get<Assignment>(_statement));
|
return holds_alternative<Assignment>(_statement) && m_toRemove.count(&std::get<Assignment>(_statement));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include <libsolutil/Visitor.h>
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
#include <boost/range/algorithm_ext/erase.hpp>
|
#include <boost/range/algorithm_ext/erase.hpp>
|
||||||
#include <boost/algorithm/cxx11/any_of.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
|
@ -29,8 +29,6 @@
|
|||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
#include <libyul/SideEffects.h>
|
#include <libyul/SideEffects.h>
|
||||||
|
|
||||||
#include <boost/algorithm/cxx11/none_of.hpp>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
@ -85,9 +83,10 @@ void UnusedPruner::operator()(Block& _block)
|
|||||||
// movable or it returns a single value. In the latter case, we
|
// movable or it returns a single value. In the latter case, we
|
||||||
// replace `let a := f()` by `pop(f())` (in pure Yul, this will be
|
// replace `let a := f()` by `pop(f())` (in pure Yul, this will be
|
||||||
// `drop(f())`).
|
// `drop(f())`).
|
||||||
if (boost::algorithm::none_of(
|
if (std::none_of(
|
||||||
varDecl.variables,
|
varDecl.variables.begin(),
|
||||||
[=](TypedName const& _typedName) { return used(_typedName.name); }
|
varDecl.variables.end(),
|
||||||
|
[&](TypedName const& _typedName) { return used(_typedName.name); }
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
if (!varDecl.value)
|
if (!varDecl.value)
|
||||||
|
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
|
set -e
|
||||||
cd docs
|
cd docs
|
||||||
pip install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
sphinx-build -nW -b html -d _build/doctrees . _build/html
|
sphinx-build -nW -b html -d _build/doctrees . _build/html
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -100,7 +100,7 @@ do
|
|||||||
force_abiv2_flag=""
|
force_abiv2_flag=""
|
||||||
if [[ "$abiv2" == "yes" ]]
|
if [[ "$abiv2" == "yes" ]]
|
||||||
then
|
then
|
||||||
force_abiv2_flag="--abiencoderv2 --optimize-yul"
|
force_abiv2_flag="--abiencoderv2"
|
||||||
fi
|
fi
|
||||||
printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv2_flag..."
|
printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv2_flag..."
|
||||||
|
|
||||||
|
@ -699,7 +699,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
|
|||||||
string pathName = (p / _fileName).string();
|
string pathName = (p / _fileName).string();
|
||||||
if (fs::exists(pathName) && !m_args.count(g_strOverwrite))
|
if (fs::exists(pathName) && !m_args.count(g_strOverwrite))
|
||||||
{
|
{
|
||||||
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl;
|
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl;
|
||||||
m_error = true;
|
m_error = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -719,10 +719,10 @@ bool CommandLineInterface::parseArguments(int _argc, char** _argv)
|
|||||||
g_hasOutput = false;
|
g_hasOutput = false;
|
||||||
|
|
||||||
// Declare the supported options.
|
// Declare the supported options.
|
||||||
po::options_description desc(R"(solc, the Solidity commandline compiler.
|
po::options_description desc((R"(solc, the Solidity commandline compiler.
|
||||||
|
|
||||||
This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you
|
This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you
|
||||||
are welcome to redistribute it under certain conditions. See 'solc --license'
|
are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"('
|
||||||
for details.
|
for details.
|
||||||
|
|
||||||
Usage: solc [options] [input_file...]
|
Usage: solc [options] [input_file...]
|
||||||
@ -732,9 +732,9 @@ at standard output or in files in the output directory, if specified.
|
|||||||
Imports are automatically read from the filesystem, but it is also possible to
|
Imports are automatically read from the filesystem, but it is also possible to
|
||||||
remap paths using the context:prefix=path syntax.
|
remap paths using the context:prefix=path syntax.
|
||||||
Example:
|
Example:
|
||||||
solc --bin -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
|
solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
|
||||||
|
|
||||||
Allowed options)",
|
Allowed options)").c_str(),
|
||||||
po::options_description::m_default_line_length,
|
po::options_description::m_default_line_length,
|
||||||
po::options_description::m_default_line_length - 23
|
po::options_description::m_default_line_length - 23
|
||||||
);
|
);
|
||||||
@ -780,20 +780,27 @@ Allowed options)",
|
|||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argImportAst.c_str(),
|
g_argImportAst.c_str(),
|
||||||
"Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
|
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
|
||||||
"Supported Inputs is the output of the --standard-json or the one produced by --combined-json ast,compact-format"
|
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
|
||||||
|
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argAssemble.c_str(),
|
g_argAssemble.c_str(),
|
||||||
"Switch to assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is assembly."
|
("Switch to assembly mode, ignoring all options except "
|
||||||
|
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
|
||||||
|
"and assumes input is assembly.").c_str()
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argYul.c_str(),
|
g_argYul.c_str(),
|
||||||
"Switch to Yul mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is Yul."
|
("Switch to Yul mode, ignoring all options except "
|
||||||
|
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
|
||||||
|
"and assumes input is Yul.").c_str()
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argStrictAssembly.c_str(),
|
g_argStrictAssembly.c_str(),
|
||||||
"Switch to strict assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is strict assembly."
|
("Switch to strict assembly mode, ignoring all options except "
|
||||||
|
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
|
||||||
|
"and assumes input is strict assembly.").c_str()
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_strYulDialect.c_str(),
|
g_strYulDialect.c_str(),
|
||||||
@ -807,8 +814,8 @@ Allowed options)",
|
|||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argLink.c_str(),
|
g_argLink.c_str(),
|
||||||
"Switch to linker mode, ignoring all options apart from --libraries "
|
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
|
||||||
"and modify binaries in place."
|
"and modify binaries in place.").c_str()
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
g_argMetadataHash.c_str(),
|
g_argMetadataHash.c_str(),
|
||||||
@ -835,7 +842,7 @@ Allowed options)",
|
|||||||
"Set for how many contract runs to optimize. "
|
"Set for how many contract runs to optimize. "
|
||||||
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
|
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
|
||||||
)
|
)
|
||||||
(g_strOptimizeYul.c_str(), "Legacy option, ignored. Use the general --optimize to enable Yul optimizer.")
|
(g_strOptimizeYul.c_str(), ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str())
|
||||||
(g_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.")
|
(g_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.")
|
||||||
(
|
(
|
||||||
g_strYulOptimizations.c_str(),
|
g_strYulOptimizations.c_str(),
|
||||||
@ -933,7 +940,7 @@ Allowed options)",
|
|||||||
for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(",")))
|
for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(",")))
|
||||||
if (!g_combinedJsonArgs.count(item))
|
if (!g_combinedJsonArgs.count(item))
|
||||||
{
|
{
|
||||||
serr() << "Invalid option to --combined-json: " << item << endl;
|
serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1047,7 +1054,7 @@ bool CommandLineInterface::processInput()
|
|||||||
std::optional<langutil::EVMVersion> versionOption = langutil::EVMVersion::fromString(versionOptionStr);
|
std::optional<langutil::EVMVersion> versionOption = langutil::EVMVersion::fromString(versionOptionStr);
|
||||||
if (!versionOption)
|
if (!versionOption)
|
||||||
{
|
{
|
||||||
serr() << "Invalid option for --evm-version: " << versionOptionStr << endl;
|
serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_evmVersion = *versionOption;
|
m_evmVersion = *versionOption;
|
||||||
@ -1064,14 +1071,37 @@ bool CommandLineInterface::processInput()
|
|||||||
bool optimize = m_args.count(g_argOptimize);
|
bool optimize = m_args.count(g_argOptimize);
|
||||||
if (m_args.count(g_strOptimizeYul))
|
if (m_args.count(g_strOptimizeYul))
|
||||||
{
|
{
|
||||||
serr() << "--optimize-yul is invalid in assembly mode. Use --optimize instead." << endl;
|
serr() << "--" << g_strOptimizeYul << " is invalid in assembly mode. Use --" << g_argOptimize << " instead." << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (m_args.count(g_strNoOptimizeYul))
|
if (m_args.count(g_strNoOptimizeYul))
|
||||||
{
|
{
|
||||||
serr() << "--no-optimize-yul is invalid in assembly mode. Optimization is disabled by default and can be enabled with --optimize." << endl;
|
serr() << "--" << g_strNoOptimizeYul << " is invalid in assembly mode. Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optional<string> yulOptimiserSteps;
|
||||||
|
if (m_args.count(g_strYulOptimizations))
|
||||||
|
{
|
||||||
|
if (!optimize)
|
||||||
|
{
|
||||||
|
serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as<string>());
|
||||||
|
}
|
||||||
|
catch (yul::OptimizerException const& _exception)
|
||||||
|
{
|
||||||
|
serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
yulOptimiserSteps = m_args[g_strYulOptimizations].as<string>();
|
||||||
|
}
|
||||||
|
|
||||||
if (m_args.count(g_argMachine))
|
if (m_args.count(g_argMachine))
|
||||||
{
|
{
|
||||||
string machine = m_args[g_argMachine].as<string>();
|
string machine = m_args[g_argMachine].as<string>();
|
||||||
@ -1083,7 +1113,7 @@ bool CommandLineInterface::processInput()
|
|||||||
targetMachine = Machine::Ewasm;
|
targetMachine = Machine::Ewasm;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serr() << "Invalid option for --machine: " << machine << endl;
|
serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1099,13 +1129,14 @@ bool CommandLineInterface::processInput()
|
|||||||
inputLanguage = Input::Ewasm;
|
inputLanguage = Input::Ewasm;
|
||||||
if (targetMachine != Machine::Ewasm)
|
if (targetMachine != Machine::Ewasm)
|
||||||
{
|
{
|
||||||
serr() << "If you select Ewasm as --yul-dialect, --machine has to be Ewasm as well." << endl;
|
serr() << "If you select Ewasm as --" << g_strYulDialect << ", ";
|
||||||
|
serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serr() << "Invalid option for --yul-dialect: " << dialect << endl;
|
serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1122,7 +1153,7 @@ bool CommandLineInterface::processInput()
|
|||||||
"Warning: Yul is still experimental. Please use the output with care." <<
|
"Warning: Yul is still experimental. Please use the output with care." <<
|
||||||
endl;
|
endl;
|
||||||
|
|
||||||
return assemble(inputLanguage, targetMachine, optimize);
|
return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps);
|
||||||
}
|
}
|
||||||
if (m_args.count(g_argLink))
|
if (m_args.count(g_argLink))
|
||||||
{
|
{
|
||||||
@ -1142,7 +1173,7 @@ bool CommandLineInterface::processInput()
|
|||||||
m_metadataHash = CompilerStack::MetadataHash::None;
|
m_metadataHash = CompilerStack::MetadataHash::None;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serr() << "Invalid option for --metadata-hash: " << hashStr << endl;
|
serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1534,18 +1565,21 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _
|
|||||||
bool CommandLineInterface::assemble(
|
bool CommandLineInterface::assemble(
|
||||||
yul::AssemblyStack::Language _language,
|
yul::AssemblyStack::Language _language,
|
||||||
yul::AssemblyStack::Machine _targetMachine,
|
yul::AssemblyStack::Machine _targetMachine,
|
||||||
bool _optimize
|
bool _optimize,
|
||||||
|
optional<string> _yulOptimiserSteps
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
solAssert(_optimize || !_yulOptimiserSteps.has_value(), "");
|
||||||
|
|
||||||
bool successful = true;
|
bool successful = true;
|
||||||
map<string, yul::AssemblyStack> assemblyStacks;
|
map<string, yul::AssemblyStack> assemblyStacks;
|
||||||
for (auto const& src: m_sourceCodes)
|
for (auto const& src: m_sourceCodes)
|
||||||
{
|
{
|
||||||
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(
|
OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal();
|
||||||
m_evmVersion,
|
if (_yulOptimiserSteps.has_value())
|
||||||
_language,
|
settings.yulOptimiserSteps = _yulOptimiserSteps.value();
|
||||||
_optimize ? OptimiserSettings::full() : OptimiserSettings::minimal()
|
|
||||||
);
|
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!stack.parseAndAnalyze(src.first, src.second))
|
if (!stack.parseAndAnalyze(src.first, src.second))
|
||||||
|
@ -56,7 +56,12 @@ private:
|
|||||||
/// @returns the full object with library placeholder hints in hex.
|
/// @returns the full object with library placeholder hints in hex.
|
||||||
static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj);
|
static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj);
|
||||||
|
|
||||||
bool assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize);
|
bool assemble(
|
||||||
|
yul::AssemblyStack::Language _language,
|
||||||
|
yul::AssemblyStack::Machine _targetMachine,
|
||||||
|
bool _optimize,
|
||||||
|
std::optional<std::string> _yulOptimiserSteps = std::nullopt
|
||||||
|
);
|
||||||
|
|
||||||
void outputCompilationResults();
|
void outputCompilationResults();
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ add_executable(soltest ${sources}
|
|||||||
${libsolidity_util_sources}
|
${libsolidity_util_sources}
|
||||||
${yul_phaser_sources}
|
${yul_phaser_sources}
|
||||||
)
|
)
|
||||||
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::program_options Boost::unit_test_framework evmc)
|
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::filesystem Boost::program_options Boost::unit_test_framework evmc)
|
||||||
|
|
||||||
|
|
||||||
# Special compilation flag for Visual Studio (version 2019 at least affected)
|
# Special compilation flag for Visual Studio (version 2019 at least affected)
|
||||||
|
@ -94,7 +94,7 @@ CommonOptions::CommonOptions(std::string _caption):
|
|||||||
("evmonepath", po::value<fs::path>(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library")
|
("evmonepath", po::value<fs::path>(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library")
|
||||||
("no-smt", po::bool_switch(&disableSMT), "disable SMT checker")
|
("no-smt", po::bool_switch(&disableSMT), "disable SMT checker")
|
||||||
("optimize", po::bool_switch(&optimize), "enables optimization")
|
("optimize", po::bool_switch(&optimize), "enables optimization")
|
||||||
("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization")
|
("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.")
|
||||||
("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2")
|
("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2")
|
||||||
("show-messages", po::bool_switch(&showMessages), "enables message output")
|
("show-messages", po::bool_switch(&showMessages), "enables message output")
|
||||||
("show-metadata", po::bool_switch(&showMetadata), "enables metadata output");
|
("show-metadata", po::bool_switch(&showMetadata), "enables metadata output");
|
||||||
|
@ -46,7 +46,7 @@ struct CommonOptions: boost::noncopyable
|
|||||||
boost::filesystem::path evmonePath;
|
boost::filesystem::path evmonePath;
|
||||||
boost::filesystem::path testPath;
|
boost::filesystem::path testPath;
|
||||||
bool optimize = false;
|
bool optimize = false;
|
||||||
bool optimizeYul = false;
|
bool enforceViaYul = false;
|
||||||
bool disableSMT = false;
|
bool disableSMT = false;
|
||||||
bool useABIEncoderV2 = false;
|
bool useABIEncoderV2 = false;
|
||||||
bool showMessages = false;
|
bool showMessages = false;
|
||||||
|
@ -49,9 +49,7 @@ ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion):
|
|||||||
m_showMessages(solidity::test::CommonOptions::get().showMessages),
|
m_showMessages(solidity::test::CommonOptions::get().showMessages),
|
||||||
m_evmHost(make_shared<EVMHost>(m_evmVersion))
|
m_evmHost(make_shared<EVMHost>(m_evmVersion))
|
||||||
{
|
{
|
||||||
if (solidity::test::CommonOptions::get().optimizeYul)
|
if (solidity::test::CommonOptions::get().optimize)
|
||||||
m_optimiserSettings = solidity::frontend::OptimiserSettings::full();
|
|
||||||
else if (solidity::test::CommonOptions::get().optimize)
|
|
||||||
m_optimiserSettings = solidity::frontend::OptimiserSettings::standard();
|
m_optimiserSettings = solidity::frontend::OptimiserSettings::standard();
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
@ -40,6 +40,11 @@ void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const
|
|||||||
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl;
|
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix)
|
||||||
|
{
|
||||||
|
printSettings(_stream, _linePrefix);
|
||||||
|
}
|
||||||
|
|
||||||
bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
|
bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
|
||||||
{
|
{
|
||||||
string extension = _filename.extension().string();
|
string extension = _filename.extension().string();
|
||||||
|
@ -38,6 +38,7 @@ public:
|
|||||||
{
|
{
|
||||||
std::string filename;
|
std::string filename;
|
||||||
langutil::EVMVersion evmVersion;
|
langutil::EVMVersion evmVersion;
|
||||||
|
bool enforceCompileViaYul;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TestResult { Success, Failure, FatalError };
|
enum class TestResult { Success, Failure, FatalError };
|
||||||
@ -59,6 +60,8 @@ public:
|
|||||||
virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0;
|
virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0;
|
||||||
/// Outputs settings.
|
/// Outputs settings.
|
||||||
virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
|
virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
|
||||||
|
/// Outputs updated settings
|
||||||
|
virtual void printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix = "");
|
||||||
/// Outputs test expectations to @arg _stream that match the actual results of the test.
|
/// Outputs test expectations to @arg _stream that match the actual results of the test.
|
||||||
/// Each line of output is prefixed with @arg _linePrefix.
|
/// Each line of output is prefixed with @arg _linePrefix.
|
||||||
virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0;
|
virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0;
|
||||||
|
@ -64,12 +64,13 @@ int registerTests(
|
|||||||
boost::unit_test::test_suite& _suite,
|
boost::unit_test::test_suite& _suite,
|
||||||
boost::filesystem::path const& _basepath,
|
boost::filesystem::path const& _basepath,
|
||||||
boost::filesystem::path const& _path,
|
boost::filesystem::path const& _path,
|
||||||
|
bool _enforceViaYul,
|
||||||
TestCase::TestCaseCreator _testCaseCreator
|
TestCase::TestCaseCreator _testCaseCreator
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int numTestsAdded = 0;
|
int numTestsAdded = 0;
|
||||||
fs::path fullpath = _basepath / _path;
|
fs::path fullpath = _basepath / _path;
|
||||||
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion()};
|
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul};
|
||||||
if (fs::is_directory(fullpath))
|
if (fs::is_directory(fullpath))
|
||||||
{
|
{
|
||||||
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
|
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
|
||||||
@ -78,7 +79,12 @@ int registerTests(
|
|||||||
fs::directory_iterator()
|
fs::directory_iterator()
|
||||||
))
|
))
|
||||||
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
|
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
|
||||||
numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename(), _testCaseCreator);
|
numTestsAdded += registerTests(
|
||||||
|
*sub_suite,
|
||||||
|
_basepath, _path / entry.path().filename(),
|
||||||
|
_enforceViaYul,
|
||||||
|
_testCaseCreator
|
||||||
|
);
|
||||||
_suite.add(sub_suite);
|
_suite.add(sub_suite);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -164,6 +170,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
|||||||
master,
|
master,
|
||||||
options.testPath / ts.path,
|
options.testPath / ts.path,
|
||||||
ts.subpath,
|
ts.subpath,
|
||||||
|
options.enforceViaYul,
|
||||||
ts.testCaseCreator
|
ts.testCaseCreator
|
||||||
) > 0, std::string("no ") + ts.title + " tests found");
|
) > 0, std::string("no ") + ts.title + " tests found");
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user