mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #8936 from ethereum/develop
Merge develop into release for 0.6.8.
This commit is contained in:
commit
0bbfe45376
@ -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}"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ include(EthPolicy)
|
|||||||
eth_policy()
|
eth_policy()
|
||||||
|
|
||||||
# project name and version should be set after cmake_policy CMP0048
|
# project name and version should be set after cmake_policy CMP0048
|
||||||
set(PROJECT_VERSION "0.6.7")
|
set(PROJECT_VERSION "0.6.8")
|
||||||
# OSX target needed in order to support std::visit
|
# OSX target needed in order to support std::visit
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
|
||||||
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
||||||
|
27
Changelog.md
27
Changelog.md
@ -1,3 +1,30 @@
|
|||||||
|
### 0.6.8 (2020-05-14)
|
||||||
|
|
||||||
|
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.
|
||||||
|
* Disallow array slices of arrays with dynamically encoded base types.
|
||||||
|
* String literals containing backslash characters can no longer cause incorrect code to be generated when passed directly to function calls or encoding functions when ABIEncoderV2 is active.
|
||||||
|
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* Implemented ``type(T).min`` and ``type(T).max`` for every integer type ``T`` that returns the smallest and largest value representable by the type.
|
||||||
|
|
||||||
|
|
||||||
|
Compiler Features:
|
||||||
|
* Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode.
|
||||||
|
* Allow using abi encoding functions for calldata array slices without explicit casts.
|
||||||
|
* Wasm binary output: Implement ``br`` and ``br_if``.
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* ABI: Skip ``private`` or ``internal`` constructors.
|
||||||
|
* 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.
|
||||||
|
* Type Checker: Checks if a literal exponent in the ``**`` operation is too large or fractional.
|
||||||
|
* Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables.
|
||||||
|
* Yul Assembler: Fix source location of variable declarations without value.
|
||||||
|
|
||||||
|
|
||||||
### 0.6.7 (2020-05-04)
|
### 0.6.7 (2020-05-04)
|
||||||
|
|
||||||
Language Features:
|
Language Features:
|
||||||
|
@ -292,6 +292,7 @@ Consider you have the following pre-0.5.0 contract already deployed:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.4.25;
|
pragma solidity ^0.4.25;
|
||||||
// This will report a warning until version 0.4.25 of the compiler
|
// This will report a warning until version 0.4.25 of the compiler
|
||||||
// This will not compile after 0.5.0
|
// This will not compile after 0.5.0
|
||||||
@ -309,6 +310,7 @@ This will no longer compile with Solidity v0.5.0. However, you can define a comp
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
interface OldContract {
|
interface OldContract {
|
||||||
function someOldFunction(uint8 a) external;
|
function someOldFunction(uint8 a) external;
|
||||||
@ -326,6 +328,7 @@ Given the interface defined above, you can now easily use the already deployed p
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
interface OldContract {
|
interface OldContract {
|
||||||
@ -347,6 +350,7 @@ commandline compiler for linking):
|
|||||||
::
|
::
|
||||||
|
|
||||||
// This will not compile after 0.6.0
|
// This will not compile after 0.6.0
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.5.99;
|
pragma solidity >=0.5.0 <0.5.99;
|
||||||
|
|
||||||
library OldLibrary {
|
library OldLibrary {
|
||||||
@ -370,6 +374,7 @@ Old version:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.4.25;
|
pragma solidity ^0.4.25;
|
||||||
// This will not compile after 0.5.0
|
// This will not compile after 0.5.0
|
||||||
|
|
||||||
@ -432,6 +437,7 @@ New version:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.5.99;
|
pragma solidity >=0.5.0 <0.5.99;
|
||||||
// This will not compile after 0.6.0
|
// This will not compile after 0.6.0
|
||||||
|
|
||||||
|
@ -232,6 +232,7 @@ Given the contract:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract Foo {
|
contract Foo {
|
||||||
@ -535,6 +536,7 @@ For example,
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -583,6 +585,7 @@ As an example, the code
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.19 <0.7.0;
|
pragma solidity >=0.4.19 <0.7.0;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ without a compiler change.
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
library GetCode {
|
library GetCode {
|
||||||
@ -66,6 +67,7 @@ efficient code, for example:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -136,6 +138,7 @@ Local Solidity variables are available for assignments, for example:
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
|
@ -1,4 +1,34 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"name": "MissingEscapingInFormatting",
|
||||||
|
"summary": "String literals containing double backslash characters passed directly to external or encoding function calls can lead to a different string being used when ABIEncoderV2 is enabled.",
|
||||||
|
"description": "When ABIEncoderV2 is enabled, string literals passed directly to encoding functions or external function calls are stored as strings in the intemediate code. Characters outside the printable range are handled correctly, but backslashes are not escaped in this procedure. This leads to double backslashes being reduced to single backslashes and consequently re-interpreted as escapes potentially resulting in a different string being encoded.",
|
||||||
|
"introduced": "0.5.14",
|
||||||
|
"fixed": "0.6.8",
|
||||||
|
"severity": "very low",
|
||||||
|
"conditions": {
|
||||||
|
"ABIEncoderV2": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"summary": "Accessing array slices of arrays with dynamically encoded base types (e.g. multi-dimensional arrays) can result in invalid data being read.",
|
||||||
|
"description": "For arrays with dynamically sized base types, index range accesses that use a start expression that is non-zero will result in invalid array slices. Any index access to such array slices will result in data being read from incorrect calldata offsets. Array slices are only supported for dynamic calldata types and all problematic type require ABIEncoderV2 to be enabled.",
|
||||||
|
"introduced": "0.6.0",
|
||||||
|
"fixed": "0.6.8",
|
||||||
|
"severity": "very low",
|
||||||
|
"conditions": {
|
||||||
|
"ABIEncoderV2": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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,8 @@
|
|||||||
},
|
},
|
||||||
"0.5.14": {
|
"0.5.14": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -905,6 +935,8 @@
|
|||||||
},
|
},
|
||||||
"0.5.15": {
|
"0.5.15": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -914,6 +946,8 @@
|
|||||||
},
|
},
|
||||||
"0.5.16": {
|
"0.5.16": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden"
|
"privateCanBeOverridden"
|
||||||
@ -922,6 +956,8 @@
|
|||||||
},
|
},
|
||||||
"0.5.17": {
|
"0.5.17": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -929,6 +965,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.2": {
|
"0.5.2": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -943,6 +980,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.3": {
|
"0.5.3": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -957,6 +995,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.4": {
|
"0.5.4": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -971,6 +1010,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.5": {
|
"0.5.5": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -987,6 +1027,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.6": {
|
"0.5.6": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1003,6 +1044,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.7": {
|
"0.5.7": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1017,6 +1059,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.8": {
|
"0.5.8": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1030,6 +1073,7 @@
|
|||||||
},
|
},
|
||||||
"0.5.9": {
|
"0.5.9": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"privateCanBeOverridden",
|
"privateCanBeOverridden",
|
||||||
@ -1042,6 +1086,9 @@
|
|||||||
},
|
},
|
||||||
"0.6.0": {
|
"0.6.0": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow",
|
"MemoryArrayCreationOverflow",
|
||||||
"YulOptimizerRedundantAssignmentBreakContinue"
|
"YulOptimizerRedundantAssignmentBreakContinue"
|
||||||
@ -1050,6 +1097,9 @@
|
|||||||
},
|
},
|
||||||
"0.6.1": {
|
"0.6.1": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1057,6 +1107,9 @@
|
|||||||
},
|
},
|
||||||
"0.6.2": {
|
"0.6.2": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1064,6 +1117,9 @@
|
|||||||
},
|
},
|
||||||
"0.6.3": {
|
"0.6.3": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1071,6 +1127,9 @@
|
|||||||
},
|
},
|
||||||
"0.6.4": {
|
"0.6.4": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents",
|
"TupleAssignmentMultiStackSlotComponents",
|
||||||
"MemoryArrayCreationOverflow"
|
"MemoryArrayCreationOverflow"
|
||||||
],
|
],
|
||||||
@ -1078,16 +1137,31 @@
|
|||||||
},
|
},
|
||||||
"0.6.5": {
|
"0.6.5": {
|
||||||
"bugs": [
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck",
|
||||||
"TupleAssignmentMultiStackSlotComponents"
|
"TupleAssignmentMultiStackSlotComponents"
|
||||||
],
|
],
|
||||||
"released": "2020-04-06"
|
"released": "2020-04-06"
|
||||||
},
|
},
|
||||||
"0.6.6": {
|
"0.6.6": {
|
||||||
"bugs": [],
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck"
|
||||||
|
],
|
||||||
"released": "2020-04-09"
|
"released": "2020-04-09"
|
||||||
},
|
},
|
||||||
"0.6.7": {
|
"0.6.7": {
|
||||||
"bugs": [],
|
"bugs": [
|
||||||
|
"MissingEscapingInFormatting",
|
||||||
|
"ArraySliceDynamicallyEncodedBaseType",
|
||||||
|
"ImplicitConstructorCallvalueCheck"
|
||||||
|
],
|
||||||
"released": "2020-05-04"
|
"released": "2020-05-04"
|
||||||
|
},
|
||||||
|
"0.6.8": {
|
||||||
|
"bugs": [],
|
||||||
|
"released": "2020-05-14"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -122,6 +122,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``, ``now`` and ``blockhash`` as a source of randomness,
|
Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,
|
||||||
|
@ -27,6 +27,7 @@ you receive the funds of the person who is now the richest.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract WithdrawalContract {
|
contract WithdrawalContract {
|
||||||
@ -60,6 +61,7 @@ This is as opposed to the more intuitive sending pattern:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract SendContract {
|
contract SendContract {
|
||||||
@ -121,6 +123,7 @@ restrictions highly readable.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract AccessRestriction {
|
contract AccessRestriction {
|
||||||
@ -273,6 +276,7 @@ function finishes.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract StateMachine {
|
contract StateMachine {
|
||||||
|
@ -13,6 +13,7 @@ This can be done by using the ``abstract`` keyword as shown in the following exa
|
|||||||
defined as abstract, because the function ``utterance()`` was defined, but no implementation was
|
defined as abstract, because the function ``utterance()`` was defined, but no implementation was
|
||||||
provided (no implementation body ``{ }`` was given).::
|
provided (no implementation body ``{ }`` was given).::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
abstract contract Feline {
|
abstract contract Feline {
|
||||||
@ -22,6 +23,7 @@ provided (no implementation body ``{ }`` was given).::
|
|||||||
Such abstract contracts can not be instantiated directly. This is also true, if an abstract contract itself does implement
|
Such abstract contracts can not be instantiated directly. This is also true, if an abstract contract itself does implement
|
||||||
all defined functions. The usage of an abstract contract as a base class is shown in the following example::
|
all defined functions. The usage of an abstract contract as a base class is shown in the following example::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
abstract contract Feline {
|
abstract contract Feline {
|
||||||
|
@ -17,6 +17,7 @@ Not all types for constants and immutables are implemented at this time. The onl
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >0.6.4 <0.7.0;
|
pragma solidity >0.6.4 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
|
@ -34,6 +34,7 @@ This means that cyclic creation dependencies are impossible.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ is that they are cheaper to deploy and call.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.21 <0.7.0;
|
pragma solidity >=0.4.21 <0.7.0;
|
||||||
|
|
||||||
contract ClientReceipt {
|
contract ClientReceipt {
|
||||||
@ -138,6 +139,7 @@ as topics. The event call above can be performed in the same way as
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.10 <0.7.0;
|
pragma solidity >=0.4.10 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
|
@ -17,6 +17,7 @@ if they are marked ``virtual``. For details, please see
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract owned {
|
contract owned {
|
||||||
|
@ -23,6 +23,7 @@ unused parameters can be omitted.
|
|||||||
For example, if you want your contract to accept one kind of external call
|
For example, if you want your contract to accept one kind of external call
|
||||||
with two integers, you would use something like the following::
|
with two integers, you would use something like the following::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract Simple {
|
contract Simple {
|
||||||
@ -55,6 +56,7 @@ Function return variables are declared with the same syntax after the
|
|||||||
For example, suppose you want to return two results: the sum and the product of
|
For example, suppose you want to return two results: the sum and the product of
|
||||||
two integers passed as function parameters, then you use something like::
|
two integers passed as function parameters, then you use something like::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract Simple {
|
contract Simple {
|
||||||
@ -79,6 +81,7 @@ or you can provide return values
|
|||||||
(either a single or :ref:`multiple ones<multi-return>`) directly with the ``return``
|
(either a single or :ref:`multiple ones<multi-return>`) directly with the ``return``
|
||||||
statement::
|
statement::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract Simple {
|
contract Simple {
|
||||||
@ -142,6 +145,7 @@ The following statements are considered modifying the state:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -187,6 +191,7 @@ In addition to the list of state modifying statements explained above, the follo
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -280,6 +285,7 @@ Below you can see an example of a Sink contract that uses function ``receive``.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
// This contract keeps all Ether sent to it with no way
|
// This contract keeps all Ether sent to it with no way
|
||||||
@ -335,6 +341,7 @@ operations as long as there is enough gas passed on to it.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.2 <0.7.0;
|
pragma solidity >=0.6.2 <0.7.0;
|
||||||
|
|
||||||
contract Test {
|
contract Test {
|
||||||
@ -407,6 +414,7 @@ The following example shows overloading of the function
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
@ -425,6 +433,7 @@ externally visible functions differ by their Solidity types but not by their ext
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
// This will not compile
|
// This will not compile
|
||||||
@ -458,6 +467,7 @@ candidate, resolution fails.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
|
@ -38,6 +38,7 @@ Details are given in the following example.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
|
|
||||||
@ -125,6 +126,7 @@ Note that above, we call ``Destructible.destroy()`` to "forward" the
|
|||||||
destruction request. The way this is done is problematic, as
|
destruction request. The way this is done is problematic, as
|
||||||
seen in the following example::
|
seen in the following example::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
contract owned {
|
contract owned {
|
||||||
@ -154,6 +156,7 @@ A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it
|
|||||||
explicitly in the final override, but this function will bypass
|
explicitly in the final override, but this function will bypass
|
||||||
``Base1.destroy``. The way around this is to use ``super``::
|
``Base1.destroy``. The way around this is to use ``super``::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract owned {
|
contract owned {
|
||||||
@ -204,6 +207,7 @@ use the ``override`` keyword in the function header as shown in this example:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract Base
|
contract Base
|
||||||
@ -227,6 +231,7 @@ bases, it has to explicitly override it:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract Base1
|
contract Base1
|
||||||
@ -253,6 +258,7 @@ that already overrides all other functions.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract A { function f() public pure{} }
|
contract A { function f() public pure{} }
|
||||||
@ -293,6 +299,7 @@ of the variable:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract A
|
contract A
|
||||||
@ -324,6 +331,7 @@ and the ``override`` keyword must be used in the overriding modifier:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract Base
|
contract Base
|
||||||
@ -342,6 +350,7 @@ explicitly:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract Base1
|
contract Base1
|
||||||
@ -389,6 +398,7 @@ equivalent to ``constructor() public {}``. For example:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
@ -419,6 +429,7 @@ The constructors of all the base contracts will be called following the
|
|||||||
linearization rules explained below. If the base constructors have arguments,
|
linearization rules explained below. If the base constructors have arguments,
|
||||||
derived contracts need to specify all of them. This can be done in two ways::
|
derived contracts need to specify all of them. This can be done in two ways::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract Base {
|
contract Base {
|
||||||
@ -478,6 +489,7 @@ error "Linearization of inheritance graph impossible".
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract X {}
|
contract X {}
|
||||||
@ -498,6 +510,7 @@ One area where inheritance linearization is especially important and perhaps not
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract Base1 {
|
contract Base1 {
|
||||||
|
@ -22,6 +22,7 @@ Interfaces are denoted by their own keyword:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.2 <0.7.0;
|
pragma solidity >=0.6.2 <0.7.0;
|
||||||
|
|
||||||
interface Token {
|
interface Token {
|
||||||
@ -42,6 +43,7 @@ inheritance.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.2 <0.7.0;
|
pragma solidity >=0.6.2 <0.7.0;
|
||||||
|
|
||||||
interface ParentA {
|
interface ParentA {
|
||||||
|
@ -47,6 +47,7 @@ more advanced example to implement a set).
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -125,6 +126,7 @@ custom types without the overhead of external function calls:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
struct bigint {
|
struct bigint {
|
||||||
@ -239,6 +241,7 @@ Its value can be obtained from Solidity using the ``.selector`` member as follow
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.14 <0.7.0;
|
pragma solidity >=0.5.14 <0.7.0;
|
||||||
|
|
||||||
library L {
|
library L {
|
||||||
|
@ -29,6 +29,7 @@ may only be used inside a contract, not inside any of its functions.
|
|||||||
Let us rewrite the set example from the
|
Let us rewrite the set example from the
|
||||||
:ref:`libraries` in this way::
|
:ref:`libraries` in this way::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -81,6 +82,7 @@ Let us rewrite the set example from the
|
|||||||
|
|
||||||
It is also possible to extend elementary types in that way::
|
It is also possible to extend elementary types in that way::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
library Search {
|
library Search {
|
||||||
|
@ -54,6 +54,7 @@ return parameter list for functions.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -68,6 +69,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -112,6 +114,7 @@ when they are declared.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -132,6 +135,7 @@ it evaluates to a state variable. If it is accessed externally
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -151,6 +155,7 @@ to write a function, for example:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract arrayExample {
|
contract arrayExample {
|
||||||
@ -177,6 +182,7 @@ The next example is more complex:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract Complex {
|
contract Complex {
|
||||||
|
@ -41,6 +41,7 @@ Internal Function Calls
|
|||||||
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
|
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
|
||||||
this nonsensical example::
|
this nonsensical example::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -82,6 +83,7 @@ to the total balance of that contract:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.2 <0.7.0;
|
pragma solidity >=0.6.2 <0.7.0;
|
||||||
|
|
||||||
contract InfoFeed {
|
contract InfoFeed {
|
||||||
@ -137,6 +139,7 @@ parameters from the function declaration, but can be in arbitrary order.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -160,6 +163,7 @@ Those parameters will still be present on the stack, but they are inaccessible.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -183,6 +187,7 @@ is compiled so recursive creation-dependencies are not possible.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.2 <0.7.0;
|
pragma solidity >=0.6.2 <0.7.0;
|
||||||
|
|
||||||
contract D {
|
contract D {
|
||||||
@ -238,6 +243,7 @@ which only need to be created if there is a dispute.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.2 <0.7.0;
|
pragma solidity >=0.6.2 <0.7.0;
|
||||||
|
|
||||||
contract D {
|
contract D {
|
||||||
@ -307,6 +313,7 @@ groupings of expressions.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -352,6 +359,7 @@ because only a reference and not a copy is passed.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -410,6 +418,7 @@ the two variables have the same name but disjoint scopes.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
contract C {
|
contract C {
|
||||||
function minimalScoping() pure public {
|
function minimalScoping() pure public {
|
||||||
@ -431,6 +440,7 @@ In any case, you will get a warning about the outer variable being shadowed.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
// This will report a warning
|
// This will report a warning
|
||||||
contract C {
|
contract C {
|
||||||
@ -452,6 +462,7 @@ In any case, you will get a warning about the outer variable being shadowed.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
// This will not compile
|
// This will not compile
|
||||||
contract C {
|
contract C {
|
||||||
@ -540,6 +551,7 @@ and ``assert`` for internal error checking.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract Sharer {
|
contract Sharer {
|
||||||
@ -584,6 +596,7 @@ The following example shows how to use an error string together with ``revert``
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract VendingMachine {
|
contract VendingMachine {
|
||||||
@ -627,6 +640,7 @@ A failure in an external call can be caught using a try/catch statement, as foll
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
interface DataFeed { function getData(address token) external returns (uint value); }
|
interface DataFeed { function getData(address token) external returns (uint value); }
|
||||||
|
@ -24,6 +24,7 @@ to receive their money - contracts cannot activate themselves.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract SimpleAuction {
|
contract SimpleAuction {
|
||||||
@ -184,6 +185,7 @@ invalid bids.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract BlindAuction {
|
contract BlindAuction {
|
||||||
|
@ -142,6 +142,7 @@ The full contract
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.24 <0.7.0;
|
pragma solidity >=0.4.24 <0.7.0;
|
||||||
|
|
||||||
contract ReceiverPays {
|
contract ReceiverPays {
|
||||||
@ -338,6 +339,7 @@ The full contract
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract SimplePaymentChannel {
|
contract SimplePaymentChannel {
|
||||||
|
@ -19,6 +19,7 @@ and the sum of all balances is an invariant across the lifetime of the contract.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
library Balances {
|
library Balances {
|
||||||
|
@ -25,6 +25,7 @@ you can use state machine-like constructs inside a contract.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract Purchase {
|
contract Purchase {
|
||||||
|
@ -32,6 +32,7 @@ of votes.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
/// @title Voting with delegation.
|
/// @title Voting with delegation.
|
||||||
|
@ -71,6 +71,7 @@ So for the following contract snippet
|
|||||||
the position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``::
|
the position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``::
|
||||||
|
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -171,6 +172,7 @@ value and reference types, types that are encoded packed, and nested types.
|
|||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
contract A {
|
contract A {
|
||||||
struct S {
|
struct S {
|
||||||
|
@ -17,6 +17,7 @@ Storage Example
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract SimpleStorage {
|
contract SimpleStorage {
|
||||||
@ -31,8 +32,12 @@ Storage Example
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The first line tells you that the source code is written for
|
The first line tells you that the source code is licensed under the
|
||||||
Solidity version 0.4.0, or a newer version of the language up to, but not including version 0.7.0.
|
GPL version 3.0. Machine-readable license specifiers are important
|
||||||
|
in a setting where publishing the source code is the default.
|
||||||
|
|
||||||
|
The next line specifies that the source code is written for
|
||||||
|
Solidity version 0.4.16, or a newer version of the language up to, but not including version 0.7.0.
|
||||||
This is to ensure that the contract is not compilable with a new (breaking) compiler version, where it could behave differently.
|
This is to ensure that the contract is not compilable with a new (breaking) compiler version, where it could behave differently.
|
||||||
:ref:`Pragmas<pragma>` are common instructions for compilers about how to treat the
|
:ref:`Pragmas<pragma>` are common instructions for compilers about how to treat the
|
||||||
source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_).
|
source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_).
|
||||||
@ -77,6 +82,7 @@ registering with a username and password, all you need is an Ethereum keypair.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract Coin {
|
contract Coin {
|
||||||
|
@ -7,6 +7,38 @@ Source files can contain an arbitrary number of
|
|||||||
:ref:`pragma directives<pragma>` and
|
:ref:`pragma directives<pragma>` and
|
||||||
:ref:`struct<structs>` and :ref:`enum<enums>` definitions.
|
:ref:`struct<structs>` and :ref:`enum<enums>` definitions.
|
||||||
|
|
||||||
|
.. index:: ! license, spdx
|
||||||
|
|
||||||
|
SPDX License Identifier
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Trust in smart contract can be better established if their source code
|
||||||
|
is available. Since making source code available always touches on legal problems
|
||||||
|
with regards to copyright, the Solidity compiler encouranges the use
|
||||||
|
of machine-readable `SPDX license identifiers <https://spdx.org>`_.
|
||||||
|
Every source file should start with a comment indicating its license:
|
||||||
|
|
||||||
|
``// SPDX-License-Identifier: MIT``
|
||||||
|
|
||||||
|
The compiler does not validate that the license is part of the
|
||||||
|
`list allowed by SPDX <https://spdx.org/licenses/>`_, but
|
||||||
|
it does include the supplied string in the `bytecode metadata <metadata>`_.
|
||||||
|
|
||||||
|
If you do not want to specify a license or if the source code is
|
||||||
|
not open-source, please use the special value ``UNLICENSED``.
|
||||||
|
|
||||||
|
Supplying this comment of course does not free you from other
|
||||||
|
obligations related to licensing like having to mention
|
||||||
|
a specific license header in each source file or the
|
||||||
|
original copyright holder.
|
||||||
|
|
||||||
|
The comment is recognized by the compiler anywhere in the file at the
|
||||||
|
file level, but it is recommended to put it at the top of the file.
|
||||||
|
|
||||||
|
More information about how to use SPDX license identifiers
|
||||||
|
can be found at the `SPDX website <https://spdx.org/ids-how>`_.
|
||||||
|
|
||||||
|
|
||||||
.. index:: ! pragma
|
.. index:: ! pragma
|
||||||
|
|
||||||
.. _pragma:
|
.. _pragma:
|
||||||
@ -284,6 +316,7 @@ for the two function parameters and two return variables.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.21 <0.7.0;
|
pragma solidity >=0.4.21 <0.7.0;
|
||||||
|
|
||||||
/** @title Shape calculator. */
|
/** @title Shape calculator. */
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
.. _metadata:
|
||||||
|
|
||||||
#################
|
#################
|
||||||
Contract Metadata
|
Contract Metadata
|
||||||
#################
|
#################
|
||||||
@ -54,7 +56,9 @@ explanatory purposes.
|
|||||||
// Required (unless "content" is used, see below): Sorted URL(s)
|
// Required (unless "content" is used, see below): Sorted URL(s)
|
||||||
// to the source file, protocol is more or less arbitrary, but a
|
// to the source file, protocol is more or less arbitrary, but a
|
||||||
// Swarm URL is recommended
|
// Swarm URL is recommended
|
||||||
"urls": [ "bzzr://56ab..." ]
|
"urls": [ "bzzr://56ab..." ],
|
||||||
|
// Optional: SPDX license identifier as given in the source file
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"destructible": {
|
"destructible": {
|
||||||
// Required: keccak256 hash of the source file
|
// Required: keccak256 hash of the source file
|
||||||
@ -82,8 +86,12 @@ explanatory purposes.
|
|||||||
deduplicate: false,
|
deduplicate: false,
|
||||||
cse: false,
|
cse: false,
|
||||||
constantOptimizer: false,
|
constantOptimizer: false,
|
||||||
yul: false,
|
yul: true,
|
||||||
yulDetails: {}
|
// Optional: Only present if "yul" is "true"
|
||||||
|
yulDetails: {
|
||||||
|
stackAllocation: false,
|
||||||
|
optimizerSteps: "dhfoDgvulfnTUtnIf..."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
|
@ -49,6 +49,7 @@ The following example shows a contract and a function using all available tags.
|
|||||||
|
|
||||||
.. code:: solidity
|
.. code:: solidity
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
/// @title A simulator for trees
|
/// @title A simulator for trees
|
||||||
|
@ -58,6 +58,7 @@ complete contract):
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||||
@ -81,6 +82,7 @@ as it uses ``call`` which forwards all remaining gas by default:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.2 <0.7.0;
|
pragma solidity >=0.6.2 <0.7.0;
|
||||||
|
|
||||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||||
@ -100,6 +102,7 @@ outlined further below:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.11 <0.7.0;
|
pragma solidity >=0.4.11 <0.7.0;
|
||||||
|
|
||||||
contract Fund {
|
contract Fund {
|
||||||
@ -197,6 +200,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||||
@ -217,6 +221,7 @@ Now someone tricks you into sending Ether to the address of this attack wallet:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
interface TxUserWallet {
|
interface TxUserWallet {
|
||||||
@ -277,6 +282,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract Map {
|
contract Map {
|
||||||
@ -555,6 +561,7 @@ not mean loss of proving power.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0;
|
pragma solidity >=0.5.0;
|
||||||
pragma experimental SMTChecker;
|
pragma experimental SMTChecker;
|
||||||
// This may report a warning if no SMT solver available.
|
// This may report a warning if no SMT solver available.
|
||||||
@ -609,6 +616,7 @@ types.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0;
|
pragma solidity >=0.5.0;
|
||||||
pragma experimental SMTChecker;
|
pragma experimental SMTChecker;
|
||||||
// This will report a warning
|
// This will report a warning
|
||||||
|
@ -26,6 +26,7 @@ storage.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract SimpleStorage {
|
contract SimpleStorage {
|
||||||
@ -46,6 +47,7 @@ Functions are the executable units of code within a contract.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract SimpleAuction {
|
contract SimpleAuction {
|
||||||
@ -74,6 +76,7 @@ Like functions, modifiers can be :ref:`overridden <modifier-overriding>`.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract Purchase {
|
contract Purchase {
|
||||||
@ -101,6 +104,7 @@ Events are convenience interfaces with the EVM logging facilities.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.21 <0.7.0;
|
pragma solidity >=0.4.21 <0.7.0;
|
||||||
|
|
||||||
contract SimpleAuction {
|
contract SimpleAuction {
|
||||||
@ -125,6 +129,7 @@ Structs are custom defined types that can group several variables (see
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract Ballot {
|
contract Ballot {
|
||||||
@ -146,6 +151,7 @@ Enums can be used to create custom types with a finite set of 'constant values'
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract Purchase {
|
contract Purchase {
|
||||||
|
@ -55,6 +55,7 @@ Surround top level declarations in solidity source with two blank lines.
|
|||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
@ -73,6 +74,7 @@ Yes::
|
|||||||
|
|
||||||
No::
|
No::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
@ -92,6 +94,7 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu
|
|||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
abstract contract A {
|
abstract contract A {
|
||||||
@ -112,6 +115,7 @@ Yes::
|
|||||||
|
|
||||||
No::
|
No::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
abstract contract A {
|
abstract contract A {
|
||||||
@ -246,6 +250,7 @@ Import statements should always be placed at the top of the file.
|
|||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
import "./Owned.sol";
|
import "./Owned.sol";
|
||||||
@ -260,6 +265,7 @@ Yes::
|
|||||||
|
|
||||||
No::
|
No::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
@ -293,6 +299,7 @@ Within a grouping, place the ``view`` and ``pure`` functions last.
|
|||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
@ -329,6 +336,7 @@ Yes::
|
|||||||
|
|
||||||
No::
|
No::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity ^0.6.0;
|
pragma solidity ^0.6.0;
|
||||||
|
|
||||||
contract A {
|
contract A {
|
||||||
@ -436,6 +444,7 @@ should:
|
|||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract Coin {
|
contract Coin {
|
||||||
@ -447,6 +456,7 @@ Yes::
|
|||||||
|
|
||||||
No::
|
No::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract Coin
|
contract Coin
|
||||||
@ -747,6 +757,7 @@ manner as modifiers if the function declaration is long or hard to read.
|
|||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
// Base contracts just to make this compile
|
// Base contracts just to make this compile
|
||||||
@ -779,6 +790,7 @@ Yes::
|
|||||||
|
|
||||||
No::
|
No::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -1002,6 +1014,7 @@ As shown in the example below, if the contract name is ``Congress`` and the libr
|
|||||||
|
|
||||||
Yes::
|
Yes::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -1025,6 +1038,7 @@ Yes::
|
|||||||
|
|
||||||
and in ``Congress.sol``::
|
and in ``Congress.sol``::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
import "./Owned.sol";
|
import "./Owned.sol";
|
||||||
@ -1036,6 +1050,7 @@ and in ``Congress.sol``::
|
|||||||
|
|
||||||
No::
|
No::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
@ -1140,6 +1155,7 @@ multiline comment starting with ``/**`` and ending with ``*/``.
|
|||||||
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
|
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
|
||||||
added looks like the one below::
|
added looks like the one below::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ contract that returns the value at the specified address.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract MappingExample {
|
contract MappingExample {
|
||||||
@ -66,6 +67,7 @@ The example below uses ``_allowances`` to record the amount someone else is allo
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
contract MappingExample {
|
contract MappingExample {
|
||||||
@ -120,6 +122,7 @@ the ``sum`` function iterates over to sum all the values.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
struct IndexValue { uint keyIndex; uint value; }
|
struct IndexValue { uint keyIndex; uint value; }
|
||||||
|
@ -42,6 +42,7 @@ value it referred to previously.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
contract DeleteExample {
|
contract DeleteExample {
|
||||||
|
@ -57,6 +57,7 @@ Data locations are not only relevant for persistency of data, but also for the s
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.5.0 <0.7.0;
|
pragma solidity >=0.5.0 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -167,6 +168,7 @@ or create a new memory array and copy every element.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -198,6 +200,7 @@ the first element to ``uint``.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -214,6 +217,7 @@ memory arrays, i.e. the following is not possible:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.0 <0.7.0;
|
pragma solidity >=0.4.0 <0.7.0;
|
||||||
|
|
||||||
// This will not compile.
|
// This will not compile.
|
||||||
@ -274,6 +278,7 @@ Array Members
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract ArrayContract {
|
contract ArrayContract {
|
||||||
@ -406,6 +411,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
contract Proxy {
|
contract Proxy {
|
||||||
@ -443,6 +449,7 @@ shown in the following example:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
// Defines a new type with two fields.
|
// Defines a new type with two fields.
|
||||||
|
@ -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``.
|
||||||
@ -540,6 +543,7 @@ subsequent unsigned integer values starting from ``0``.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
contract test {
|
contract test {
|
||||||
@ -650,6 +654,7 @@ External (or public) functions have the following members:
|
|||||||
|
|
||||||
Example that shows how to use the members::
|
Example that shows how to use the members::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
// This will report a warning
|
// This will report a warning
|
||||||
|
|
||||||
@ -668,6 +673,7 @@ Example that shows how to use the members::
|
|||||||
|
|
||||||
Example that shows how to use internal function types::
|
Example that shows how to use internal function types::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.16 <0.7.0;
|
pragma solidity >=0.4.16 <0.7.0;
|
||||||
|
|
||||||
library ArrayUtils {
|
library ArrayUtils {
|
||||||
@ -725,6 +731,7 @@ Example that shows how to use internal function types::
|
|||||||
|
|
||||||
Another example that uses external function types::
|
Another example that uses external function types::
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.4.22 <0.7.0;
|
pragma solidity >=0.4.22 <0.7.0;
|
||||||
|
|
||||||
|
|
||||||
|
@ -294,10 +294,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.
|
||||||
@ -328,3 +329,10 @@ for an interface type ``I``:
|
|||||||
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``.
|
||||||
|
@ -231,7 +231,10 @@ Input Description
|
|||||||
"yulDetails": {
|
"yulDetails": {
|
||||||
// Improve allocation of stack slots for variables, can free up stack slots early.
|
// Improve allocation of stack slots for variables, can free up stack slots early.
|
||||||
// Activated by default if the Yul optimizer is activated.
|
// Activated by default if the Yul optimizer is activated.
|
||||||
"stackAllocation": true
|
"stackAllocation": true,
|
||||||
|
// Select optimization steps to be applied.
|
||||||
|
// Optional, the optimizer will use the default sequence if omitted.
|
||||||
|
"optimizerSteps": "dhfoDgvulfnTUtnIf..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -614,6 +617,7 @@ Assume you have the following contracts you want to update declared in ``Source.
|
|||||||
.. code-block:: none
|
.. code-block:: none
|
||||||
|
|
||||||
// This will not compile after 0.5.0
|
// This will not compile after 0.5.0
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >0.4.23 <0.5.0;
|
pragma solidity >0.4.23 <0.5.0;
|
||||||
|
|
||||||
contract Updateable {
|
contract Updateable {
|
||||||
@ -695,6 +699,7 @@ The command above applies all changes as shown below. Please review them careful
|
|||||||
|
|
||||||
.. code-block:: solidity
|
.. code-block:: solidity
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.6.0 <0.7.0;
|
pragma solidity >=0.6.0 <0.7.0;
|
||||||
|
|
||||||
abstract contract Updateable {
|
abstract contract Updateable {
|
||||||
|
285
docs/yul.rst
285
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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -885,13 +887,6 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a
|
|||||||
| gaslimit() | | F | block gas limit of the current block |
|
| gaslimit() | | F | block gas limit of the current block |
|
||||||
+-------------------------+-----+---+-----------------------------------------------------------------+
|
+-------------------------+-----+---+-----------------------------------------------------------------+
|
||||||
|
|
||||||
There are three additional functions, ``datasize(x)``, ``dataoffset(x)`` and ``datacopy(t, f, l)``,
|
|
||||||
which are used to access other parts of a Yul object.
|
|
||||||
|
|
||||||
``datasize`` and ``dataoffset`` can only take string literals (the names of other objects)
|
|
||||||
as arguments and return the size and offset in the data area, respectively.
|
|
||||||
For the EVM, the ``datacopy`` function is equivalent to ``codecopy``.
|
|
||||||
|
|
||||||
.. _yul-call-return-area:
|
.. _yul-call-return-area:
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
@ -903,6 +898,32 @@ For the EVM, the ``datacopy`` function is equivalent to ``codecopy``.
|
|||||||
The remaining bytes will retain their values as of before the call. If the call fails (it returns ``0``),
|
The remaining bytes will retain their values as of before the call. If the call fails (it returns ``0``),
|
||||||
nothing is written to that area, but you can still retrieve the failure data using ``returndatacopy``.
|
nothing is written to that area, but you can still retrieve the failure data using ``returndatacopy``.
|
||||||
|
|
||||||
|
|
||||||
|
In some internal dialects, there are additional functions:
|
||||||
|
|
||||||
|
datasize, dataoffset, datacopy
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The functions ``datasize(x)``, ``dataoffset(x)`` and ``datacopy(t, f, l)``,
|
||||||
|
are used to access other parts of a Yul object.
|
||||||
|
|
||||||
|
``datasize`` and ``dataoffset`` can only take string literals (the names of other objects)
|
||||||
|
as arguments and return the size and offset in the data area, respectively.
|
||||||
|
For the EVM, the ``datacopy`` function is equivalent to ``codecopy``.
|
||||||
|
|
||||||
|
|
||||||
|
setimmutable, loadimmutable
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The functions ``setimmutable("name", value)`` and ``loadimmutable("name")`` are
|
||||||
|
used for the immutable mechanism in Solidity and do not nicely map to pur Yul.
|
||||||
|
The function ``setimmutable`` assumes that the runtime code of a contract
|
||||||
|
is currently copied to memory at offsot zero. The call to ``setimmutable("name", value)``
|
||||||
|
will store ``value`` at all points in memory that contain a call to
|
||||||
|
``loadimmutable("name")``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.. _yul-object:
|
.. _yul-object:
|
||||||
|
|
||||||
Specification of Yul Object
|
Specification of Yul Object
|
||||||
@ -1015,3 +1036,253 @@ If you want to use Solidity in stand-alone Yul mode, you activate the optimizer
|
|||||||
solc --strict-assembly --optimize
|
solc --strict-assembly --optimize
|
||||||
|
|
||||||
In Solidity mode, the Yul optimizer is activated together with the regular optimizer.
|
In Solidity mode, the Yul optimizer is activated together with the regular optimizer.
|
||||||
|
|
||||||
|
Optimization step sequence
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly.
|
||||||
|
You can override this sequence and supply your own using the ``--yul-optimizations`` option:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
|
||||||
|
|
||||||
|
The order of steps is significant and affects the quality of the output.
|
||||||
|
Moreover, applying a step may uncover new optimization opportunities for others that were already
|
||||||
|
applied so repeating steps is often beneficial.
|
||||||
|
By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly
|
||||||
|
apply that part until it no longer improves the size of the resulting assembly.
|
||||||
|
You can use brackets multiple times in a single sequence but they cannot be nested.
|
||||||
|
|
||||||
|
The following optimization steps are available:
|
||||||
|
|
||||||
|
============ ===============================
|
||||||
|
Abbreviation Full name
|
||||||
|
============ ===============================
|
||||||
|
``f`` ``BlockFlattener``
|
||||||
|
``l`` ``CircularReferencesPruner``
|
||||||
|
``c`` ``CommonSubexpressionEliminator``
|
||||||
|
``C`` ``ConditionalSimplifier``
|
||||||
|
``U`` ``ConditionalUnsimplifier``
|
||||||
|
``n`` ``ControlFlowSimplifier``
|
||||||
|
``D`` ``DeadCodeEliminator``
|
||||||
|
``v`` ``EquivalentFunctionCombiner``
|
||||||
|
``e`` ``ExpressionInliner``
|
||||||
|
``j`` ``ExpressionJoiner``
|
||||||
|
``s`` ``ExpressionSimplifier``
|
||||||
|
``x`` ``ExpressionSplitter``
|
||||||
|
``I`` ``ForLoopConditionIntoBody``
|
||||||
|
``O`` ``ForLoopConditionOutOfBody``
|
||||||
|
``o`` ``ForLoopInitRewriter``
|
||||||
|
``i`` ``FullInliner``
|
||||||
|
``g`` ``FunctionGrouper``
|
||||||
|
``h`` ``FunctionHoister``
|
||||||
|
``T`` ``LiteralRematerialiser``
|
||||||
|
``L`` ``LoadResolver``
|
||||||
|
``M`` ``LoopInvariantCodeMotion``
|
||||||
|
``r`` ``RedundantAssignEliminator``
|
||||||
|
``m`` ``Rematerialiser``
|
||||||
|
``V`` ``SSAReverser``
|
||||||
|
``a`` ``SSATransform``
|
||||||
|
``t`` ``StructuralSimplifier``
|
||||||
|
``u`` ``UnusedPruner``
|
||||||
|
``d`` ``VarDeclInitializer``
|
||||||
|
============ ===============================
|
||||||
|
|
||||||
|
Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``.
|
||||||
|
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
|
||||||
|
|
||||||
|
|
||||||
|
.. _erc20yul:
|
||||||
|
|
||||||
|
Complete ERC20 Example
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. code-block:: yul
|
||||||
|
|
||||||
|
object "Token" {
|
||||||
|
code {
|
||||||
|
// Store the creator in slot zero.
|
||||||
|
sstore(0, caller())
|
||||||
|
|
||||||
|
// Deploy the contract
|
||||||
|
datacopy(0, dataoffset("runtime"), datasize("runtime"))
|
||||||
|
return(0, datasize("runtime"))
|
||||||
|
}
|
||||||
|
object "runtime" {
|
||||||
|
code {
|
||||||
|
// Protection against sending Ether
|
||||||
|
require(iszero(callvalue()))
|
||||||
|
|
||||||
|
// Dispatcher
|
||||||
|
switch selector()
|
||||||
|
case 0x70a08231 /* "balanceOf(address)" */ {
|
||||||
|
returnUint(balanceOf(decodeAsAddress(0)))
|
||||||
|
}
|
||||||
|
case 0x18160ddd /* "totalSupply()" */ {
|
||||||
|
returnUint(totalSupply())
|
||||||
|
}
|
||||||
|
case 0xa9059cbb /* "transfer(address,uint256)" */ {
|
||||||
|
transfer(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0x23b872dd /* "transferFrom(address,address,uint256)" */ {
|
||||||
|
transferFrom(decodeAsAddress(0), decodeAsAddress(1), decodeAsUint(2))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0x095ea7b3 /* "approve(address,uint256)" */ {
|
||||||
|
approve(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
case 0xdd62ed3e /* "allowance(address,address)" */ {
|
||||||
|
returnUint(allowance(decodeAsAddress(0), decodeAsAddress(1)))
|
||||||
|
}
|
||||||
|
case 0x40c10f19 /* "mint(address,uint256)" */ {
|
||||||
|
mint(decodeAsAddress(0), decodeAsUint(1))
|
||||||
|
returnTrue()
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mint(account, amount) {
|
||||||
|
require(calledByOwner())
|
||||||
|
|
||||||
|
mintTokens(amount)
|
||||||
|
addToBalance(account, amount)
|
||||||
|
emitTransfer(0, account, amount)
|
||||||
|
}
|
||||||
|
function transfer(to, amount) {
|
||||||
|
executeTransfer(caller(), to, amount)
|
||||||
|
}
|
||||||
|
function approve(spender, amount) {
|
||||||
|
revertIfZeroAddress(spender)
|
||||||
|
setAllowance(caller(), spender, amount)
|
||||||
|
emitApproval(caller(), spender, amount)
|
||||||
|
}
|
||||||
|
function transferFrom(from, to, amount) {
|
||||||
|
decreaseAllowanceBy(from, caller(), amount)
|
||||||
|
executeTransfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeTransfer(from, to, amount) {
|
||||||
|
revertIfZeroAddress(to)
|
||||||
|
deductFromBalance(from, amount)
|
||||||
|
addToBalance(to, amount)
|
||||||
|
emitTransfer(from, to, amount)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- calldata decoding functions ----------- */
|
||||||
|
function selector() -> s {
|
||||||
|
s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeAsAddress(offset) -> v {
|
||||||
|
v := decodeAsUint(offset)
|
||||||
|
if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function decodeAsUint(offset) -> v {
|
||||||
|
let pos := add(4, mul(offset, 0x20))
|
||||||
|
if lt(calldatasize(), add(pos, 0x20)) {
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
v := calldataload(pos)
|
||||||
|
}
|
||||||
|
/* ---------- calldata encoding functions ---------- */
|
||||||
|
function returnUint(v) {
|
||||||
|
mstore(0, v)
|
||||||
|
return(0, 0x20)
|
||||||
|
}
|
||||||
|
function returnTrue() {
|
||||||
|
returnUint(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- events ---------- */
|
||||||
|
function emitTransfer(from, to, amount) {
|
||||||
|
let signatureHash := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
|
||||||
|
emitEvent(signatureHash, from, to, amount)
|
||||||
|
}
|
||||||
|
function emitApproval(from, spender, amount) {
|
||||||
|
let signatureHash := 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
|
||||||
|
emitEvent(signatureHash, from, spender, amount)
|
||||||
|
}
|
||||||
|
function emitEvent(signatureHash, indexed1, indexed2, nonIndexed) {
|
||||||
|
mstore(0, nonIndexed)
|
||||||
|
log3(0, 0x20, signatureHash, indexed1, indexed2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- storage layout ---------- */
|
||||||
|
function ownerPos() -> p { p := 0 }
|
||||||
|
function totalSupplyPos() -> p { p := 1 }
|
||||||
|
function accountToStorageOffset(account) -> offset {
|
||||||
|
offset := add(0x1000, account)
|
||||||
|
}
|
||||||
|
function allowanceStorageOffset(account, spender) -> offset {
|
||||||
|
offset := accountToStorageOffset(account)
|
||||||
|
mstore(0, offset)
|
||||||
|
mstore(0x20, spender)
|
||||||
|
offset := keccak256(0, 0x40)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------- storage access ---------- */
|
||||||
|
function owner() -> o {
|
||||||
|
o := sload(ownerPos())
|
||||||
|
}
|
||||||
|
function totalSupply() -> supply {
|
||||||
|
supply := sload(totalSupplyPos())
|
||||||
|
}
|
||||||
|
function mintTokens(amount) {
|
||||||
|
sstore(totalSupplyPos(), safeAdd(totalSupply(), amount))
|
||||||
|
}
|
||||||
|
function balanceOf(account) -> bal {
|
||||||
|
bal := sload(accountToStorageOffset(account))
|
||||||
|
}
|
||||||
|
function addToBalance(account, amount) {
|
||||||
|
let offset := accountToStorageOffset(account)
|
||||||
|
sstore(offset, safeAdd(sload(offset), amount))
|
||||||
|
}
|
||||||
|
function deductFromBalance(account, amount) {
|
||||||
|
let offset := accountToStorageOffset(account)
|
||||||
|
let bal := sload(offset)
|
||||||
|
require(lte(amount, bal))
|
||||||
|
sstore(offset, sub(bal, amount))
|
||||||
|
}
|
||||||
|
function allowance(account, spender) -> amount {
|
||||||
|
amount := sload(allowanceStorageOffset(account, spender))
|
||||||
|
}
|
||||||
|
function setAllowance(account, spender, amount) {
|
||||||
|
sstore(allowanceStorageOffset(account, spender), amount)
|
||||||
|
}
|
||||||
|
function decreaseAllowanceBy(account, spender, amount) {
|
||||||
|
let offset := allowanceStorageOffset(account, spender)
|
||||||
|
let currentAllowance := sload(offset)
|
||||||
|
require(lte(amount, currentAllowance))
|
||||||
|
sstore(offset, sub(currentAllowance, amount))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- utility functions ---------- */
|
||||||
|
function lte(a, b) -> r {
|
||||||
|
r := iszero(gt(a, b))
|
||||||
|
}
|
||||||
|
function gte(a, b) -> r {
|
||||||
|
r := iszero(lt(a, b))
|
||||||
|
}
|
||||||
|
function safeAdd(a, b) -> r {
|
||||||
|
r := add(a, b)
|
||||||
|
if or(lt(r, a), lt(r, b)) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
function calledByOwner() -> cbo {
|
||||||
|
cbo := eq(owner(), caller())
|
||||||
|
}
|
||||||
|
function revertIfZeroAddress(addr) {
|
||||||
|
require(addr)
|
||||||
|
}
|
||||||
|
function require(condition) {
|
||||||
|
if iszero(condition) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,9 +26,6 @@
|
|||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/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,48 +61,52 @@ 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!");
|
||||||
@ -100,14 +115,14 @@ public:
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
@ -119,23 +134,32 @@ public:
|
|||||||
return m_errorCount > 0;
|
return m_errorCount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @returns the number of errors (ignores warnings).
|
||||||
|
unsigned errorCount() const
|
||||||
|
{
|
||||||
|
return m_errorCount;
|
||||||
|
}
|
||||||
|
|
||||||
// @returns true if the maximum error count has been reached.
|
// @returns true if the maximum error count has been reached.
|
||||||
bool hasExcessiveErrors() const;
|
bool hasExcessiveErrors() const;
|
||||||
|
|
||||||
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,32 @@ 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::parserWarning(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.parserError(_location, _description);
|
m_errorReporter.warning(_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::parserError(string const& _description)
|
void ParserBase::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
parserError(currentLocation(), _description);
|
m_errorReporter.parserError(_error, _location, _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::fatalParserError(string const& _description)
|
void ParserBase::parserError(ErrorId _error, string const& _description)
|
||||||
{
|
{
|
||||||
fatalParserError(currentLocation(), _description);
|
parserError(_error, currentLocation(), _description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description)
|
void ParserBase::fatalParserError(ErrorId _error, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorReporter.fatalParserError(_location, _description);
|
fatalParserError(_error, currentLocation(), _description);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParserBase::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _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,18 @@ 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);
|
||||||
|
void parserWarning(ErrorId _error, SourceLocation const& _location, 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())
|
||||||
{
|
{
|
||||||
@ -359,24 +361,22 @@ 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_errorReporter.typeError(2311_error, _location, _description);
|
||||||
m_errorReporter.typeError(_location, _description);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorReporter.fatalTypeError(5651_error, _location, _description);
|
||||||
m_errorReporter.fatalTypeError(_location, _description);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
|
||||||
{
|
{
|
||||||
m_errorOccurred = true;
|
m_errorReporter.fatalDeclarationError(2046_error, _location, _description);
|
||||||
m_errorReporter.fatalDeclarationError(_location, _description);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
bool DeclarationTypeChecker::check(ASTNode const& _node)
|
||||||
{
|
{
|
||||||
|
unsigned errorCount = m_errorReporter.errorCount();
|
||||||
_node.accept(*this);
|
_node.accept(*this);
|
||||||
return !m_errorOccurred;
|
return m_errorReporter.errorCount() == errorCount;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,6 @@ private:
|
|||||||
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);
|
||||||
|
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
bool m_errorOccurred = false;
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
bool m_insideFunctionType = false;
|
bool m_insideFunctionType = false;
|
||||||
bool m_recursiveStructSeen = false;
|
bool m_recursiveStructSeen = false;
|
||||||
|
@ -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,6 +775,7 @@ 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 \"" +
|
||||||
@ -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"}
|
||||||
|
@ -156,19 +156,26 @@ std::vector<T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> co
|
|||||||
class SourceUnit: public ASTNode
|
class SourceUnit: public ASTNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SourceUnit(int64_t _id, SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> _nodes):
|
SourceUnit(
|
||||||
ASTNode(_id, _location), m_nodes(std::move(_nodes)) {}
|
int64_t _id,
|
||||||
|
SourceLocation const& _location,
|
||||||
|
std::optional<std::string> _licenseString,
|
||||||
|
std::vector<ASTPointer<ASTNode>> _nodes
|
||||||
|
):
|
||||||
|
ASTNode(_id, _location), m_licenseString(std::move(_licenseString)), m_nodes(std::move(_nodes)) {}
|
||||||
|
|
||||||
void accept(ASTVisitor& _visitor) override;
|
void accept(ASTVisitor& _visitor) override;
|
||||||
void accept(ASTConstVisitor& _visitor) const override;
|
void accept(ASTConstVisitor& _visitor) const override;
|
||||||
SourceUnitAnnotation& annotation() const override;
|
SourceUnitAnnotation& annotation() const override;
|
||||||
|
|
||||||
|
std::optional<std::string> const& licenseString() const { return m_licenseString; }
|
||||||
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
|
std::vector<ASTPointer<ASTNode>> nodes() const { return m_nodes; }
|
||||||
|
|
||||||
/// @returns a set of referenced SourceUnits. Recursively if @a _recurse is true.
|
/// @returns a set of referenced SourceUnits. Recursively if @a _recurse is true.
|
||||||
std::set<SourceUnit const*> referencedSourceUnits(bool _recurse = false, std::set<SourceUnit const*> _skipList = std::set<SourceUnit const*>()) const;
|
std::set<SourceUnit const*> referencedSourceUnits(bool _recurse = false, std::set<SourceUnit const*> _skipList = std::set<SourceUnit const*>()) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::optional<std::string> m_licenseString;
|
||||||
std::vector<ASTPointer<ASTNode>> m_nodes;
|
std::vector<ASTPointer<ASTNode>> m_nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,6 +225,7 @@ bool ASTJsonConverter::visit(SourceUnit const& _node)
|
|||||||
{
|
{
|
||||||
make_pair("absolutePath", _node.annotation().path),
|
make_pair("absolutePath", _node.annotation().path),
|
||||||
make_pair("exportedSymbols", move(exportedSymbols)),
|
make_pair("exportedSymbols", move(exportedSymbols)),
|
||||||
|
make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue),
|
||||||
make_pair("nodes", toJson(_node.nodes()))
|
make_pair("nodes", toJson(_node.nodes()))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -218,10 +218,15 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js
|
|||||||
|
|
||||||
ASTPointer<SourceUnit> ASTJsonImporter::createSourceUnit(Json::Value const& _node, string const& _srcName)
|
ASTPointer<SourceUnit> ASTJsonImporter::createSourceUnit(Json::Value const& _node, string const& _srcName)
|
||||||
{
|
{
|
||||||
|
optional<string> license;
|
||||||
|
if (_node.isMember("license") && !_node["license"].isNull())
|
||||||
|
license = _node["license"].asString();
|
||||||
|
|
||||||
vector<ASTPointer<ASTNode>> nodes;
|
vector<ASTPointer<ASTNode>> nodes;
|
||||||
for (auto& child: member(_node, "nodes"))
|
for (auto& child: member(_node, "nodes"))
|
||||||
nodes.emplace_back(convertJsonToASTNode(child));
|
nodes.emplace_back(convertJsonToASTNode(child));
|
||||||
ASTPointer<SourceUnit> tmp = createASTNode<SourceUnit>(_node, nodes);
|
|
||||||
|
ASTPointer<SourceUnit> tmp = createASTNode<SourceUnit>(_node, license, nodes);
|
||||||
tmp->annotation().path = _srcName;
|
tmp->annotation().path = _srcName;
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
|
|||||||
// Structs are fine in the following circumstances:
|
// Structs are fine in the following circumstances:
|
||||||
// - ABIv2 or,
|
// - ABIv2 or,
|
||||||
// - storage struct for a library
|
// - storage struct for a library
|
||||||
if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage))
|
if (_inLibraryCall && encodingType && encodingType->dataStoredIn(DataLocation::Storage))
|
||||||
return encodingType;
|
return encodingType;
|
||||||
TypePointer baseType = encodingType;
|
TypePointer baseType = encodingType;
|
||||||
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType))
|
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType))
|
||||||
@ -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())
|
||||||
@ -597,6 +613,13 @@ TypeResult IntegerType::binaryOperatorResult(Token _operator, Type const* _other
|
|||||||
}
|
}
|
||||||
else if (dynamic_cast<FixedPointType const*>(_other))
|
else if (dynamic_cast<FixedPointType const*>(_other))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
else if (auto rationalNumberType = dynamic_cast<RationalNumberType const*>(_other))
|
||||||
|
{
|
||||||
|
if (rationalNumberType->isFractional())
|
||||||
|
return TypeResult::err("Exponent is fractional.");
|
||||||
|
if (!rationalNumberType->integerType())
|
||||||
|
return TypeResult::err("Exponent too large.");
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1948,6 +1971,19 @@ string ArraySliceType::toString(bool _short) const
|
|||||||
return m_arrayType.toString(_short) + " slice";
|
return m_arrayType.toString(_short) + " slice";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer ArraySliceType::mobileType() const
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
m_arrayType.dataStoredIn(DataLocation::CallData) &&
|
||||||
|
m_arrayType.isDynamicallySized() &&
|
||||||
|
!m_arrayType.baseType()->isDynamicallyEncoded()
|
||||||
|
)
|
||||||
|
return &m_arrayType;
|
||||||
|
else
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::tuple<std::string, TypePointer>> ArraySliceType::makeStackItems() const
|
std::vector<std::tuple<std::string, TypePointer>> ArraySliceType::makeStackItems() const
|
||||||
{
|
{
|
||||||
return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}};
|
return {{"offset", TypeProvider::uint256()}, {"length", TypeProvider::uint256()}};
|
||||||
@ -3763,9 +3799,15 @@ 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"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (m_typeArgument->category() == Type::Category::Contract)
|
||||||
|
{
|
||||||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
|
||||||
if (contract.canBeDeployed())
|
if (contract.canBeDeployed())
|
||||||
return MemberList::MemberMap({
|
return MemberList::MemberMap({
|
||||||
@ -3778,6 +3820,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
|
|||||||
{"interfaceId", TypeProvider::fixedBytes(4)},
|
{"interfaceId", TypeProvider::fixedBytes(4)},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if (m_typeArgument->category() == Type::Category::Integer)
|
||||||
|
{
|
||||||
|
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
|
||||||
|
return MemberList::MemberMap({
|
||||||
|
{"min", integerTypePointer},
|
||||||
|
{"max", integerTypePointer},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
solAssert(false, "Unknown kind of magic.");
|
solAssert(false, "Unknown kind of magic.");
|
||||||
return {};
|
return {};
|
||||||
|
@ -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;
|
||||||
|
|
||||||
@ -828,6 +831,7 @@ public:
|
|||||||
bool isDynamicallyEncoded() const override { return true; }
|
bool isDynamicallyEncoded() const override { return true; }
|
||||||
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
|
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
|
TypePointer mobileType() const override;
|
||||||
|
|
||||||
BoolResult validForLocation(DataLocation _loc) const override { return m_arrayType.validForLocation(_loc); }
|
BoolResult validForLocation(DataLocation _loc) const override { return m_arrayType.validForLocation(_loc); }
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
@ -268,31 +279,47 @@ string ABIFunctions::abiEncodingFunction(
|
|||||||
return abiEncodingFunctionStringLiteral(_from, to, _options);
|
return abiEncodingFunctionStringLiteral(_from, to, _options);
|
||||||
else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
|
else if (auto toArray = dynamic_cast<ArrayType const*>(&to))
|
||||||
{
|
{
|
||||||
solAssert(_from.category() == Type::Category::Array, "");
|
ArrayType const* fromArray = nullptr;
|
||||||
solAssert(to.dataStoredIn(DataLocation::Memory), "");
|
switch (_from.category())
|
||||||
ArrayType const& fromArray = dynamic_cast<ArrayType const&>(_from);
|
{
|
||||||
|
case Type::Category::Array:
|
||||||
|
fromArray = dynamic_cast<ArrayType const*>(&_from);
|
||||||
|
break;
|
||||||
|
case Type::Category::ArraySlice:
|
||||||
|
fromArray = &dynamic_cast<ArraySliceType const*>(&_from)->arrayType();
|
||||||
|
solAssert(
|
||||||
|
fromArray->dataStoredIn(DataLocation::CallData) &&
|
||||||
|
fromArray->isDynamicallySized() &&
|
||||||
|
!fromArray->baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (fromArray.location())
|
switch (fromArray->location())
|
||||||
{
|
{
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
if (
|
if (
|
||||||
fromArray.isByteArray() ||
|
fromArray->isByteArray() ||
|
||||||
*fromArray.baseType() == *TypeProvider::uint256() ||
|
*fromArray->baseType() == *TypeProvider::uint256() ||
|
||||||
*fromArray.baseType() == FixedBytesType(32)
|
*fromArray->baseType() == FixedBytesType(32)
|
||||||
)
|
)
|
||||||
return abiEncodingFunctionCalldataArrayWithoutCleanup(fromArray, *toArray, _options);
|
return abiEncodingFunctionCalldataArrayWithoutCleanup(*fromArray, *toArray, _options);
|
||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
if (fromArray.isByteArray())
|
if (fromArray->isByteArray())
|
||||||
return abiEncodingFunctionMemoryByteArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionMemoryByteArray(*fromArray, *toArray, _options);
|
||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
case DataLocation::Storage:
|
case DataLocation::Storage:
|
||||||
if (fromArray.baseType()->storageBytes() <= 16)
|
if (fromArray->baseType()->storageBytes() <= 16)
|
||||||
return abiEncodingFunctionCompactStorageArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionCompactStorageArray(*fromArray, *toArray, _options);
|
||||||
else
|
else
|
||||||
return abiEncodingFunctionSimpleArray(fromArray, *toArray, _options);
|
return abiEncodingFunctionSimpleArray(*fromArray, *toArray, _options);
|
||||||
default:
|
default:
|
||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -480,6 +480,16 @@ void CompilerUtils::encodeToMemory(
|
|||||||
convertType(*_givenTypes[i], *targetType, true);
|
convertType(*_givenTypes[i], *targetType, true);
|
||||||
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
if (auto arrayType = dynamic_cast<ArrayType const*>(type))
|
||||||
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
|
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
|
||||||
|
else if (auto arraySliceType = dynamic_cast<ArraySliceType const*>(type))
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
arraySliceType->dataStoredIn(DataLocation::CallData) &&
|
||||||
|
arraySliceType->isDynamicallySized() &&
|
||||||
|
!arraySliceType->arrayType().baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
ArrayUtils(m_context).copyArrayToMemory(arraySliceType->arrayType(), _padToWordBoundaries);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
storeInMemoryDynamic(*type, _padToWordBoundaries);
|
storeInMemoryDynamic(*type, _padToWordBoundaries);
|
||||||
}
|
}
|
||||||
@ -516,22 +526,39 @@ void CompilerUtils::encodeToMemory(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(_givenTypes[i]->category() == Type::Category::Array, "Unknown dynamic type.");
|
ArrayType const* arrayType = nullptr;
|
||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(*_givenTypes[i]);
|
switch (_givenTypes[i]->category())
|
||||||
|
{
|
||||||
|
case Type::Category::Array:
|
||||||
|
arrayType = dynamic_cast<ArrayType const*>(_givenTypes[i]);
|
||||||
|
break;
|
||||||
|
case Type::Category::ArraySlice:
|
||||||
|
arrayType = &dynamic_cast<ArraySliceType const*>(_givenTypes[i])->arrayType();
|
||||||
|
solAssert(
|
||||||
|
arrayType->isDynamicallySized() &&
|
||||||
|
arrayType->dataStoredIn(DataLocation::CallData) &&
|
||||||
|
!arrayType->baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "Unknown dynamic type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
// now copy the array
|
// now copy the array
|
||||||
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType.sizeOnStack());
|
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType->sizeOnStack());
|
||||||
// stack: ... <end_of_mem> <value...>
|
// stack: ... <end_of_mem> <value...>
|
||||||
// copy length to memory
|
// copy length to memory
|
||||||
m_context << dupInstruction(1 + arrayType.sizeOnStack());
|
m_context << dupInstruction(1 + arrayType->sizeOnStack());
|
||||||
ArrayUtils(m_context).retrieveLength(arrayType, 1);
|
ArrayUtils(m_context).retrieveLength(*arrayType, 1);
|
||||||
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
|
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
|
||||||
storeInMemoryDynamic(*TypeProvider::uint256(), true);
|
storeInMemoryDynamic(*TypeProvider::uint256(), true);
|
||||||
// stack: ... <end_of_mem> <value...> <end_of_mem''>
|
// stack: ... <end_of_mem> <value...> <end_of_mem''>
|
||||||
// copy the new memory pointer
|
// copy the new memory pointer
|
||||||
m_context << swapInstruction(arrayType.sizeOnStack() + 1) << Instruction::POP;
|
m_context << swapInstruction(arrayType->sizeOnStack() + 1) << Instruction::POP;
|
||||||
// stack: ... <end_of_mem''> <value...>
|
// stack: ... <end_of_mem''> <value...>
|
||||||
// copy data part
|
// copy data part
|
||||||
ArrayUtils(m_context).copyArrayToMemory(arrayType, _padToWordBoundaries);
|
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
|
||||||
// stack: ... <end_of_mem'''>
|
// stack: ... <end_of_mem'''>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,8 +586,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -990,7 +1017,8 @@ void CompilerUtils::convertType(
|
|||||||
solAssert(_targetType == typeOnStack.arrayType(), "");
|
solAssert(_targetType == typeOnStack.arrayType(), "");
|
||||||
solUnimplementedAssert(
|
solUnimplementedAssert(
|
||||||
typeOnStack.arrayType().location() == DataLocation::CallData &&
|
typeOnStack.arrayType().location() == DataLocation::CallData &&
|
||||||
typeOnStack.arrayType().isDynamicallySized(),
|
typeOnStack.arrayType().isDynamicallySized() &&
|
||||||
|
!typeOnStack.arrayType().baseType()->isDynamicallyEncoded(),
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -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
|
||||||
@ -1760,7 +1769,12 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
case Type::Category::ArraySlice:
|
case Type::Category::ArraySlice:
|
||||||
{
|
{
|
||||||
auto const& arrayType = dynamic_cast<ArraySliceType const&>(baseType).arrayType();
|
auto const& arrayType = dynamic_cast<ArraySliceType const&>(baseType).arrayType();
|
||||||
solAssert(arrayType.location() == DataLocation::CallData && arrayType.isDynamicallySized(), "");
|
solAssert(
|
||||||
|
arrayType.location() == DataLocation::CallData &&
|
||||||
|
arrayType.isDynamicallySized() &&
|
||||||
|
!arrayType.baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
solAssert(_indexAccess.indexExpression(), "Index expression expected.");
|
||||||
|
|
||||||
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
|
acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true);
|
||||||
@ -1843,7 +1857,12 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess)
|
|||||||
arrayType = &sliceType->arrayType();
|
arrayType = &sliceType->arrayType();
|
||||||
|
|
||||||
solAssert(arrayType, "");
|
solAssert(arrayType, "");
|
||||||
solUnimplementedAssert(arrayType->location() == DataLocation::CallData && arrayType->isDynamicallySized(), "");
|
solUnimplementedAssert(
|
||||||
|
arrayType->location() == DataLocation::CallData &&
|
||||||
|
arrayType->isDynamicallySized() &&
|
||||||
|
!arrayType->baseType()->isDynamicallyEncoded(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
|
||||||
if (_indexAccess.startExpression())
|
if (_indexAccess.startExpression())
|
||||||
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
|
acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256());
|
||||||
|
@ -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()}
|
||||||
@ -1511,6 +1599,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
ArraySliceType const& fromType = dynamic_cast<ArraySliceType const&>(_from);
|
ArraySliceType const& fromType = dynamic_cast<ArraySliceType const&>(_from);
|
||||||
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_to);
|
ArrayType const& targetType = dynamic_cast<ArrayType const&>(_to);
|
||||||
|
|
||||||
|
solAssert(!fromType.arrayType().baseType()->isDynamicallyEncoded(), "");
|
||||||
solAssert(
|
solAssert(
|
||||||
*fromType.arrayType().baseType() == *targetType.baseType(),
|
*fromType.arrayType().baseType() == *targetType.baseType(),
|
||||||
"Converting arrays of different type is not possible"
|
"Converting arrays of different type is not possible"
|
||||||
@ -1764,9 +1853,19 @@ 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())
|
||||||
|
{
|
||||||
|
case FunctionType::Kind::External:
|
||||||
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
|
||||||
break;
|
break;
|
||||||
|
case FunctionType::Kind::Internal:
|
||||||
|
templ("body", "cleaned := value");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solAssert(false, "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Type::Category::Array:
|
case Type::Category::Array:
|
||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
case Type::Category::Mapping:
|
case Type::Category::Mapping:
|
||||||
@ -1938,6 +2037,7 @@ 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)
|
||||||
}
|
}
|
||||||
@ -1945,6 +2045,7 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
|
|||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("minval", toCompactHexWithPrefix(minintval))
|
("minval", toCompactHexWithPrefix(minintval))
|
||||||
("lt", type.isSigned() ? "slt" : "lt")
|
("lt", type.isSigned() ? "slt" : "lt")
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1966,6 +2067,7 @@ 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)
|
||||||
}
|
}
|
||||||
@ -1973,6 +2075,7 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
|
|||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("maxval", toCompactHexWithPrefix(maxintval))
|
("maxval", toCompactHexWithPrefix(maxintval))
|
||||||
("gt", type.isSigned() ? "sgt" : "gt")
|
("gt", type.isSigned() ? "sgt" : "gt")
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1988,13 +2091,15 @@ 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))
|
||||||
|
("cleanupFunction", cleanupFunction(_type))
|
||||||
.render();
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -2214,7 +2319,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 +2332,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.
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||||
#include <libsolidity/codegen/ABIFunctions.h>
|
#include <libsolidity/codegen/ABIFunctions.h>
|
||||||
|
#include <libsolidity/codegen/CompilerUtils.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <libsolidity/ast/TypeProvider.h>
|
#include <libsolidity/ast/TypeProvider.h>
|
||||||
|
|
||||||
@ -76,6 +77,36 @@ IRVariable const& IRGenerationContext::localVariable(VariableDeclaration const&
|
|||||||
return m_localVariables.at(&_varDecl);
|
return m_localVariables.at(&_varDecl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IRGenerationContext::registerImmutableVariable(VariableDeclaration const& _variable)
|
||||||
|
{
|
||||||
|
solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable.");
|
||||||
|
solUnimplementedAssert(
|
||||||
|
_variable.annotation().type->isValueType(),
|
||||||
|
"Only immutable variables of value type are supported."
|
||||||
|
);
|
||||||
|
solAssert(m_reservedMemory.has_value(), "Reserved memory has already been reset.");
|
||||||
|
m_immutableVariables[&_variable] = CompilerUtils::generalPurposeMemoryStart + *m_reservedMemory;
|
||||||
|
solAssert(_variable.annotation().type->memoryHeadSize() == 32, "Memory writes might overlap.");
|
||||||
|
*m_reservedMemory += _variable.annotation().type->memoryHeadSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t IRGenerationContext::immutableMemoryOffset(VariableDeclaration const& _variable) const
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
m_immutableVariables.count(&_variable),
|
||||||
|
"Unknown immutable variable: " + _variable.name()
|
||||||
|
);
|
||||||
|
return m_immutableVariables.at(&_variable);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t IRGenerationContext::reservedMemory()
|
||||||
|
{
|
||||||
|
solAssert(m_reservedMemory.has_value(), "Reserved memory was used before.");
|
||||||
|
size_t reservedMemory = *m_reservedMemory;
|
||||||
|
m_reservedMemory = std::nullopt;
|
||||||
|
return reservedMemory;
|
||||||
|
}
|
||||||
|
|
||||||
void IRGenerationContext::addStateVariable(
|
void IRGenerationContext::addStateVariable(
|
||||||
VariableDeclaration const& _declaration,
|
VariableDeclaration const& _declaration,
|
||||||
u256 _storageOffset,
|
u256 _storageOffset,
|
||||||
|
@ -81,6 +81,17 @@ public:
|
|||||||
bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
|
bool isLocalVariable(VariableDeclaration const& _varDecl) const { return m_localVariables.count(&_varDecl); }
|
||||||
IRVariable const& localVariable(VariableDeclaration const& _varDecl);
|
IRVariable const& localVariable(VariableDeclaration const& _varDecl);
|
||||||
|
|
||||||
|
/// Registers an immutable variable of the contract.
|
||||||
|
/// Should only be called at construction time.
|
||||||
|
void registerImmutableVariable(VariableDeclaration const& _varDecl);
|
||||||
|
/// @returns the reserved memory for storing the value of the
|
||||||
|
/// immutable @a _variable during contract creation.
|
||||||
|
size_t immutableMemoryOffset(VariableDeclaration const& _variable) const;
|
||||||
|
/// @returns the reserved memory and resets it to mark it as used.
|
||||||
|
/// Intended to be used only once for initializing the free memory pointer
|
||||||
|
/// to after the area used for immutables.
|
||||||
|
size_t reservedMemory();
|
||||||
|
|
||||||
void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
|
void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset);
|
||||||
bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
|
bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); }
|
||||||
std::pair<u256, unsigned> storageLocationOfVariable(VariableDeclaration const& _varDecl) const
|
std::pair<u256, unsigned> storageLocationOfVariable(VariableDeclaration const& _varDecl) const
|
||||||
@ -123,6 +134,12 @@ private:
|
|||||||
OptimiserSettings m_optimiserSettings;
|
OptimiserSettings m_optimiserSettings;
|
||||||
ContractDefinition const* m_mostDerivedContract = nullptr;
|
ContractDefinition const* m_mostDerivedContract = nullptr;
|
||||||
std::map<VariableDeclaration const*, IRVariable> m_localVariables;
|
std::map<VariableDeclaration const*, IRVariable> m_localVariables;
|
||||||
|
/// Memory offsets reserved for the values of immutable variables during contract creation.
|
||||||
|
/// This map is empty in the runtime context.
|
||||||
|
std::map<VariableDeclaration const*, size_t> m_immutableVariables;
|
||||||
|
/// Total amount of reserved memory. Reserved memory is used to store
|
||||||
|
/// immutable variables during contract creation.
|
||||||
|
std::optional<size_t> m_reservedMemory = {0};
|
||||||
/// Storage offsets of state variables
|
/// Storage offsets of state variables
|
||||||
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||||
MultiUseYulFunctionCollector m_functions;
|
MultiUseYulFunctionCollector m_functions;
|
||||||
|
@ -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;
|
||||||
@ -114,6 +111,8 @@ string IRGenerator::generate(
|
|||||||
)");
|
)");
|
||||||
|
|
||||||
resetContext(_contract);
|
resetContext(_contract);
|
||||||
|
for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
|
||||||
|
m_context.registerImmutableVariable(*var);
|
||||||
|
|
||||||
t("CreationObject", m_context.creationObjectName(_contract));
|
t("CreationObject", m_context.creationObjectName(_contract));
|
||||||
t("memoryInit", memoryInit());
|
t("memoryInit", memoryInit());
|
||||||
@ -142,6 +141,7 @@ string IRGenerator::generate(
|
|||||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||||
|
|
||||||
resetContext(_contract);
|
resetContext(_contract);
|
||||||
|
// Do not register immutables to avoid assignment.
|
||||||
t("RuntimeObject", m_context.runtimeObjectName(_contract));
|
t("RuntimeObject", m_context.runtimeObjectName(_contract));
|
||||||
t("dispatch", dispatchRoutine(_contract));
|
t("dispatch", dispatchRoutine(_contract));
|
||||||
generateQueuedFunctions();
|
generateQueuedFunctions();
|
||||||
@ -169,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();
|
||||||
@ -199,12 +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.immutable(), "");
|
|
||||||
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);
|
||||||
@ -254,6 +253,30 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
solUnimplementedAssert(type->isValueType(), "");
|
solUnimplementedAssert(type->isValueType(), "");
|
||||||
|
|
||||||
return m_context.functionCollector().createFunction(functionName, [&]() {
|
return m_context.functionCollector().createFunction(functionName, [&]() {
|
||||||
|
if (_varDecl.immutable())
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(type->sizeOnStack() == 1, "");
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>() -> rval {
|
||||||
|
rval := loadimmutable("<id>")
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("id", to_string(_varDecl.id()))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
else if (_varDecl.isConstant())
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>() -> <ret> {
|
||||||
|
<ret> := <constantValueFunction>()
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
|
||||||
|
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
|
||||||
|
.render();
|
||||||
|
else
|
||||||
|
{
|
||||||
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
|
||||||
|
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
@ -265,6 +288,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
|
|||||||
("readStorage", m_utils.readFromStorage(*type, slot_offset.second, false))
|
("readStorage", m_utils.readFromStorage(*type, slot_offset.second, false))
|
||||||
("slot", slot_offset.first.str())
|
("slot", slot_offset.first.str())
|
||||||
.render();
|
.render();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,11 +300,11 @@ 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())
|
||||||
@ -293,10 +317,14 @@ pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstr
|
|||||||
);
|
);
|
||||||
|
|
||||||
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())
|
))
|
||||||
|
if (
|
||||||
|
FunctionDefinition const* baseConstructor = baseContract->constructor();
|
||||||
|
baseConstructor && modifier->arguments()
|
||||||
|
)
|
||||||
baseConstructorArguments.emplace_back(
|
baseConstructorArguments.emplace_back(
|
||||||
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
|
||||||
modifier->arguments()
|
modifier->arguments()
|
||||||
@ -310,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +353,7 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
|
|||||||
{
|
{
|
||||||
IRGeneratorForStatements generator{m_context, m_utils};
|
IRGeneratorForStatements generator{m_context, m_utils};
|
||||||
for (VariableDeclaration const* variable: _contract.stateVariables())
|
for (VariableDeclaration const* variable: _contract.stateVariables())
|
||||||
if (!variable->isConstant() && !variable->immutable())
|
if (!variable->isConstant())
|
||||||
generator.initializeStateVar(*variable);
|
generator.initializeStateVar(*variable);
|
||||||
|
|
||||||
return generator.code();
|
return generator.code();
|
||||||
@ -335,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];
|
||||||
@ -359,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)
|
||||||
@ -376,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);
|
||||||
@ -391,10 +419,41 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
|
|||||||
string IRGenerator::deployCode(ContractDefinition const& _contract)
|
string IRGenerator::deployCode(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
Whiskers t(R"X(
|
Whiskers t(R"X(
|
||||||
|
<#loadImmutables>
|
||||||
|
let <var> := mload(<memoryOffset>)
|
||||||
|
</loadImmutables>
|
||||||
|
|
||||||
codecopy(0, dataoffset("<object>"), datasize("<object>"))
|
codecopy(0, dataoffset("<object>"), datasize("<object>"))
|
||||||
|
|
||||||
|
<#storeImmutables>
|
||||||
|
setimmutable("<immutableName>", <var>)
|
||||||
|
</storeImmutables>
|
||||||
|
|
||||||
return(0, datasize("<object>"))
|
return(0, datasize("<object>"))
|
||||||
)X");
|
)X");
|
||||||
t("object", m_context.runtimeObjectName(_contract));
|
t("object", m_context.runtimeObjectName(_contract));
|
||||||
|
|
||||||
|
vector<map<string, string>> loadImmutables;
|
||||||
|
vector<map<string, string>> storeImmutables;
|
||||||
|
|
||||||
|
for (VariableDeclaration const* immutable: ContractType(_contract).immutableVariables())
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(immutable->type()->isValueType(), "");
|
||||||
|
solUnimplementedAssert(immutable->type()->sizeOnStack() == 1, "");
|
||||||
|
string yulVar = m_context.newYulVariable();
|
||||||
|
loadImmutables.emplace_back(map<string, string>{
|
||||||
|
{"var"s, yulVar},
|
||||||
|
{"memoryOffset"s, to_string(m_context.immutableMemoryOffset(*immutable))}
|
||||||
|
});
|
||||||
|
storeImmutables.emplace_back(map<string, string>{
|
||||||
|
{"var"s, yulVar},
|
||||||
|
{"immutableName"s, to_string(immutable->id())}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
t("loadImmutables", std::move(loadImmutables));
|
||||||
|
// reverse order to ease stack strain
|
||||||
|
reverse(storeImmutables.begin(), storeImmutables.end());
|
||||||
|
t("storeImmutables", std::move(storeImmutables));
|
||||||
return t.render();
|
return t.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,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>
|
||||||
@ -445,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);
|
||||||
@ -462,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())
|
||||||
@ -489,9 +545,9 @@ string IRGenerator::memoryInit()
|
|||||||
// and thus can assume all memory to be zero, including the contents of
|
// and thus can assume all memory to be zero, including the contents of
|
||||||
// the "zero memory area" (the position CompilerUtils::zeroPointer points to).
|
// the "zero memory area" (the position CompilerUtils::zeroPointer points to).
|
||||||
return
|
return
|
||||||
Whiskers{"mstore(<memPtr>, <generalPurposeStart>)"}
|
Whiskers{"mstore(<memPtr>, <freeMemoryStart>)"}
|
||||||
("memPtr", to_string(CompilerUtils::freeMemoryPointer))
|
("memPtr", to_string(CompilerUtils::freeMemoryPointer))
|
||||||
("generalPurposeStart", to_string(CompilerUtils::generalPurposeMemoryStart))
|
("freeMemoryStart", to_string(CompilerUtils::generalPurposeMemoryStart + m_context.reservedMemory()))
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,20 +141,21 @@ string IRGeneratorForStatements::code() const
|
|||||||
|
|
||||||
void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl)
|
void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
solAssert(m_context.isStateVariable(_varDecl), "Must be a state variable.");
|
solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable.");
|
||||||
solAssert(!_varDecl.isConstant(), "");
|
solAssert(!_varDecl.isConstant(), "");
|
||||||
solAssert(!_varDecl.immutable(), "");
|
if (!_varDecl.value())
|
||||||
if (_varDecl.value())
|
return;
|
||||||
{
|
|
||||||
_varDecl.value()->accept(*this);
|
_varDecl.value()->accept(*this);
|
||||||
writeToLValue(IRLValue{
|
writeToLValue(
|
||||||
*_varDecl.annotation().type,
|
_varDecl.immutable() ?
|
||||||
IRLValue::Storage{
|
IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} :
|
||||||
|
IRLValue{*_varDecl.annotation().type, IRLValue::Storage{
|
||||||
util::toCompactHexWithPrefix(m_context.storageLocationOfVariable(_varDecl).first),
|
util::toCompactHexWithPrefix(m_context.storageLocationOfVariable(_varDecl).first),
|
||||||
m_context.storageLocationOfVariable(_varDecl).second
|
m_context.storageLocationOfVariable(_varDecl).second
|
||||||
}
|
}},
|
||||||
}, *_varDecl.value());
|
*_varDecl.value()
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl)
|
void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl)
|
||||||
@ -177,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())
|
||||||
@ -229,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(
|
|
||||||
|
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())
|
&type(_assignment.leftHandSide())
|
||||||
);
|
);
|
||||||
IRVariable value = convert(_assignment.rightHandSide(), *intermediateType);
|
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);
|
||||||
|
if (TokenTraits::isShiftOp(binaryOperator))
|
||||||
|
{
|
||||||
|
solAssert(type(_assignment) == leftIntermediate.type(), "");
|
||||||
|
solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
|
||||||
|
define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value);
|
||||||
|
|
||||||
|
writeToLValue(*m_currentLValue, IRVariable(_assignment));
|
||||||
|
m_currentLValue.reset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(type(_assignment.leftHandSide()) == *rightIntermediateType, "");
|
||||||
m_code << value.name() << " := " << binaryOperation(
|
m_code << value.name() << " := " << binaryOperation(
|
||||||
TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()),
|
binaryOperator,
|
||||||
*intermediateType,
|
*rightIntermediateType,
|
||||||
leftIntermediate.name(),
|
leftIntermediate.name(),
|
||||||
value.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);
|
||||||
@ -476,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))
|
||||||
{
|
{
|
||||||
@ -495,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)
|
||||||
@ -516,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);
|
||||||
@ -583,13 +641,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
break;
|
break;
|
||||||
case FunctionType::Kind::Internal:
|
case FunctionType::Kind::Internal:
|
||||||
{
|
{
|
||||||
vector<string> args;
|
|
||||||
for (unsigned 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()))
|
||||||
{
|
{
|
||||||
@ -627,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()) <<
|
||||||
@ -648,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.");
|
||||||
@ -662,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())
|
||||||
@ -685,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]);
|
||||||
}
|
}
|
||||||
@ -703,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();
|
||||||
@ -730,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;
|
||||||
}
|
}
|
||||||
@ -759,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) <<
|
||||||
@ -828,15 +1007,43 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FunctionType::Kind::GasLeft:
|
case FunctionType::Kind::AddMod:
|
||||||
|
case FunctionType::Kind::MulMod:
|
||||||
{
|
{
|
||||||
define(_functionCall) << "gas()\n";
|
static map<FunctionType::Kind, string> functions = {
|
||||||
|
{FunctionType::Kind::AddMod, "addmod"},
|
||||||
|
{FunctionType::Kind::MulMod, "mulmod"},
|
||||||
|
};
|
||||||
|
solAssert(functions.find(functionType->kind()) != functions.end(), "");
|
||||||
|
solAssert(arguments.size() == 3 && parameterTypes.size() == 3, "");
|
||||||
|
|
||||||
|
IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2]));
|
||||||
|
define(modulus, *arguments[2]);
|
||||||
|
Whiskers templ("if iszero(<modulus>) { invalid() }\n");
|
||||||
|
m_code << templ("modulus", modulus.name()).render();
|
||||||
|
|
||||||
|
string args;
|
||||||
|
for (size_t i = 0; i < 2; ++i)
|
||||||
|
args += expressionAsType(*arguments[i], *(parameterTypes[i])) + ", ";
|
||||||
|
args += modulus.name();
|
||||||
|
define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case FunctionType::Kind::GasLeft:
|
||||||
case FunctionType::Kind::Selfdestruct:
|
case FunctionType::Kind::Selfdestruct:
|
||||||
|
case FunctionType::Kind::BlockHash:
|
||||||
{
|
{
|
||||||
solAssert(arguments.size() == 1, "");
|
static map<FunctionType::Kind, string> functions = {
|
||||||
define(_functionCall) << "selfdestruct(" << expressionAsType(*arguments.front(), *parameterTypes.front()) << ")\n";
|
{FunctionType::Kind::GasLeft, "gas"},
|
||||||
|
{FunctionType::Kind::Selfdestruct, "selfdestruct"},
|
||||||
|
{FunctionType::Kind::BlockHash, "blockhash"},
|
||||||
|
};
|
||||||
|
solAssert(functions.find(functionType->kind()) != functions.end(), "");
|
||||||
|
|
||||||
|
string args;
|
||||||
|
for (size_t i = 0; i < arguments.size(); ++i)
|
||||||
|
args += (args.empty() ? "" : ", ") + expressionAsType(*arguments[i], *(parameterTypes[i]));
|
||||||
|
define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FunctionType::Kind::Log0:
|
case FunctionType::Kind::Log0:
|
||||||
@ -876,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 =
|
||||||
@ -908,7 +1115,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
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())
|
||||||
@ -1097,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")
|
||||||
{
|
{
|
||||||
@ -1112,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
|
||||||
@ -1283,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,
|
||||||
@ -1513,12 +1740,14 @@ 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(), "");
|
setLValue(_referencingExpression, IRLValue{
|
||||||
solUnimplementedAssert(!_variable.immutable(), "");
|
*_variable.annotation().type,
|
||||||
if (m_context.isLocalVariable(_variable))
|
IRLValue::Immutable{&_variable}
|
||||||
|
});
|
||||||
|
else if (m_context.isLocalVariable(_variable))
|
||||||
setLValue(_referencingExpression, IRLValue{
|
setLValue(_referencingExpression, IRLValue{
|
||||||
*_variable.annotation().type,
|
*_variable.annotation().type,
|
||||||
IRLValue::Stack{m_context.localVariable(_variable)}
|
IRLValue::Stack{m_context.localVariable(_variable)}
|
||||||
@ -1541,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};
|
||||||
|
|
||||||
@ -1561,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())
|
||||||
{
|
{
|
||||||
@ -1578,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>
|
|
||||||
<!bareCall>
|
|
||||||
mstore(<pos>, <shl28>(<funId>))
|
mstore(<pos>, <shl28>(<funId>))
|
||||||
</bareCall>
|
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
|
||||||
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
|
||||||
@ -1615,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
|
||||||
@ -1628,16 +1841,7 @@ 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());
|
||||||
|
|
||||||
if (funKind == FunctionType::Kind::ECRecover)
|
|
||||||
templ("address", "1");
|
|
||||||
else if (funKind == FunctionType::Kind::SHA256)
|
|
||||||
templ("address", "2");
|
|
||||||
else if (funKind == FunctionType::Kind::RIPEMD160)
|
|
||||||
templ("address", "3");
|
|
||||||
else
|
|
||||||
templ("address", IRVariable(_functionCall.expression()).part("address").name());
|
templ("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.
|
||||||
@ -1651,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");
|
||||||
@ -1691,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())
|
||||||
@ -1707,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.
|
||||||
@ -1721,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();
|
||||||
}
|
}
|
||||||
@ -1744,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)
|
||||||
|
{
|
||||||
|
if (_forceCleanup)
|
||||||
|
return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")";
|
||||||
|
else
|
||||||
return from.commaSeparatedList();
|
return from.commaSeparatedList();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
|
||||||
}
|
}
|
||||||
@ -1827,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;
|
||||||
@ -1870,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();
|
||||||
@ -1939,6 +2247,18 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
|
[&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); },
|
||||||
|
[&](IRLValue::Immutable const& _immutable)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_lvalue.type.isValueType(), "");
|
||||||
|
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1, "");
|
||||||
|
solAssert(_lvalue.type == *_immutable.variable->type(), "");
|
||||||
|
size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable);
|
||||||
|
|
||||||
|
IRVariable prepared(m_context.newYulVariable(), _lvalue.type);
|
||||||
|
define(prepared, _value);
|
||||||
|
|
||||||
|
m_code << "mstore(" << to_string(memOffset) << ", " << prepared.commaSeparatedList() << ")\n";
|
||||||
|
},
|
||||||
[&](IRLValue::Tuple const& _tuple) {
|
[&](IRLValue::Tuple const& _tuple) {
|
||||||
auto components = std::move(_tuple.components);
|
auto components = std::move(_tuple.components);
|
||||||
for (size_t i = 0; i < components.size(); i++)
|
for (size_t i = 0; i < components.size(); i++)
|
||||||
@ -1976,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) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
@ -1994,6 +2308,12 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
|
|||||||
[&](IRLValue::Stack const& _stack) {
|
[&](IRLValue::Stack const& _stack) {
|
||||||
define(result, _stack.variable);
|
define(result, _stack.variable);
|
||||||
},
|
},
|
||||||
|
[&](IRLValue::Immutable const& _immutable) {
|
||||||
|
solUnimplementedAssert(_lvalue.type.isValueType(), "");
|
||||||
|
solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1, "");
|
||||||
|
solAssert(_lvalue.type == *_immutable.variable->type(), "");
|
||||||
|
define(result) << "loadimmutable(\"" << to_string(_immutable.variable->id()) << "\")\n";
|
||||||
|
},
|
||||||
[&](IRLValue::Tuple const&) {
|
[&](IRLValue::Tuple const&) {
|
||||||
solAssert(false, "Attempted to read from tuple lvalue.");
|
solAssert(false, "Attempted to read from tuple lvalue.");
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -35,6 +35,10 @@ struct IRLValue
|
|||||||
{
|
{
|
||||||
IRVariable variable;
|
IRVariable variable;
|
||||||
};
|
};
|
||||||
|
struct Immutable
|
||||||
|
{
|
||||||
|
VariableDeclaration const* variable = nullptr;
|
||||||
|
};
|
||||||
struct Storage
|
struct Storage
|
||||||
{
|
{
|
||||||
std::string const slot;
|
std::string const slot;
|
||||||
@ -59,7 +63,7 @@ struct IRLValue
|
|||||||
{
|
{
|
||||||
std::vector<std::optional<IRLValue>> components;
|
std::vector<std::optional<IRLValue>> components;
|
||||||
};
|
};
|
||||||
std::variant<Stack, Storage, Memory, Tuple> kind;
|
std::variant<Stack, Immutable, Storage, Memory, Tuple> kind;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user