Merge branch 'develop' into breaking

Manual Resolved Conflicts:
	Changelog.md
	 * Updated changelog
	test/externalTests/ens.sh
	 * Merged fixes for upstream from both develop and breaking
	test/libsolidity/semanticTests/inlineAssembly/external_identifier_access_shadowing.sol
	 * Removed in #11735 (breaking)
	test/libsolidity/semanticTests/inlineAssembly/function_name_clash.sol
	 * Removed in #12209 (breaking)
	test/libsolidity/semanticTests/storage/mappings_array2d_pop_delete.sol
	 * Removed in #11843 (breaking)
	test/libsolidity/semanticTests/storage/mappings_array_pop_delete.sol
	 * Removed in #11843 (breaking)
	test/libsolidity/syntaxTests/inlineAssembly/basefee_berlin_function.sol
	 * Used version of file from #11842 (breaking)
This commit is contained in:
Marenz 2022-08-30 18:00:33 +02:00
commit 5663fbb903
1924 changed files with 11977 additions and 5668 deletions

View File

@ -9,20 +9,20 @@ version: 2.1
parameters:
ubuntu-2004-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-13
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:aa64242ecba4f040a839eadfaf20e8489cf93d1cb96ab90df2b240cdbbfe7f7c"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-14
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d1ef23849db4c5462b248d89c111da4009b153cbd5002cb8755b0580312be581"
ubuntu-2004-clang-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-13
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:caaf8d42aaf07397d1540e570f096a4fb1ef11fda7da3f1141d8852ec8322a9e"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-14
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:beb8c91998ec0df99a488900b3723a06f1122f0954fc73786b6c53fd73a6408d"
ubuntu-1604-clang-ossfuzz-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-18
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:048002d71a1f86f83dedb79dd057760b752256c75646ba5ad5c1bbe92e1695aa"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-19
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:8c9bf1813c261d781f4c65fceed2dfb3ecf5be9ecf49bddbd250b570a7f3baea"
emscripten-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:emscripten-11
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:0ad7c65e8c54d926ba9cb80d56246e4fc49f9284ad5188aaaa4834f46ab0c315"
# solbuildpackpusher/solidity-buildpack-deps:emscripten-13
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:f1c13f3450d1f2e53ea18ac1ac1a17e932573cb9a5ccd0fd9ef6dd44f6402fa9"
evm-version:
type: string
default: london
@ -36,7 +36,7 @@ commands:
parameters:
event:
type: enum
enum: ["failure", "success"]
enum: ["failure", "success", "release"]
condition:
type: string
steps:
@ -58,6 +58,10 @@ commands:
[[ "<< parameters.event >>" == "failure" ]] && message=" ❌ [${workflow_name}] Job ${job} failed on **${CIRCLE_BRANCH}**. Please see [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
[[ "<< parameters.event >>" == "success" ]] && message=" ✅ [${workflow_name}] Job ${job} succeeded on **${CIRCLE_BRANCH}**. Please see [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
[[ "<< parameters.event >>" == "release" ]] && message=" 📦 Release binaries for version **${CIRCLE_TAG}** are ready and attached as artifacts to [build ${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}). **Please make sure the whole workflow succeeded before using them.**"
# The release notification only makes sense on tagged commits. If the commit is untagged, just bail out.
[[ "<< parameters.event >>" == "release" ]] && { [[ $CIRCLE_TAG != "" ]] || { echo "Not a tagged commit - notification skipped."; exit 0; } }
curl "https://api.gitter.im/v1/rooms/${GITTER_NOTIFY_ROOM_ID}/chatMessages" \
--request POST \
@ -81,6 +85,34 @@ commands:
event: success
condition: on_success
gitter_notify_release_unless_pr:
description: "Posts a release notification to the main room on Gitter (if not running on a PR)."
steps:
- gitter_notify_unless_pr:
event: release
condition: on_success
prepare_bytecode_report:
description: "Generate bytecode report and upload it as an artifact."
parameters:
label:
type: string
steps:
- run: mkdir test-cases/
- run: cd test-cases && ../scripts/isolate_tests.py ../test/
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface standard-json --report-file "../bytecode-report-<< parameters.label >>-json.txt"
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface cli --report-file "../bytecode-report-<< parameters.label >>-cli.txt"
- store_artifacts:
path: bytecode-report-<< parameters.label >>-json.txt
- store_artifacts:
path: bytecode-report-<< parameters.label >>-cli.txt
- persist_to_workspace:
root: .
paths:
- bytecode-report-<< parameters.label >>-json.txt
- bytecode-report-<< parameters.label >>-cli.txt
- gitter_notify_failure_unless_pr
defaults:
# --------------------------------------------------------------------------
@ -211,7 +243,7 @@ defaults:
command: pip install --user deepdiff colorama
- run:
name: Executing solc LSP test suite
command: ./test/lsp.py ./build/solc/solc
command: ./test/lsp.py ./build/solc/solc --non-interactive
- gitter_notify_failure_unless_pr
- steps_build: &steps_build
@ -418,6 +450,13 @@ defaults:
tags:
only: /.*/
- workflow_trigger_on_releases: &workflow_trigger_on_releases
filters:
tags:
only: /^v.*/
branches:
ignore: /.*/
- workflow_ubuntu2004: &workflow_ubuntu2004
<<: *workflow_trigger_on_tags
requires:
@ -588,6 +627,20 @@ defaults:
project: brink
binary_type: native
nodejs_version: '16'
- job_native_test_ext_chainlink: &job_native_test_ext_chainlink
<<: *workflow_ubuntu2004_static
name: t_native_test_ext_chainlink
project: chainlink
binary_type: native
nodejs_version: '16'
resource_class: large # Tests run out of memory on a smaller machine
- job_native_test_ext_gp2: &job_native_test_ext_gp2
<<: *workflow_ubuntu2004_static
name: t_native_test_ext_gp2
project: gp2
binary_type: native
# NOTE: Tests crash on nodejs 17: "Error: error:0308010C:digital envelope routines::unsupported"
nodejs_version: '16'
- job_ems_test_ext_colony: &job_ems_test_ext_colony
<<: *workflow_emscripten
name: t_ems_test_ext_colony
@ -620,7 +673,7 @@ jobs:
pip install --user codespell
- run:
name: Check spelling
command: ~/.local/bin/codespell -S "*.enc,.git,Dockerfile*" -I ./scripts/codespell_whitelist.txt
command: ~/.local/bin/codespell --skip "*.enc,.git,Dockerfile*,LICENSE,codespell_whitelist.txt,codespell_ignored_lines.txt" --ignore-words ./scripts/codespell_whitelist.txt --exclude-file ./scripts/codespell_ignored_lines.txt
- gitter_notify_failure_unless_pr
chk_docs_examples:
@ -672,9 +725,17 @@ jobs:
name: Install pip
command: apt -q update && apt install -y python3-pip
- run:
name: Install pylint
command: python3 -m pip install pylint z3-solver pygments-lexer-solidity parsec tabulate deepdiff colorama
# also z3-solver, parsec and tabulate to make sure pylint knows about this module, pygments-lexer-solidity for docs
name: Install pylint and dependencies of the scripts that will be linted
command: python3 -m pip install
pylint
z3-solver
pygments-lexer-solidity
parsec
tabulate
deepdiff
colorama
requests
- run: pylint --version
- run:
name: Linting Python Scripts
command: ./scripts/pylint_all.py
@ -887,11 +948,14 @@ jobs:
environment:
TERM: xterm
MAKEFLAGS: -j 5
# Build without Z3. We won't be running SMT tests on Arch because that requires a specific
# version of Z3 and the one with the official repos is often not the one we need.
USE_Z3: OFF
steps:
- run:
name: Install build dependencies
command: |
pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake z3 cvc4 git openssh tar
pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake cvc4 git openssh tar
- checkout
- run: *run_build
- store_artifacts: *artifacts_solc
@ -1007,7 +1071,7 @@ jobs:
TERM: xterm
# For Archlinux we do not have prebuilt docker images and we would need to build evmone from source,
# thus we forgo semantics tests to speed things up.
SOLTEST_FLAGS: --no-semantic-tests
SOLTEST_FLAGS: --no-semantic-tests --no-smt
steps:
- run:
name: Install runtime dependencies
@ -1017,22 +1081,15 @@ jobs:
condition: true
<<: *steps_soltest
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
<<: *base_ubuntu2004
parallelism: 20
environment:
EVM: << pipeline.parameters.evm-version >>
SOLTEST_FLAGS: --enforce-via-yul
OPTIMIZE: 0
TERM: xterm
<<: *steps_soltest
t_ubu_clang_soltest: &t_ubu_clang_soltest
<<: *base_ubuntu2004_clang
parallelism: 20
environment:
EVM: << pipeline.parameters.evm-version >>
OPTIMIZE: 0
# The high parallelism in this job is causing the SMT tests to run out of memory,
# so disabling for now.
SOLTEST_FLAGS: --no-smt
<<: *steps_soltest
t_ubu_release_soltest_all: &t_ubu_release_soltest_all
@ -1219,16 +1276,70 @@ jobs:
- checkout
- attach_workspace:
at: .
- run:
name: Install dependencies of helper scripts
command: |
sudo apt update
sudo apt install python3-pip --assume-yes --no-install-recommends
python3 -m pip install requests --user
- run:
name: Combine benchmark reports
command: cat reports/externalTests/benchmark-*.json | scripts/externalTests/merge_benchmarks.sh > reports/externalTests/all-benchmarks.json
- run:
name: Summarize reports
command: cat reports/externalTests/all-benchmarks.json | scripts/externalTests/summarize_benchmarks.sh > reports/externalTests/summarized-benchmarks.json
- run:
name: Download reports from base branch
command: |
if [[ $CIRCLE_PULL_REQUEST != "" ]]; then
mkdir reports/externalTests/base-branch/
cd reports/externalTests/base-branch/
pr_id=$(echo "$CIRCLE_PULL_REQUEST" | sed 's|\(.*\)\/||')
scripts_dir=../../../scripts
# Our main goal here is to provide new benchmarks, the diff is optional. When benchmarks from
# the previous run are not available for whatever reason, we still succeed and just skip the diff.
# download_benchmarks.py exits with status 2 in that case.
if "${scripts_dir}/externalTests/download_benchmarks.py" --base-of-pr "$pr_id" || [[ $? == 2 ]]; then
echo 'export SKIP_BENCHMARK_DIFF=true' >> $BASH_ENV
fi
fi
- run:
name: Diff benchmarks
command: |
if [[ $CIRCLE_PULL_REQUEST != "" && $SKIP_BENCHMARK_DIFF != "true" ]]; then
cd reports/externalTests/
mkdir diff/
scripts_dir=../../scripts
"${scripts_dir}/externalTests/benchmark_diff.py" table \
--output-format markdown \
--style humanized \
base-branch/summarized-benchmarks-*.json \
summarized-benchmarks.json > diff/benchmark-diff-summarized-table-markdown-humanized.md
"${scripts_dir}/externalTests/benchmark_diff.py" table \
--output-format markdown \
--style absolute \
base-branch/summarized-benchmarks-*.json \
summarized-benchmarks.json > diff/benchmark-diff-summarized-table-markdown-absolute.md
"${scripts_dir}/externalTests/benchmark_diff.py" inplace \
--style absolute \
base-branch/summarized-benchmarks-*.json \
summarized-benchmarks.json > diff/benchmark-diff-summarized-inplace-absolute.md
"${scripts_dir}/externalTests/benchmark_diff.py" inplace \
--style absolute \
base-branch/all-benchmarks-*.json \
all-benchmarks.json > diff/benchmark-diff-all-table-inplace-absolute.md
fi
- store_artifacts:
path: reports/externalTests/all-benchmarks.json
- store_artifacts:
path: reports/externalTests/summarized-benchmarks.json
- store_artifacts:
path: reports/externalTests/diff/
- store_artifacts:
path: reports/externalTests/base-branch/
b_win: &b_win
<<: *base_win_powershell_large
@ -1288,7 +1399,7 @@ jobs:
command: Get-Content ./test/lsp.py
- run:
name: Executing solc LSP test suite
command: python ./test/lsp.py .\build\solc\Release\solc.exe
command: python ./test/lsp.py .\build\solc\Release\solc.exe --non-interactive
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
@ -1302,20 +1413,8 @@ jobs:
- checkout
- attach_workspace:
at: build
- run: mkdir test-cases/
- run: cd test-cases && ../scripts/isolate_tests.py ../test/
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface standard-json --report-file ../bytecode-report-ubuntu-json.txt
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface cli --report-file ../bytecode-report-ubuntu-cli.txt
- store_artifacts:
path: bytecode-report-ubuntu-json.txt
- store_artifacts:
path: bytecode-report-ubuntu-cli.txt
- persist_to_workspace:
root: .
paths:
- bytecode-report-ubuntu-json.txt
- bytecode-report-ubuntu-cli.txt
- gitter_notify_failure_unless_pr
- prepare_bytecode_report:
label: "ubuntu"
b_bytecode_osx:
<<: *base_osx
@ -1323,20 +1422,8 @@ jobs:
- checkout
- attach_workspace:
at: .
- run: mkdir test-cases/
- run: cd test-cases && ../scripts/isolate_tests.py ../test/
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface standard-json --report-file ../bytecode-report-osx-json.txt
- run: cd test-cases && ../scripts/bytecodecompare/prepare_report.py ../build/solc/solc --interface cli --report-file ../bytecode-report-osx-cli.txt
- store_artifacts:
path: bytecode-report-osx-json.txt
- store_artifacts:
path: bytecode-report-osx-cli.txt
- persist_to_workspace:
root: .
paths:
- bytecode-report-osx-json.txt
- bytecode-report-osx-cli.txt
- gitter_notify_failure_unless_pr
- prepare_bytecode_report:
label: "osx"
b_bytecode_win:
<<: *base_win_cmd
@ -1416,6 +1503,47 @@ jobs:
path: all-bytecode-reports.zip
- gitter_notify_failure_unless_pr
c_release_binaries:
<<: *base_ubuntu2004
steps:
- checkout
- attach_workspace:
at: workspace
- run:
name: Gather and rename binaries from dependent jobs
command: |
mkdir github/
cp workspace/solc/solc github/solc-static-linux
cp workspace/build/solc/solc github/solc-macos
cp workspace/solc/Release/solc.exe github/solc-windows.exe
cp workspace/soljson.js github/soljson.js
cd github/
tar --create --file ../github-binaries.tar *
- store_artifacts:
path: github-binaries.tar
- run:
name: Rename binaries to solc-bin naming convention
command: |
full_version=$(
github/solc-static-linux --version |
sed -En 's/^Version: ([0-9.]+.*\+commit\.[0-9a-f]+(\.mod)?).*$/\1/p'
)
mkdir -p solc-bin/{linux-amd64,macosx-amd64,windows-amd64,bin}
mv github/solc-static-linux "solc-bin/linux-amd64/solc-linux-amd64-v${full_version}"
mv github/solc-macos "solc-bin/macosx-amd64/solc-macosx-amd64-v${full_version}"
mv github/solc-windows.exe "solc-bin/windows-amd64/solc-windows-amd64-v${full_version}.exe"
mv github/soljson.js "solc-bin/bin/soljson-v${full_version}.js"
cd solc-bin/
tar --create --file ../solc-bin-binaries.tar *
- store_artifacts:
path: solc-bin-binaries.tar
- gitter_notify_failure_unless_pr
- gitter_notify_release_unless_pr
workflows:
version: 2
@ -1456,7 +1584,6 @@ workflows:
- t_ubu_cli: *workflow_ubuntu2004
- t_ubu_locale: *workflow_ubuntu2004
- t_ubu_soltest_all: *workflow_ubuntu2004
- t_ubu_soltest_enforce_yul: *workflow_ubuntu2004
- b_ubu_clang: *workflow_trigger_on_tags
- t_ubu_clang_soltest: *workflow_ubuntu2004_clang
- t_ubu_lsp: *workflow_ubuntu2004
@ -1486,6 +1613,8 @@ workflows:
- t_ems_ext: *job_native_test_ext_prb_math
- t_ems_ext: *job_native_test_ext_elementfi
- t_ems_ext: *job_native_test_ext_brink
- t_ems_ext: *job_native_test_ext_chainlink
- t_ems_ext: *job_native_test_ext_gp2
- c_ext_benchmarks:
<<: *workflow_trigger_on_tags
@ -1504,6 +1633,8 @@ workflows:
- t_native_test_ext_prb_math
- t_native_test_ext_elementfi
- t_native_test_ext_brink
- t_native_test_ext_chainlink
- t_native_test_ext_gp2
# Windows build and tests
- b_win: *workflow_trigger_on_tags
@ -1536,6 +1667,15 @@ workflows:
- b_bytecode_osx
- b_bytecode_ems
# Final artifacts
- c_release_binaries:
<<: *workflow_trigger_on_releases
requires:
- b_ubu_static
- b_osx
- b_win_release
- b_ems
nightly:
triggers:

View File

@ -61,11 +61,11 @@ then
./scripts/install_obsolete_jsoncpp_1_7_4.sh
# z3
z3_version="4.8.17"
z3_version="4.11.0"
z3_dir="z3-${z3_version}-x64-osx-10.16"
z3_package="${z3_dir}.zip"
wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_package}"
validate_checksum "$z3_package" 189667930517aee07f1ce36485d5924a9a2cb4f8c3c9586b03e714a2c657541a
validate_checksum "$z3_package" b6a4a6d587e4bfb0643db81129f0f447692fae13d4bd1bd4d93f1c0301b75ffc
unzip "$z3_package"
rm "$z3_package"
cp "${z3_dir}/bin/libz3.a" /usr/local/lib

View File

@ -6,7 +6,7 @@ cd "$PSScriptRoot\.."
if ( -not $? ) { throw "Cannot execute solc --version." }
mkdir test_results
.\build\test\Release\soltest.exe --color_output=no --show_progress=yes --logger=JUNIT,error,test_results/result.xml -- --no-smt
.\build\test\Release\soltest.exe --color_output=no --show_progress=yes --logger=JUNIT,error,test_results/result.xml --logger=HRF,error,stdout -- --no-smt
if ( -not $? ) { throw "Unoptimized soltest run failed." }
.\build\test\Release\soltest.exe --color_output=no --show_progress=yes --logger=JUNIT,error,test_results/result_opt.xml -- --optimize --no-smt
if ( -not $? ) { throw "Optimized soltest run failed." }
.\build\test\Release\soltest.exe --color_output=no --show_progress=yes --logger=JUNIT,error,test_results/result_opt.xml --logger=HRF,error,stdout -- --optimize --no-smt
if ( -not $? ) { throw "Optimized soltest run failed." }

View File

@ -75,6 +75,7 @@ do
"--color_output=no"
"--show_progress=yes"
"--logger=JUNIT,error,test_results/$(get_logfile_basename "$((CPUs * CIRCLE_NODE_INDEX + run))").xml"
"--logger=HRF,error,stdout"
"${BOOST_TEST_ARGS[@]}"
)
SOLTEST_ARGS=("--evm-version=$EVM" "${SOLTEST_FLAGS[@]}")

View File

@ -1,14 +1,17 @@
blank_issues_enabled: false
contact_links:
- name: Bug Report
url: https://github.com/ethereum/solidity/issues/new?template=bug_report.md&projects=ethereum/solidity/43&labels=bug+%3Abug%3A
about: Bug reports about the Solidity Compiler.
url: https://github.com/ethereum/solidity/issues/new?template=bug_report.md&projects=ethereum/solidity/43
about: Bug reports for the Solidity Compiler.
- name: Documentation Issue
url: https://github.com/ethereum/solidity/issues/new?template=documentation_issue.md&projects=ethereum/solidity/43&labels=documentation+%3Abook%3A
url: https://github.com/ethereum/solidity/issues/new?template=documentation_issue.md&projects=ethereum/solidity/43
about: Solidity documentation.
- name: Feature Request
url: https://github.com/ethereum/solidity/issues/new?template=feature_request.md&projects=ethereum/solidity/43&labels=feature
url: https://github.com/ethereum/solidity/issues/new?template=feature_request.md&projects=ethereum/solidity/43
about: Solidity language or infrastructure feature requests.
- name: Report a security vulnerability
url: https://github.com/ethereum/solidity/security/policy
about: Please review our security policy for more details.
- name: Initiate a language design or feedback discussion
url: https://forum.soliditylang.org
about: Open a thread on the Solidity forum.

View File

@ -10,6 +10,8 @@ name: Feature Request
- [Solidity chat](https://gitter.im/ethereum/solidity)
- [Stack Overflow](https://ethereum.stackexchange.com/)
- Ensure the issue isn't already reported (check `feature` and `language design` labels).
- If you feel uncertain about your feature request, perhaps it's better to open a language design or feedback forum thread via the issue selector, or by going to the forum directly.
- [Solidity forum](https://forum.soliditylang.org/)
*Delete the above section and the instructions in the sections below before submitting*
-->

View File

@ -37,6 +37,9 @@ jobs:
- name: comment PR
if: "env.DOCKER_IMAGE"
uses: aarlt/comment-on-pr@v1.2.0
# NOTE: Can't update to v1.3.1 due to an error: `/entrypoint.sh:5:in 'require_relative': cannot load such file -- /lib/github (LoadError)`
uses: unsplash/comment-on-pr@ffe8f97ccc63ce12c3c23c6885b169db67958d3b #v1.3.0
with:
msg: "`${{ env.DOCKER_IMAGE }} ${{ env.DOCKER_REPO_DIGEST }}`."
msg: "`${{ env.DOCKER_IMAGE }} ${{ env.DOCKER_REPO_DIGEST }}`."
check_for_duplicate_msg: false

34
.gitignore vendored
View File

@ -1,11 +1,16 @@
commit_hash.txt
prerelease.txt
/commit_hash.txt
/prerelease.txt
# Auth config for ppa release
/.release_ppa_auth
# Compiled Object files
*.slo
*.lo
*.o
*.obj
*.pyc
__pycache__
# Precompiled Headers
*.gch
@ -16,9 +21,6 @@ prerelease.txt
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
@ -33,14 +35,9 @@ prerelease.txt
# Build directory
/build*
emscripten_build/
docs/_build
docs/_static/robots.txt
__pycache__
docs/utils/*.pyc
/deps/downloads/
deps/install
deps/cache
cmake-build-*/
/docs/_build
/docs/_static/robots.txt
/deps
# vim stuff
[._]*.sw[a-p]
@ -50,18 +47,15 @@ cmake-build-*/
*~
# IDE files
.idea
.vscode
browse.VC.db
CMakeLists.txt.user
/.idea/
/.vscode/
/browse.VC.db
/CMakeLists.txt.user
/CMakeSettings.json
/.vs
/.cproject
/.project
# place to put local temporary files
tmp
# OS specific local files
.DS_Store
Thumbs.db

View File

@ -21,7 +21,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.8.15")
set(PROJECT_VERSION "0.8.17")
# OSX target needed in order to support std::visit
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
@ -35,6 +35,7 @@ endif()
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF)
option(STRICT_Z3_VERSION "Use the latest version of Z3" ON)
option(PEDANTIC "Enable extra warnings and pedantic build flags. Treat all warnings as errors." ON)
# Setup cccache.
include(EthCcache)
@ -48,6 +49,9 @@ include_directories(SYSTEM ${JSONCPP_INCLUDE_DIR})
find_package(Threads)
if(NOT PEDANTIC)
message(WARNING "-- Pedantic build flags turned off. Warnings will not make compilation fail. This is NOT recommended in development builds.")
endif()
# Figure out what compiler and system are we using
include(EthCompilerSettings)
@ -65,7 +69,7 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens
include(EthOptions)
configure_project(TESTS)
set(LATEST_Z3_VERSION "4.8.17")
set(LATEST_Z3_VERSION "4.11.0")
set(MINIMUM_Z3_VERSION "4.8.0")
find_package(Z3)
if (${Z3_FOUND})

View File

@ -11,15 +11,69 @@ Breaking changes:
* View Pure Checker: Mark ``returndatasize`` and ``returndatacopy`` as view to disallow them in inline assembly blocks in pure functions.
### 0.8.15 (unreleased)
### 0.8.17 (unreleased)
Important Bugfixes:
Language Features:
Compiler Features:
* Code Generator: More efficient overflow checks for multiplication.
* Yul Optimizer: Simplify the starting offset of zero-length operations to zero.
* Language Server: Analyze all files in a project by default (can be customized by setting ``'file-load-strategy'`` to ``'directly-opened-and-on-import'`` in LSP settings object).
Bugfixes:
* Type Checker: Fix internal compiler error on tuple assignments with invalid left-hand side.
### 0.8.16 (2022-08-08)
Important Bugfixes:
* Code Generation: Fix data corruption that affected ABI-encoding of calldata values represented by tuples: structs at any nesting level; argument lists of external functions, events and errors; return value lists of external functions. The 32 leading bytes of the first dynamically-encoded value in the tuple would get zeroed when the last component contained a statically-encoded array.
Compiler Features:
* Code Generator: More efficient code for checked addition and subtraction.
* TypeChecker: Support using library constants in initializers of other constants.
* Yul IR Code Generation: Improved copy routines for arrays with packed storage layout.
* Yul Optimizer: Add rule to convert ``mod(add(X, Y), A)`` into ``addmod(X, Y, A)``, if ``A`` is a power of two.
* Yul Optimizer: Add rule to convert ``mod(mul(X, Y), A)`` into ``mulmod(X, Y, A)``, if ``A`` is a power of two.
Bugfixes:
* Commandline Interface: Disallow the following options outside of the compiler mode: ``--via-ir``,``--metadata-literal``, ``--metadata-hash``, ``--model-checker-show-unproved``, ``--model-checker-div-mod-no-slacks``, ``--model-checker-engine``, ``--model-checker-invariants``, ``--model-checker-solvers``, ``--model-checker-timeout``, ``--model-checker-contracts``, ``--model-checker-targets``.
* Type Checker: Fix compiler crash on tuple assignments involving certain patterns with unary tuples on the left-hand side.
* Type Checker: Fix compiler crash when ``abi.encodeCall`` received a tuple expression instead of an inline tuple.
* Type Checker: Fix null dereference in ``abi.encodeCall`` type checking of free function.
### 0.8.15 (2022-06-15)
Important Bugfixes:
* Code Generation: Avoid writing dirty bytes to storage when copying ``bytes`` arrays.
* Yul Optimizer: Keep all memory side-effects of inline assembly blocks.
Language Features:
* Add `E.selector` for a non-anonymous event `E` to access the 32-byte selector topic.
Compiler Features:
* Language Server: Add rudimentary support for semantic highlighting.
* Language Server: Adds support for configuring ``include-paths`` JSON settings object that can be passed during LSP configuration stage.
* Language Server: Always add ``{project_root}/node_modules`` to include search paths.
* Type Checker: Warn about assignments involving multiple pushes to storage ``bytes`` that may invalidate references.
* Yul Optimizer: Improve inlining heuristics for via IR code generation and pure Yul compilation.
Bugfixes:
* ABI Encoder: When encoding an empty string coming from storage do not add a superfluous empty slot for data.
* Common Subexpression Eliminator: Process assembly items in chunks with maximum size of 2000. It helps to avoid extremely time-consuming searches during code optimization.
* DocString Parser: Fix ICE caused by an immutable struct with mapping.
* Yul IR Code Generation: More robust cleanup in corner cases during memory to storage copies.
* Yul Optimizer: Do not remove ``returndatacopy`` in cases in which it might perform out-of-bounds reads that unconditionally revert as out-of-gas. Previously, any ``returndatacopy`` that wrote to memory that was never read from was removed without accounting for the out-of-bounds condition.
### 0.8.14 (2022-05-17)
@ -61,9 +115,10 @@ Compiler Features:
* Commandline Interface: Allow the use of ``--via-ir`` in place of ``--experimental-via-ir``.
* Compilation via Yul IR is no longer marked as experimental.
* JSON-AST: Added selector field for errors and events.
* LSP: Implements goto-definition.
* Language Server: Implements goto-definition.
* Peephole Optimizer: Optimize comparisons in front of conditional jumps and conditional jumps across a single unconditional jump.
* Yul EVM Code Transform: Avoid unnecessary ``pop``s on terminating control flow.
* Yul IR Code Generation: When the result of an external call is statically-sized, ignore any returndata past the size expected by the compiler.
* Yul Optimizer: Remove ``sstore`` and ``mstore`` operations that are never read from.

View File

@ -27,7 +27,7 @@ For a good overview and starting point, please check out the official [Solidity
Solidity is a statically-typed curly-braces programming language designed for developing smart contracts
that run on the Ethereum Virtual Machine. Smart contracts are programs that are executed inside a peer-to-peer
network where nobody has special authority over the execution, and thus they allow to implement tokens of value,
network where nobody has special authority over the execution, and thus they allow anyone to implement tokens of value,
ownership, voting, and other kinds of logic.
When deploying contracts, you should use the latest released version of

View File

@ -26,29 +26,16 @@
- [ ] Click the `PUBLISH RELEASE` button on the release page, creating the tag.
- [ ] Wait for the CI runs on the tag itself.
### Upload Release Artifacts
### Upload Release Artifacts and Publish Binaries
- [ ] Switch to the tag that archives have to be created for.
- [ ] Create the ``prerelease.txt`` file: (``echo -n > prerelease.txt``).
- [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. This will create the tarball in a directory called ``upload``.
- [ ] Take the tarball from the upload directory (its name should be ``solidity_x.x.x.tar.gz``, otherwise ``prerelease.txt`` was missing in the step before) and upload the source tarball to the release page.
- [ ] Take the ``solc.exe`` binary from the ``b_win_release`` run of the released commit in circle-ci and add it to the release page as ``solc-windows.exe``.
- [ ] Take the ``solc`` binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``.
- [ ] Take the ``solc`` binary from the ``b_ubu_static`` run of the released commit in circle-ci and add it to the release page as ``solc-static-linux``.
- [ ] Take the ``soljson.js`` binary from the ``b_ems`` run of the released commit in circle-ci and add it to the release page as ``soljson.js``.
### Update [solc-bin](https://github.com/ethereum/solc-bin/)
- [ ] Copy files to solc-bin:
```bash
VERSION=0.8.4
COMMIT="c7e474f2"
SOLC_BIN="/home/me/solc-bin"
chmod +x solc-static-linux solc-macos
cp soljson.js $SOLC_BIN/bin/soljson-v$VERSION+commit.$COMMIT.js
cp solc-static-linux $SOLC_BIN/linux-amd64/solc-linux-amd64-v$VERSION+commit.$COMMIT
cp solc-macos $SOLC_BIN/macosx-amd64/solc-macosx-amd64-v$VERSION+commit.$COMMIT
cp solc-windows.exe $SOLC_BIN/windows-amd64/solc-windows-amd64-v$VERSION+commit.$COMMIT.exe
- [ ] Take the ``github-binaries.tar`` tarball from ``c_release_binaries`` run of the tagged commit in circle-ci and add all binaries from it to the release page.
Make sure it contains four binaries: ``solc-windows.exe``, ``solc-macos``, ``solc-static-linux`` and ``soljson.js``.
- [ ] Take the ``solc-bin-binaries.tar`` tarball from ``c_release_binaries`` run of the tagged commit in circle-ci and add all binaries from it to solc-bin.
- [ ] Run ``./update --reuse-hashes`` in ``solc-bin`` and verify that the script has updated ``list.js``, ``list.txt`` and ``list.json`` files correctly and that symlinks to the new release have been added in ``solc-bin/wasm/`` and ``solc-bin/emscripten-wasm32/``.
- [ ] Create a pull request and merge.
- [ ] Create a pull request in solc-bin and merge.
### Homebrew and MacOS
- [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in https://github.com/Homebrew/homebrew-core/blob/master/Formula/solidity.rb

View File

@ -23,7 +23,9 @@ if(NOT EMSCRIPTEN)
endif()
endif()
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
if(PEDANTIC)
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
endif()
# Prevent the path of the source directory from ending up in the binary via __FILE__ macros.
eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/solidity")
@ -32,39 +34,45 @@ eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/
# if the argument was not wrapped in a call. This happens when moving a local
# variable in a return statement when the variable is the same type as the
# return type or using a move to create a new object from a temporary object.
eth_add_cxx_compiler_flag_if_supported(-Wpessimizing-move)
if(PEDANTIC)
eth_add_cxx_compiler_flag_if_supported(-Wpessimizing-move)
endif()
# -Wredundant-move warns when an implicit move would already be made, so the
# std::move call is not needed, such as when moving a local variable in a return
# that is different from the return type.
eth_add_cxx_compiler_flag_if_supported(-Wredundant-move)
if(PEDANTIC)
eth_add_cxx_compiler_flag_if_supported(-Wredundant-move)
endif()
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
# Enables all the warnings about constructions that some users consider questionable,
# and that are easy to avoid. Also enable some extra warning flags that are not
# enabled by -Wall. Finally, treat at warnings-as-errors, which forces developers
# to fix warnings as they arise, so they don't accumulate "to be fixed later".
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-Werror)
add_compile_options(-pedantic)
add_compile_options(-Wmissing-declarations)
add_compile_options(-Wno-unknown-pragmas)
add_compile_options(-Wimplicit-fallthrough)
add_compile_options(-Wsign-conversion)
add_compile_options(-Wconversion)
if(PEDANTIC)
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-Werror)
add_compile_options(-pedantic)
add_compile_options(-Wmissing-declarations)
add_compile_options(-Wno-unknown-pragmas)
add_compile_options(-Wimplicit-fallthrough)
add_compile_options(-Wsign-conversion)
add_compile_options(-Wconversion)
check_cxx_compiler_flag(-Wextra-semi WEXTRA_SEMI)
if(WEXTRA_SEMI)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wextra-semi>)
check_cxx_compiler_flag(-Wextra-semi WEXTRA_SEMI)
if(WEXTRA_SEMI)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-Wextra-semi>)
endif()
eth_add_cxx_compiler_flag_if_supported(-Wfinal-dtor-non-final-class)
eth_add_cxx_compiler_flag_if_supported(-Wnewline-eof)
eth_add_cxx_compiler_flag_if_supported(-Wsuggest-destructor-override)
eth_add_cxx_compiler_flag_if_supported(-Wduplicated-cond)
eth_add_cxx_compiler_flag_if_supported(-Wduplicate-enum)
eth_add_cxx_compiler_flag_if_supported(-Wlogical-op)
eth_add_cxx_compiler_flag_if_supported(-Wno-unknown-attributes)
endif()
eth_add_cxx_compiler_flag_if_supported(-Wfinal-dtor-non-final-class)
eth_add_cxx_compiler_flag_if_supported(-Wnewline-eof)
eth_add_cxx_compiler_flag_if_supported(-Wsuggest-destructor-override)
eth_add_cxx_compiler_flag_if_supported(-Wduplicated-cond)
eth_add_cxx_compiler_flag_if_supported(-Wduplicate-enum)
eth_add_cxx_compiler_flag_if_supported(-Wlogical-op)
eth_add_cxx_compiler_flag_if_supported(-Wno-unknown-attributes)
# Configuration-specific compiler settings.
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DETH_DEBUG")
@ -158,7 +166,9 @@ elseif (DEFINED MSVC)
add_compile_options(/MP) # enable parallel compilation
add_compile_options(/EHsc) # specify Exception Handling Model in msvc
add_compile_options(/WX) # enable warnings-as-errors
if(PEDANTIC)
add_compile_options(/WX) # enable warnings-as-errors
endif()
add_compile_options(/wd4068) # disable unknown pragma warning (4068)
add_compile_options(/wd4996) # disable unsafe function warning (4996)
add_compile_options(/wd4503) # disable decorated name length exceeded, name was truncated (4503)

View File

@ -20,4 +20,9 @@ macro (eth_policy)
# Allow selecting MSVC runtime library using CMAKE_MSVC_RUNTIME_LIBRARY.
cmake_policy(SET CMP0091 NEW)
endif()
# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
cmake_policy(SET CMP0135 NEW)
endif()
endmacro()

View File

@ -12,9 +12,9 @@ set(RANGE_V3_INCLUDE_DIR "${prefix}/include")
ExternalProject_Add(range-v3-project
PREFIX "${prefix}"
DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads"
DOWNLOAD_NAME range-v3-0.11.0.tar.gz
URL https://github.com/ericniebler/range-v3/archive/0.11.0.tar.gz
URL_HASH SHA256=376376615dbba43d3bef75aa590931431ecb49eb36d07bb726a19f680c75e20c
DOWNLOAD_NAME range-v3-0.12.0.tar.gz
URL https://github.com/ericniebler/range-v3/archive/0.12.0.tar.gz
URL_HASH SHA256=015adb2300a98edfceaf0725beec3337f542af4915cec4d0b89fa0886f4ba9cb
CMAKE_COMMAND ${RANGE_V3_CMAKE_COMMAND}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}

View File

@ -4,15 +4,16 @@ document.addEventListener('DOMContentLoaded', function() {
var mode = (isDay ? "Day" : "Night");
localStorage.setItem("css-mode", mode);
var daysheet = $('link[href="_static/pygments.css"]')[0].sheet;
var url_root = DOCUMENTATION_OPTIONS.URL_ROOT == "./" ? "" : DOCUMENTATION_OPTIONS.URL_ROOT;
var daysheet = $(`link[href="${url_root}_static/pygments.css"]`)[0].sheet;
daysheet.disabled = !isDay;
var nightsheet = $('link[href="_static/css/dark.css"]')[0];
var nightsheet = $(`link[href="${url_root}_static/css/dark.css"]`)[0];
if (!isDay && nightsheet === undefined) {
var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", "_static/css/dark.css");
element.setAttribute("href", `${url_root}_static/css/dark.css`);
document.getElementsByTagName("head")[0].appendChild(element);
return;
}

View File

@ -13,13 +13,13 @@ The Contract Application Binary Interface (ABI) is the standard way to interact
from outside the blockchain and for contract-to-contract interaction. Data is encoded according to its type,
as described in this specification. The encoding is not self describing and thus requires a schema in order to decode.
We assume the interface functions of a contract are strongly typed, known at compilation time and static.
We assume that the interface functions of a contract are strongly typed, known at compilation time and static.
We assume that all contracts will have the interface definitions of any contracts they call available at compile-time.
This specification does not address contracts whose interface is dynamic or otherwise known only at run-time.
.. _abi_function_selector:
.. index:: selector
.. index:: ! selector; of a function
Function Selector
=================
@ -29,7 +29,7 @@ first (left, high-order in big-endian) four bytes of the Keccak-256 hash of the
the function. The signature is defined as the canonical expression of the basic prototype without data
location specifier, i.e.
the function name with the parenthesised list of parameter types. Parameter types are split by a single
comma - no spaces are used.
comma no spaces are used.
.. note::
The return type of a function is not part of this signature. In
@ -133,7 +133,7 @@ The encoding is designed to have the following properties, which are especially
previous version of the ABI, the number of reads scaled linearly with the total number of dynamic
parameters in the worst case.
2. The data of a variable or array element is not interleaved with other data and it is
2. The data of a variable or an array element is not interleaved with other data and it is
relocatable, i.e. it only uses relative "addresses".
@ -252,7 +252,7 @@ Given the contract:
}
Thus for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``69`` and
Thus, for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``69`` and
``true``, we would pass 68 bytes total, which can be broken down into:
- ``0xcdcd77c0``: the Method ID. This is derived as the first 4 bytes of the Keccak hash of
@ -308,7 +308,7 @@ In total:
Use of Dynamic Types
====================
A call to a function with the signature ``f(uint,uint32[],bytes10,bytes)`` with values
A call to a function with the signature ``f(uint256,uint32[],bytes10,bytes)`` with values
``(0x123, [0x456, 0x789], "1234567890", "Hello, world!")`` is encoded in the following way:
We take the first four bytes of ``sha3("f(uint256,uint32[],bytes10,bytes)")``, i.e. ``0x8be65246``.
@ -348,7 +348,7 @@ All together, the encoding is (newline after function selector and each 32-bytes
000000000000000000000000000000000000000000000000000000000000000d
48656c6c6f2c20776f726c642100000000000000000000000000000000000000
Let us apply the same principle to encode the data for a function with a signature ``g(uint[][],string[])``
Let us apply the same principle to encode the data for a function with a signature ``g(uint256[][],string[])``
with values ``([[1, 2], [3]], ["one", "two", "three"])`` but start from the most atomic parts of the encoding:
First we encode the length and data of the first embedded dynamic array ``[1, 2]`` of the first root array ``[[1, 2], [3]]``:
@ -417,7 +417,7 @@ thus ``e = 0x00000000000000000000000000000000000000000000000000000000000000e0``.
Note that the encodings of the embedded elements of the root arrays are not dependent on each other
and have the same encodings for a function with a signature ``g(string[],uint[][])``.
and have the same encodings for a function with a signature ``g(string[],uint256[][])``.
Then we encode the length of the first root array:
@ -503,6 +503,7 @@ efficient search and arbitrary legibility by defining events with two arguments
indexed, one not — intended to hold the same value.
.. _abi_errors:
.. index:: error, selector; of an error
Errors
======
@ -596,7 +597,7 @@ Errors look as follows:
.. note::
There can be multiple errors with the same name and even with identical signature
in the JSON array, for example if the errors originate from different
in the JSON array; for example, if the errors originate from different
files in the smart contract or are referenced from another smart contract.
For the ABI, only the name of the error itself is relevant and not where it is
defined.
@ -645,7 +646,7 @@ would result in the JSON:
Handling tuple types
--------------------
Despite that names are intentionally not part of the ABI encoding they do make a lot of sense to be included
Despite the fact that names are intentionally not part of the ABI encoding, they do make a lot of sense to be included
in the JSON to enable displaying it to the end user. The structure is nested in the following way:
An object with members ``name``, ``type`` and potentially ``components`` describes a typed variable.
@ -653,7 +654,7 @@ The canonical type is determined until a tuple type is reached and the string de
to that point is stored in ``type`` prefix with the word ``tuple``, i.e. it will be ``tuple`` followed by
a sequence of ``[]`` and ``[k]`` with
integers ``k``. The components of the tuple are then stored in the member ``components``,
which is of array type and has the same structure as the top-level object except that
which is of an array type and has the same structure as the top-level object except that
``indexed`` is not allowed there.
As an example, the code
@ -737,10 +738,10 @@ Strict Encoding Mode
====================
Strict encoding mode is the mode that leads to exactly the same encoding as defined in the formal specification above.
This means offsets have to be as small as possible while still not creating overlaps in the data areas and thus no gaps are
This means that offsets have to be as small as possible while still not creating overlaps in the data areas, and thus no gaps are
allowed.
Usually, ABI decoders are written in a straightforward way just following offset pointers, but some decoders
Usually, ABI decoders are written in a straightforward way by just following offset pointers, but some decoders
might enforce strict mode. The Solidity ABI decoder currently does not enforce strict mode, but the encoder
always creates data in strict mode.
@ -776,7 +777,7 @@ More specifically:
encoding of its elements **with** padding.
- Dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded
without their length field.
- The encoding of ``string`` or ``bytes`` does not apply padding at the end
- The encoding of ``string`` or ``bytes`` does not apply padding at the end,
unless it is part of an array or struct (then it is padded to a multiple of
32 bytes).
@ -804,7 +805,7 @@ Encoding of Indexed Event Parameters
====================================
Indexed event parameters that are not value types, i.e. arrays and structs are not
stored directly but instead a keccak256-hash of an encoding is stored. This encoding
stored directly but instead a Keccak-256 hash of an encoding is stored. This encoding
is defined as follows:
- the encoding of a ``bytes`` and ``string`` value is just the string contents

View File

@ -8,7 +8,7 @@ Inline Assembly
You can interleave Solidity statements with inline assembly in a language close
to the one of the Ethereum virtual machine. This gives you more fine-grained control,
to the one of the Ethereum Virtual Machine. This gives you more fine-grained control,
which is especially useful when you are enhancing the language by writing libraries.
The language used for inline assembly in Solidity is called :ref:`Yul <yul>`
@ -108,7 +108,7 @@ efficient code, for example:
for
{ let end := add(dataElementLocation, mul(len, 0x20)) }
lt(dataElementLocation, end)
{ data := add(dataElementLocation, 0x20) }
{ dataElementLocation := add(dataElementLocation, 0x20) }
{
sum := add(sum, mload(dataElementLocation))
}
@ -116,7 +116,7 @@ efficient code, for example:
}
}
.. index:: selector; of a function
Access to External Variables, Functions and Libraries
-----------------------------------------------------
@ -126,15 +126,15 @@ You can access Solidity variables and other identifiers by using their name.
Local variables of value type are directly usable in inline assembly.
They can both be read and assigned to.
Local variables that refer to memory evaluate to the address of the variable in memory not the value itself.
Local variables that refer to memory evaluate to the address of the variable in memory, not the value itself.
Such variables can also be assigned to, but note that an assignment will only change the pointer and not the data
and that it is your responsibility to respect Solidity's memory management.
See :ref:`Conventions in Solidity <conventions-in-solidity>`.
Similarly, local variables that refer to statically-sized calldata arrays or calldata structs
evaluate to the address of the variable in calldata, not the value itself.
The variable can also be assigned a new offset, but note that no validation to ensure that
the variable will not point beyond ``calldatasize()`` is performed.
The variable can also be assigned a new offset, but note that no validation is performed to ensure that
the variable will not point beyond ``calldatasize()``.
For external function pointers the address and the function selector can be
accessed using ``x.address`` and ``x.selector``.
@ -205,7 +205,7 @@ Local Solidity variables are available for assignments, for example:
``assembly { signextend(<num_bytes_of_x_minus_one>, x) }``
Since Solidity 0.6.0 the name of a inline assembly variable may not
Since Solidity 0.6.0, the name of a inline assembly variable may not
shadow any declaration visible in the scope of the inline assembly block
(including variable, contract and function declarations).
@ -253,7 +253,7 @@ starting from where this pointer points at and update it.
There is no guarantee that the memory has not been used before and thus
you cannot assume that its contents are zero bytes.
There is no built-in mechanism to release or free allocated memory.
Here is an assembly snippet you can use for allocating memory that follows the process outlined above
Here is an assembly snippet you can use for allocating memory that follows the process outlined above:
.. code-block:: yul
@ -276,7 +276,7 @@ first slot of the array and followed by the array elements.
.. warning::
Statically-sized memory arrays do not have a length field, but it might be added later
to allow better convertibility between statically- and dynamically-sized arrays, so
to allow better convertibility between statically and dynamically-sized arrays; so,
do not rely on this.
Memory Safety
@ -289,8 +289,8 @@ perform additional memory optimizations, if it can rely on certain assumptions a
While we recommend to always respect Solidity's memory model, inline assembly allows you to use memory
in an incompatible way. Therefore, moving stack variables to memory and additional memory optimizations are,
by default, disabled in the presence of any inline assembly block that contains a memory operation or assigns
to solidity variables in memory.
by default, globally disabled in the presence of any inline assembly block that contains a memory operation
or assigns to Solidity variables in memory.
However, you can specifically annotate an assembly block to indicate that it in fact respects Solidity's memory
model as follows:
@ -346,7 +346,7 @@ If the memory operations use a length of zero, it is also fine to just use any o
}
Note that not only memory operations in inline assembly itself can be memory-unsafe, but also assignments to
solidity variables of reference type in memory. For example the following is not memory-safe:
Solidity variables of reference type in memory. For example the following is not memory-safe:
.. code-block:: solidity
@ -356,7 +356,7 @@ solidity variables of reference type in memory. For example the following is not
}
x[0x20] = 0x42;
Inline assembly that neither involves any operations that access memory nor assigns to any solidity variables
Inline assembly that neither involves any operations that access memory nor assigns to any Solidity variables
in memory is automatically considered memory-safe and does not need to be annotated.
.. warning::
@ -365,7 +365,7 @@ in memory is automatically considered memory-safe and does not need to be annota
undefined behaviour that cannot easily be discovered by testing.
In case you are developing a library that is meant to be compatible across multiple versions
of solidity, you can use a special comment to annotate an assembly block as memory-safe:
of Solidity, you can use a special comment to annotate an assembly block as memory-safe:
.. code-block:: solidity
@ -374,5 +374,5 @@ of solidity, you can use a special comment to annotate an assembly block as memo
...
}
Note that we will disallow the annotation via comment in a future breaking release, so if you are not concerned with
Note that we will disallow the annotation via comment in a future breaking release; so, if you are not concerned with
backwards-compatibility with older compiler versions, prefer using the dialect string.

View File

@ -1,4 +1,40 @@
[
{
"uid": "SOL-2022-6",
"name": "AbiReencodingHeadOverflowWithStaticArrayCleanup",
"summary": "ABI-encoding a tuple with a statically-sized calldata array in the last component would corrupt 32 leading bytes of its first dynamically encoded component.",
"description": "When ABI-encoding a statically-sized calldata array, the compiler always pads the data area to a multiple of 32-bytes and ensures that the padding bytes are zeroed. In some cases, this cleanup used to be performed by always writing exactly 32 bytes, regardless of how many needed to be zeroed. This was done with the assumption that the data that would eventually occupy the area past the end of the array had not yet been written, because the encoder processes tuple components in the order they were given. While this assumption is mostly true, there is an important corner case: dynamically encoded tuple components are stored separately from the statically-sized ones in an area called the *tail* of the encoding and the tail immediately follows the *head*, which is where the statically-sized components are placed. The aforementioned cleanup, if performed for the last component of the head would cross into the tail and overwrite up to 32 bytes of the first component stored there with zeros. The only array type for which the cleanup could actually result in an overwrite were arrays with ``uint256`` or ``bytes32`` as the base element type and in this case the size of the corrupted area was always exactly 32 bytes. The problem affected tuples at any nesting level. This included also structs, which are encoded as tuples in the ABI. Note also that lists of parameters and return values of functions, events and errors are encoded as tuples.",
"link": "https://blog.soliditylang.org/2022/08/08/calldata-tuple-reencoding-head-overflow-bug/",
"introduced": "0.5.8",
"fixed": "0.8.16",
"severity": "medium",
"conditions": {
"ABIEncoderV2": true
}
},
{
"uid": "SOL-2022-5",
"name": "DirtyBytesArrayToStorage",
"summary": "Copying ``bytes`` arrays from memory or calldata to storage may result in dirty storage values.",
"description": "Copying ``bytes`` arrays from memory or calldata to storage is done in chunks of 32 bytes even if the length is not a multiple of 32. Thereby, extra bytes past the end of the array may be copied from calldata or memory to storage. These dirty bytes may then become observable after a ``.push()`` without arguments to the bytes array in storage, i.e. such a push will not result in a zero value at the end of the array as expected. This bug only affects the legacy code generation pipeline, the new code generation pipeline via IR is not affected.",
"link": "https://blog.soliditylang.org/2022/06/15/dirty-bytes-array-to-storage-bug/",
"introduced": "0.0.1",
"fixed": "0.8.15",
"severity": "low"
},
{
"uid": "SOL-2022-4",
"name": "InlineAssemblyMemorySideEffects",
"summary": "The Yul optimizer may incorrectly remove memory writes from inline assembly blocks, that do not access solidity variables.",
"description": "The Yul optimizer considers all memory writes in the outermost Yul block that are never read from as unused and removes them. This is valid when that Yul block is the entire Yul program, which is always the case for the Yul code generated by the new via-IR pipeline. Inline assembly blocks are never optimized in isolation when using that pipeline. Instead they are optimized as a part of the whole Yul input. However, the legacy code generation pipeline (which is still the default) runs the Yul optimizer individually on an inline assembly block if the block does not refer to any local variables defined in the surrounding Solidity code. Consequently, memory writes in such inline assembly blocks are removed as well, if the written memory is never read from in the same assembly block, even if the written memory is accessed later, for example by a subsequent inline assembly block.",
"link": "https://blog.soliditylang.org/2022/06/15/inline-assembly-memory-side-effects-bug/",
"introduced": "0.8.13",
"fixed": "0.8.15",
"severity": "medium",
"conditions": {
"yulOptimizer": true
}
},
{
"uid": "SOL-2022-3",
"name": "DataLocationChangeInInternalOverride",
@ -8,7 +44,6 @@
"introduced": "0.6.9",
"fixed": "0.8.14",
"severity": "very low"
},
{
"uid": "SOL-2022-2",

View File

@ -1,6 +1,7 @@
{
"0.1.0": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -22,6 +23,7 @@
},
"0.1.1": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -43,6 +45,7 @@
},
"0.1.2": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -64,6 +67,7 @@
},
"0.1.3": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -85,6 +89,7 @@
},
"0.1.4": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -107,6 +112,7 @@
},
"0.1.5": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -129,6 +135,7 @@
},
"0.1.6": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -153,6 +160,7 @@
},
"0.1.7": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -177,6 +185,7 @@
},
"0.2.0": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -202,6 +211,7 @@
},
"0.2.1": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -227,6 +237,7 @@
},
"0.2.2": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -252,6 +263,7 @@
},
"0.3.0": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -279,6 +291,7 @@
},
"0.3.1": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -305,6 +318,7 @@
},
"0.3.2": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -331,6 +345,7 @@
},
"0.3.3": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -356,6 +371,7 @@
},
"0.3.4": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -381,6 +397,7 @@
},
"0.3.5": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -406,6 +423,7 @@
},
"0.3.6": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -429,6 +447,7 @@
},
"0.4.0": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -452,6 +471,7 @@
},
"0.4.1": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -475,6 +495,7 @@
},
"0.4.10": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -497,6 +518,7 @@
},
"0.4.11": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -518,6 +540,7 @@
},
"0.4.12": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -538,6 +561,7 @@
},
"0.4.13": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -558,6 +582,7 @@
},
"0.4.14": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -577,6 +602,7 @@
},
"0.4.15": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -595,6 +621,7 @@
},
"0.4.16": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -616,6 +643,7 @@
},
"0.4.17": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -638,6 +666,7 @@
},
"0.4.18": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -659,6 +688,7 @@
},
"0.4.19": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -681,6 +711,7 @@
},
"0.4.2": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -703,6 +734,7 @@
},
"0.4.20": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -725,6 +757,7 @@
},
"0.4.21": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -747,6 +780,7 @@
},
"0.4.22": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -769,6 +803,7 @@
},
"0.4.23": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -790,6 +825,7 @@
},
"0.4.24": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -811,6 +847,7 @@
},
"0.4.25": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -830,6 +867,7 @@
},
"0.4.26": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -846,6 +884,7 @@
},
"0.4.3": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -867,6 +906,7 @@
},
"0.4.4": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -887,6 +927,7 @@
},
"0.4.5": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -910,6 +951,7 @@
},
"0.4.6": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -932,6 +974,7 @@
},
"0.4.7": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -954,6 +997,7 @@
},
"0.4.8": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -976,6 +1020,7 @@
},
"0.4.9": {
"bugs": [
"DirtyBytesArrayToStorage",
"KeccakCaching",
"EmptyByteArrayCopy",
"DynamicArrayCleanup",
@ -998,6 +1043,7 @@
},
"0.5.0": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1017,6 +1063,7 @@
},
"0.5.1": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1036,6 +1083,8 @@
},
"0.5.10": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1052,6 +1101,8 @@
},
"0.5.11": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1067,6 +1118,8 @@
},
"0.5.12": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1082,6 +1135,8 @@
},
"0.5.13": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1097,6 +1152,8 @@
},
"0.5.14": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1114,6 +1171,8 @@
},
"0.5.15": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1130,6 +1189,8 @@
},
"0.5.16": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1145,6 +1206,8 @@
},
"0.5.17": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1159,6 +1222,7 @@
},
"0.5.2": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1178,6 +1242,7 @@
},
"0.5.3": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1197,6 +1262,7 @@
},
"0.5.4": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1216,6 +1282,7 @@
},
"0.5.5": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1237,6 +1304,7 @@
},
"0.5.6": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1258,6 +1326,7 @@
},
"0.5.7": {
"bugs": [
"DirtyBytesArrayToStorage",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
"EmptyByteArrayCopy",
@ -1277,6 +1346,8 @@
},
"0.5.8": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1296,6 +1367,8 @@
},
"0.5.9": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1314,6 +1387,8 @@
},
"0.6.0": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1330,6 +1405,8 @@
},
"0.6.1": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1345,6 +1422,8 @@
},
"0.6.10": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1357,6 +1436,8 @@
},
"0.6.11": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1369,6 +1450,8 @@
},
"0.6.12": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1381,6 +1464,8 @@
},
"0.6.2": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1396,6 +1481,8 @@
},
"0.6.3": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1411,6 +1498,8 @@
},
"0.6.4": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"ABIDecodeTwoDimensionalArrayMemory",
"KeccakCaching",
@ -1426,6 +1515,8 @@
},
"0.6.5": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
"ABIDecodeTwoDimensionalArrayMemory",
@ -1441,6 +1532,8 @@
},
"0.6.6": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
"ABIDecodeTwoDimensionalArrayMemory",
@ -1455,6 +1548,8 @@
},
"0.6.7": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
"ABIDecodeTwoDimensionalArrayMemory",
@ -1469,6 +1564,8 @@
},
"0.6.8": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
"ABIDecodeTwoDimensionalArrayMemory",
@ -1480,6 +1577,8 @@
},
"0.6.9": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1493,6 +1592,8 @@
},
"0.7.0": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1505,6 +1606,8 @@
},
"0.7.1": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1518,6 +1621,8 @@
},
"0.7.2": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1530,6 +1635,8 @@
},
"0.7.3": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1541,6 +1648,8 @@
},
"0.7.4": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1551,6 +1660,8 @@
},
"0.7.5": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1561,6 +1672,8 @@
},
"0.7.6": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1571,6 +1684,8 @@
},
"0.8.0": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1581,6 +1696,8 @@
},
"0.8.1": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1591,6 +1708,8 @@
},
"0.8.10": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation"
],
@ -1598,6 +1717,8 @@
},
"0.8.11": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"AbiEncodeCallLiteralAsFixedBytesBug"
@ -1606,6 +1727,8 @@
},
"0.8.12": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"AbiEncodeCallLiteralAsFixedBytesBug"
@ -1614,17 +1737,36 @@
},
"0.8.13": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"InlineAssemblyMemorySideEffects",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation"
],
"released": "2022-03-16"
},
"0.8.14": {
"bugs": [],
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"InlineAssemblyMemorySideEffects"
],
"released": "2022-05-17"
},
"0.8.15": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup"
],
"released": "2022-06-15"
},
"0.8.16": {
"bugs": [],
"released": "2022-08-08"
},
"0.8.2": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1635,6 +1777,8 @@
},
"0.8.3": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables",
@ -1644,6 +1788,8 @@
},
"0.8.4": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables"
@ -1652,6 +1798,8 @@
},
"0.8.5": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables"
@ -1660,6 +1808,8 @@
},
"0.8.6": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables"
@ -1668,6 +1818,8 @@
},
"0.8.7": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"SignedImmutables"
@ -1676,6 +1828,8 @@
},
"0.8.8": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation",
"UserDefinedValueTypesBug",
@ -1685,6 +1839,8 @@
},
"0.8.9": {
"bugs": [
"AbiReencodingHeadOverflowWithStaticArrayCleanup",
"DirtyBytesArrayToStorage",
"DataLocationChangeInInternalOverride",
"NestedCallataArrayAbiReencodingSizeValidation"
],

View File

@ -2,70 +2,11 @@
Cheatsheet
**********
.. index:: precedence
.. _order:
.. index:: operator; precedence
Order of Precedence of Operators
================================
The following is the order of precedence for operators, listed in order of evaluation.
+------------+-------------------------------------+--------------------------------------------+
| Precedence | Description | Operator |
+============+=====================================+============================================+
| *1* | Postfix increment and decrement | ``++``, ``--`` |
+ +-------------------------------------+--------------------------------------------+
| | New expression | ``new <typename>`` |
+ +-------------------------------------+--------------------------------------------+
| | Array subscripting | ``<array>[<index>]`` |
+ +-------------------------------------+--------------------------------------------+
| | Member access | ``<object>.<member>`` |
+ +-------------------------------------+--------------------------------------------+
| | Function-like call | ``<func>(<args...>)`` |
+ +-------------------------------------+--------------------------------------------+
| | Parentheses | ``(<statement>)`` |
+------------+-------------------------------------+--------------------------------------------+
| *2* | Prefix increment and decrement | ``++``, ``--`` |
+ +-------------------------------------+--------------------------------------------+
| | Unary minus | ``-`` |
+ +-------------------------------------+--------------------------------------------+
| | Unary operations | ``delete`` |
+ +-------------------------------------+--------------------------------------------+
| | Logical NOT | ``!`` |
+ +-------------------------------------+--------------------------------------------+
| | Bitwise NOT | ``~`` |
+------------+-------------------------------------+--------------------------------------------+
| *3* | Exponentiation | ``**`` |
+------------+-------------------------------------+--------------------------------------------+
| *4* | Multiplication, division and modulo | ``*``, ``/``, ``%`` |
+------------+-------------------------------------+--------------------------------------------+
| *5* | Addition and subtraction | ``+``, ``-`` |
+------------+-------------------------------------+--------------------------------------------+
| *6* | Bitwise shift operators | ``<<``, ``>>`` |
+------------+-------------------------------------+--------------------------------------------+
| *7* | Bitwise AND | ``&`` |
+------------+-------------------------------------+--------------------------------------------+
| *8* | Bitwise XOR | ``^`` |
+------------+-------------------------------------+--------------------------------------------+
| *9* | Bitwise OR | ``|`` |
+------------+-------------------------------------+--------------------------------------------+
| *10* | Inequality operators | ``<``, ``>``, ``<=``, ``>=`` |
+------------+-------------------------------------+--------------------------------------------+
| *11* | Equality operators | ``==``, ``!=`` |
+------------+-------------------------------------+--------------------------------------------+
| *12* | Logical AND | ``&&`` |
+------------+-------------------------------------+--------------------------------------------+
| *13* | Logical OR | ``||`` |
+------------+-------------------------------------+--------------------------------------------+
| *14* | Ternary operator | ``<conditional> ? <if-true> : <if-false>`` |
+ +-------------------------------------+--------------------------------------------+
| | Assignment operators | ``=``, ``|=``, ``^=``, ``&=``, ``<<=``, |
| | | ``>>=``, ``+=``, ``-=``, ``*=``, ``/=``, |
| | | ``%=`` |
+------------+-------------------------------------+--------------------------------------------+
| *15* | Comma operator | ``,`` |
+------------+-------------------------------------+--------------------------------------------+
.. include:: types/operator-precedence-table.rst
.. index:: assert, block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin, revert, require, keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, this, super, selfdestruct, balance, codehash, send
@ -135,35 +76,6 @@ Global Variables
- ``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::
When contracts are evaluated off-chain rather than in context of a transaction included in a
block, you should not assume that ``block.*`` and ``tx.*`` refer to values from any specific
block or transaction. These values are provided by the EVM implementation that executes the
contract and can be arbitrary.
.. note::
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,
unless you know what you are doing.
Both the timestamp and the block hash can be influenced by miners to some degree.
Bad actors in the mining community can for example run a casino payout function on a chosen hash
and just retry a different hash if they did not receive any money.
The current block timestamp must be strictly larger than the timestamp of the last block,
but the only guarantee is that it will be somewhere between the timestamps of two
consecutive blocks in the canonical chain.
.. note::
The block hashes are not available for all blocks for scalability reasons.
You can only access the hashes of the most recent 256 blocks, all other
values will be zero.
.. note::
In version 0.5.0, the following aliases were removed: ``suicide`` as alias for ``selfdestruct``,
``msg.gas`` as alias for ``gasleft``, ``block.blockhash`` as alias for ``blockhash`` and
``sha3`` as alias for ``keccak256``.
.. note::
In version 0.7.0, the alias ``now`` (for ``block.timestamp``) was removed.
.. index:: visibility, public, private, external, internal
@ -200,13 +112,3 @@ Modifiers
- ``override``: States that this function, modifier or public state variable changes
the behaviour of a function or modifier in a base contract.
Reserved Keywords
=================
These keywords are reserved in Solidity. They might become part of the syntax in the future:
``after``, ``alias``, ``apply``, ``auto``, ``byte``, ``case``, ``copyof``, ``default``,
``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``,
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``,
``var``.

View File

@ -81,7 +81,7 @@ construction time.
The contract creation code generated by the compiler will modify the
contract's runtime code before it is returned by replacing all references
to immutables by the values assigned to the them. This is important if
to immutables with the values assigned to them. This is important if
you are comparing the
runtime code generated by the compiler with the one actually stored in the
blockchain.
@ -95,4 +95,4 @@ blockchain.
This is a safeguard against different interpretations about the order
of state variable initialization and constructor execution, especially
with regards to inheritance.
with regards to inheritance.

View File

@ -1,5 +1,4 @@
.. index:: ! error, revert
.. index:: ! error, revert, ! selector; of an error
.. _errors:
*******************************
@ -80,3 +79,8 @@ of the built-in type ``Panic(uint256)``.
by default. This means that an inner call
can "forge" revert data that looks like it could have come from the
contract that called it.
Members of Errors
=================
- ``error.selector``: A ``bytes4`` value containing the error selector.

View File

@ -1,4 +1,4 @@
.. index:: ! event
.. index:: ! event, ! event; anonymous, ! event; indexed, ! event; topic
.. _events:
@ -73,6 +73,18 @@ four indexed arguments rather than three.
In particular, it is possible to "fake" the signature of another event
using an anonymous event.
.. index:: ! selector; of an event
Members of Events
=================
- ``event.selector``: For non-anonymous events, this is a ``bytes32`` value
containing the ``keccak256`` hash of the event signature, as used in the default topic.
Example
=======
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
@ -137,7 +149,7 @@ The output of the above looks like the following (trimmed):
}
Additional Resources for Understanding Events
==============================================
=============================================
- `Javascript documentation <https://github.com/ethereum/web3.js/blob/1.x/docs/web3-eth-contract.rst#events>`_
- `Example usage of events <https://github.com/ethchange/smart-exchange/blob/master/lib/contracts/SmartExchange.sol>`_

View File

@ -111,6 +111,12 @@ whitespace-separated list and are evaluated in the order presented.
Modifiers cannot implicitly access or change the arguments and return values of functions they modify.
Their values can only be passed to them explicitly at the point of invocation.
In function modifiers, it is necessary to specify when you want the function to which the modifier is
applied to be run. The placeholder statement (denoted by a single underscore character ``_``) is used to
denote where the body of the function being modified should be inserted. Note that the
placeholder operator is different from using underscores as leading or trailing characters in variable
names, which is a stylistic choice.
Explicit returns from a modifier or function body only leave the current
modifier or function body. Return variables are assigned and
control flow continues after the ``_`` in the preceding modifier.

View File

@ -35,10 +35,10 @@ that call them, similar to internal library functions.
.. note::
Functions defined outside a contract are still always executed
in the context of a contract. They still have access to the variable ``this``,
can call other contracts, send them Ether and destroy the contract that called them,
in the context of a contract.
They still can call other contracts, send them Ether and destroy the contract that called them,
among other things. The main difference to functions defined inside a contract
is that free functions do not have direct access to storage variables and functions
is that free functions do not have direct access to the variable ``this``, storage variables and functions
not in their scope.
.. _function-parameters-return-variables:
@ -72,16 +72,6 @@ with two integers, you would use something like the following:
Function parameters can be used as any other local variable and they can also be assigned to.
.. note::
An :ref:`external function<external-function-calls>` cannot accept a
multi-dimensional array as an input
parameter. This functionality is possible if you enable the ABI coder v2
by adding ``pragma abicoder v2;`` to your source file.
An :ref:`internal function<external-function-calls>` can accept a
multi-dimensional array without enabling the feature.
.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct
Return Variables
@ -139,12 +129,16 @@ If you use an early ``return`` to leave a function that has return variables,
you must provide return values together with the return statement.
.. note::
You cannot return some types from non-internal functions, notably
multi-dimensional dynamic arrays and structs. If you enable the
ABI coder v2 by adding ``pragma abicoder v2;``
to your source file then more types are available, but
``mapping`` types are still limited to inside a single contract and you
cannot transfer them.
You cannot return some types from non-internal functions.
This includes the types listed below and any composite types that recursively contain them:
- mappings,
- internal function types,
- reference types with location set to ``storage``,
- multi-dimensional arrays (applies only to :ref:`ABI coder v1 <abi_coder>`),
- structs (applies only to :ref:`ABI coder v1 <abi_coder>`).
This restriction does not apply to library functions because of their different :ref:`internal ABI <library-selectors>`.
.. _multi-return:

View File

@ -215,7 +215,7 @@ In comparison to contracts, libraries are restricted in the following ways:
(These might be lifted at a later point.)
.. _library-selectors:
.. index:: selector
.. index:: ! selector; of a library function
Function Signatures and Selectors in Libraries
==============================================

View File

@ -87,7 +87,6 @@ instead of library functions.
}
function contains(Data storage self, uint value)
public
view
returns (bool)
{

View File

@ -2,7 +2,7 @@
Contributing
############
Help is always welcome and there are plenty of options how you can contribute to Solidity.
Help is always welcome and there are plenty of options to contribute to Solidity.
In particular, we appreciate support in the following areas:
@ -22,7 +22,7 @@ To get started, you can try :ref:`building-from-source` in order to familiarize
yourself with the components of Solidity and the build process. Also, it may be
useful to become well-versed at writing smart-contracts in Solidity.
Please note that this project is released with a `Contributor Code of Conduct <https://raw.githubusercontent.com/ethereum/solidity/develop/CODE_OF_CONDUCT.md>`_. By participating in this project - in the issues, pull requests, or Gitter channels - you agree to abide by its terms.
Please note that this project is released with a `Contributor Code of Conduct <https://raw.githubusercontent.com/ethereum/solidity/develop/CODE_OF_CONDUCT.md>`_. By participating in this project — in the issues, pull requests, or Gitter channels — you agree to abide by its terms.
Team Calls
==========
@ -30,8 +30,7 @@ Team Calls
If you have issues or pull requests to discuss, or are interested in hearing what
the team and contributors are working on, you can join our public team calls:
- Mondays at 3pm CET/CEST.
- Wednesdays at 2pm CET/CEST.
- Mondays and Wednesdays at 3pm CET/CEST.
Both calls take place on `Jitsi <https://meet.ethereum.org/solidity>`_.
@ -49,7 +48,7 @@ reporting issues, please mention the following details:
* Actual vs. expected behaviour.
Reducing the source code that caused the issue to a bare minimum is always
very helpful and sometimes even clarifies a misunderstanding.
very helpful, and sometimes even clarifies a misunderstanding.
Workflow for Pull Requests
==========================
@ -67,7 +66,7 @@ Additionally, if you are writing a new feature, please ensure you add appropriat
test cases under ``test/`` (see below).
However, if you are making a larger change, please consult with the `Solidity Development Gitter channel
<https://gitter.im/ethereum/solidity-dev>`_ (different from the one mentioned above, this one is
<https://gitter.im/ethereum/solidity-dev>`_ (different from the one mentioned above this one is
focused on compiler and language development instead of language usage) first.
New features and bugfixes should be added to the ``Changelog.md`` file: please
@ -91,10 +90,10 @@ dependencies (`evmone <https://github.com/ethereum/evmone/releases>`_,
`libz3 <https://github.com/Z3Prover/z3>`_, and
`libhera <https://github.com/ewasm/hera>`_).
On macOS some of the testing scripts expect GNU coreutils to be installed.
On macOS systems, some of the testing scripts expect GNU coreutils to be installed.
This can be easiest accomplished using Homebrew: ``brew install coreutils``.
On Windows systems make sure that you have a privilege to create symlinks,
On Windows systems, make sure that you have a privilege to create symlinks,
otherwise several tests may fail.
Administrators should have that privilege, but you may also
`grant it to other users <https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links#policy-management>`_
@ -117,7 +116,7 @@ The test system automatically tries to discover the location of
the `evmone <https://github.com/ethereum/evmone/releases>`_ for running the semantic tests.
The ``evmone`` library must be located in the ``deps`` or ``deps/lib`` directory relative to the
current working directory, to its parent or its parent's parent. Alternatively an explicit location
current working directory, to its parent or its parent's parent. Alternatively, an explicit location
for the ``evmone`` shared object can be specified via the ``ETH_EVMONE`` environment variable.
``evmone`` is needed mainly for running semantic and gas tests.
@ -245,7 +244,7 @@ It offers several options for failing tests:
- ``skip``: Skips the execution of this particular test.
- ``quit``: Quits ``isoltest``.
All of these options apply to the current contract, expect ``quit`` which stops the entire testing process.
All of these options apply to the current contract, except ``quit`` which stops the entire testing process.
Automatically updating the test above changes it to
@ -292,7 +291,7 @@ Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compile
cmake .. -DCMAKE_C_COMPILER=path/to/afl-gcc -DCMAKE_CXX_COMPILER=path/to/afl-g++
make solfuzzer
At this stage you should be able to see a message similar to the following:
At this stage, you should be able to see a message similar to the following:
.. code-block:: text
@ -481,7 +480,7 @@ needed for documentation and checks for any problems such as broken links or syn
Solidity Language Design
========================
To actively get involved in the language design process and share your ideas concerning the future of Solidity,
To actively get involved in the language design process and to share your ideas concerning the future of Solidity,
please join the `Solidity forum <https://forum.soliditylang.org/>`_.
The Solidity forum serves as the place to propose and discuss new language features and their implementation in
@ -500,7 +499,7 @@ If you want to know where the team is standing in terms or implementing new feat
Issues in the design backlog need further specification and will either be discussed in a language design call or in a regular team call. You can
see the upcoming changes for the next breaking release by changing from the default branch (`develop`) to the `breaking branch <https://github.com/ethereum/solidity/tree/breaking>`_.
For ad-hoc cases and questions you can reach out to us via the `Solidity-dev Gitter channel <https://gitter.im/ethereum/solidity-dev>`_, a
For ad-hoc cases and questions, you can reach out to us via the `Solidity-dev Gitter channel <https://gitter.im/ethereum/solidity-dev>`_ a
dedicated chatroom for conversations around the Solidity compiler and language development.
We are happy to hear your thoughts on how we can improve the language design process to be even more collaborative and transparent.

View File

@ -150,8 +150,8 @@ throws an exception or goes out of gas.
use ``f.value(x).gas(g)()``. This was deprecated in Solidity 0.6.2 and is no
longer possible since Solidity 0.7.0.
Named Calls and Anonymous Function Parameters
---------------------------------------------
Function Calls with Named Parameters
------------------------------------
Function call arguments can be given by name, in any order,
if they are enclosed in ``{ }`` as can be seen in the following
@ -176,11 +176,13 @@ parameters from the function declaration, but can be in arbitrary order.
}
Omitted Function Parameter Names
--------------------------------
Omitted Names in Function Definitions
-------------------------------------
The names of unused parameters (especially return parameters) can be omitted.
Those parameters will still be present on the stack, but they are inaccessible.
The names of parameters and return values in the function declaration can be omitted.
Those items with omitted names will still be present on the stack, but they are
inaccessible by name. An omitted return value name
can still return a value to the caller by use of the ``return`` statement.
.. code-block:: solidity

View File

@ -7,7 +7,7 @@ Modular Contracts
A modular approach to building your contracts helps you reduce the complexity
and improve the readability which will help to identify bugs and vulnerabilities
during development and code review.
If you specify and control the behaviour or each module in isolation, the
If you specify and control the behaviour of each module in isolation, the
interactions you have to consider are only those between the module specifications
and not every other moving part of the contract.
In the example below, the contract uses the ``move`` method

View File

@ -129,14 +129,16 @@ of votes.
require(to != msg.sender, "Found loop in delegation.");
}
// Since `sender` is a reference, this
// modifies `voters[msg.sender].voted`
Voter storage delegate_ = voters[to];
// Voters cannot delegate to accounts that cannot vote.
require(delegate_.weight >= 1);
// Since `sender` is a reference, this
// modifies `voters[msg.sender]`.
sender.voted = true;
sender.delegate = to;
if (delegate_.voted) {
// If the delegate already voted,
// directly add to the number of votes
@ -191,5 +193,8 @@ of votes.
Possible Improvements
=====================
Currently, many transactions are needed to assign the rights
to vote to all participants. Can you think of a better way?
Currently, many transactions are needed to
assign the rights to vote to all participants.
Moreover, if two or more proposals have the same
number of votes, ``winningProposal()`` is not able
to register a tie. Can you think of a way to fix these issues?

View File

@ -9,10 +9,9 @@ ReservedKeywords:
| 'partial' | 'promise' | 'reference' | 'relocatable' | 'sealed' | 'sizeof' | 'static'
| 'supports' | 'switch' | 'typedef' | 'typeof' | 'var';
Pragma: 'pragma' -> pushMode(PragmaMode);
Abstract: 'abstract';
Anonymous: 'anonymous';
Address: 'address';
Anonymous: 'anonymous';
As: 'as';
Assembly: 'assembly' -> pushMode(AssemblyBlockMode);
Bool: 'bool';
@ -30,13 +29,11 @@ Else: 'else';
Emit: 'emit';
Enum: 'enum';
Error: 'error'; // not a real keyword
Revert: 'revert'; // not a real keyword
Event: 'event';
External: 'external';
Fallback: 'fallback';
False: 'false';
Fixed: 'fixed' | ('fixed' [1-9][0-9]* 'x' [1-9][0-9]*);
From: 'from'; // not a real keyword
/**
* Bytes types of fixed length.
*/
@ -46,6 +43,7 @@ FixedBytes:
'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' |
'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32';
For: 'for';
From: 'from'; // not a real keyword
Function: 'function';
Global: 'global'; // not a real keyword
Hex: 'hex';
@ -67,12 +65,14 @@ New: 'new';
NumberUnit: 'wei' | 'gwei' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years';
Override: 'override';
Payable: 'payable';
Pragma: 'pragma' -> pushMode(PragmaMode);
Private: 'private';
Public: 'public';
Pure: 'pure';
Receive: 'receive';
Return: 'return';
Returns: 'returns';
Revert: 'revert'; // not a real keyword
/**
* Sized signed integer types.
* int is an alias of int256.

View File

@ -354,6 +354,17 @@ The following are dependencies for all builds of Solidity:
If you do this, however, please remember to pass the ``--no-smt`` option to ``scripts/tests.sh``
to skip the SMT tests.
.. note::
By default the build is performed in *pedantic mode*, which enables extra warnings and tells the
compiler to treat all warnings as errors.
This forces developers to fix warnings as they arise, so they do not accumulate "to be fixed later".
If you are only interested in creating a release build and do not intend to modify the source code
to deal with such warnings, you can pass ``-DPEDANTIC=OFF`` option to CMake to disable this mode.
Doing this is not recommended for general use but may be necessary when using a toolchain we are
not testing with or trying to build an older version with newer tools.
If you encounter such warnings, please consider
`reporting them <https://github.com/ethereum/solidity/issues/new>`_.
Minimum Compiler Versions
^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -153,7 +153,7 @@ the :ref:`standard JSON interface <compiler-api>`. The output is a JSON object
element has the following form:
.. code::
.. code-block:: json
{
@ -181,7 +181,7 @@ The given ``type``, in this case ``t_uint256`` represents an element in
``types``, which has the form:
.. code::
.. code-block:: json
{
"encoding": "inplace",
@ -238,7 +238,7 @@ value and reference types, types that are encoded packed, and nested types.
bytes b1;
}
.. code:: json
.. code-block:: json
{
"storage": [

View File

@ -330,7 +330,7 @@ the ``--yul-optimizations`` option:
The sequence inside ``[...]`` will be applied multiple times in a loop until the Yul code
remains unchanged or until the maximum number of rounds (currently 12) has been reached.
Available abbreviations are listed in the `Yul optimizer docs <yul.rst#optimization-step-sequence>`_.
Available abbreviations are listed in the :ref:`Yul optimizer docs <optimization-step-sequence>`.
Preprocessing
-------------
@ -683,7 +683,7 @@ Conflicting values are resolved in the following way:
- "unused", "undecided" -> "undecided"
- "unused", "used" -> "used"
- "undecided, "used" -> "used"
- "undecided", "used" -> "used"
For for-loops, the condition, body and post-part are visited twice, taking
the joining control-flow at the condition into account.

View File

@ -186,7 +186,9 @@ transactions.
To listen for this event, you could use the following
JavaScript code, which uses `web3.js <https://github.com/ethereum/web3.js/>`_ to create the ``Coin`` contract object,
and any user interface calls the automatically generated ``balances`` function from above::
and any user interface calls the automatically generated ``balances`` function from above:
.. code-block:: javascript
Coin.Sent().watch({}, '', function(error, result) {
if (!error) {
@ -504,10 +506,10 @@ operations, loops should be preferred over recursive calls. Furthermore,
only 63/64th of the gas can be forwarded in a message call, which causes a
depth limit of a little less than 1000 in practice.
.. index:: delegatecall, callcode, library
.. index:: delegatecall, library
Delegatecall / Callcode and Libraries
=====================================
Delegatecall and Libraries
==========================
There exists a special variant of a message call, named **delegatecall**
which is identical to a message call apart from the fact that
@ -548,7 +550,7 @@ these **create calls** and normal message calls is that the payload data is
executed and the result stored as code and the caller / creator
receives the address of the new contract on the stack.
.. index:: selfdestruct, self-destruct, deactivate
.. index:: ! selfdestruct, deactivate
Deactivate and Self-destruct
============================

View File

@ -154,37 +154,6 @@ hiding new and different behavior in existing code.
- New code generator: ``0`` as all parameters, including return parameters, will be re-initialized before
each ``_;`` evaluation.
- Copying ``bytes`` arrays from memory to storage is implemented in a different way.
The old code generator always copies full words, while the new one cuts the byte
array after its end. The old behaviour can lead to dirty data being copied after
the end of the array (but still in the same storage slot).
This causes differences in some contracts, for example:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.1;
contract C {
bytes x;
function f() public returns (uint r) {
bytes memory m = "tmp";
assembly {
mstore(m, 8)
mstore(add(m, 32), "deadbeef15dead")
}
x = m;
assembly {
r := sload(x.slot)
}
}
}
Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010``
(it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly).
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has
correct length, and correct elements, but does not contain superfluous data).
.. index:: ! evaluation order; expression
- For the old code generator, the evaluation order of expressions is unspecified.

View File

@ -56,7 +56,7 @@ you have to add the pragma to all your files if you want to enable it
in your whole project. If you :ref:`import<import>` another file, the pragma
from that file does *not* automatically apply to the importing file.
.. index:: ! pragma, version
.. index:: ! pragma;version
.. _version_pragma:
@ -91,6 +91,9 @@ these follow the same syntax used by `npm <https://docs.npmjs.com/cli/v6/using-n
required by the pragma. If it does not match, the compiler issues
an error.
.. index:: ! ABI coder, ! pragma; abicoder, pragma; ABIEncoderV2
.. _abi_coder:
ABI Coder Pragma
----------------
@ -98,12 +101,11 @@ By using ``pragma abicoder v1`` or ``pragma abicoder v2`` you can
select between the two implementations of the ABI encoder and decoder.
The new ABI coder (v2) is able to encode and decode arbitrarily nested
arrays and structs. It might produce less optimal code and has not
received as much testing as the old encoder, but is considered
non-experimental as of Solidity 0.6.0. You still have to explicitly
activate it using ``pragma abicoder v2;``. Since it will be
activated by default starting from Solidity 0.8.0, there is the option to select
the old coder using ``pragma abicoder v1;``.
arrays and structs. Apart from supporting more types, it involves more extensive
validation and safety checks, which may result in higher gas costs, but also heightened
security. It is considered
non-experimental as of Solidity 0.6.0 and it is enabled by default starting
with Solidity 0.8.0. The old ABI coder can still be selected using ``pragma abicoder v1;``.
The set of types supported by the new encoder is a strict superset of
the ones supported by the old one. Contracts that use it can interact with ones
@ -126,8 +128,7 @@ enough to make the error go away.
by using ``pragma experimental ABIEncoderV2``, but it was not possible
to explicitly select coder v1 because it was the default.
.. index:: ! pragma, experimental
.. index:: ! pragma; experimental
.. _experimental_pragma:
Experimental Pragma
@ -137,6 +138,7 @@ The second pragma is the experimental pragma. It can be used to enable
features of the compiler or language that are not yet enabled by default.
The following experimental pragmas are currently supported:
.. index:: ! pragma; ABIEncoderV2
ABIEncoderV2
~~~~~~~~~~~~
@ -145,6 +147,7 @@ Because the ABI coder v2 is not considered experimental anymore,
it can be selected via ``pragma abicoder v2`` (please see above)
since Solidity 0.7.4.
.. index:: ! pragma; SMTChecker
.. _smt_checker:
SMTChecker

View File

@ -21,9 +21,12 @@ the :ref:`Standard JSON Interface<compiler-api>`.
You have to publish the metadata file to IPFS, Swarm, or another service so
that others can access it. You create the file by using the ``solc --metadata``
command that generates a file called ``ContractName_meta.json``. It contains
IPFS and Swarm references to the source code, so you have to upload all source
files and the metadata file.
command together with the ``--output-dir`` parameter. Without the parameter,
the metadata will be written to standard output.
The metadata contains IPFS and Swarm references to the source code, so you have to
upload all source files in addition to the metadata file. For IPFS, the hash contained
in the CID returned by ``ipfs add`` (not the direct sha2-256 hash of the file)
shall match with the one contained in the bytecode.
The metadata file has the following format. The example below is presented in a
human-readable way. Properly formatted metadata should use quotes correctly,
@ -43,20 +46,20 @@ explanatory purposes.
// to the language.
"compiler": {
// Required for Solidity: Version of the compiler
"version": "0.4.6+commit.2dabbdf0.Emscripten.clang",
"version": "0.8.2+commit.661d1103",
// Optional: Hash of the compiler binary which produced this output
"keccak256": "0x123..."
},
// Required: Compilation source files/source units, keys are file names
// Required: Compilation source files/source units, keys are file paths
"sources":
{
"myFile.sol": {
"myDirectory/myFile.sol": {
// Required: keccak256 hash of the source file
"keccak256": "0x123...",
// Required (unless "content" is used, see below): Sorted URL(s)
// to the source file, protocol is more or less arbitrary, but a
// Swarm URL is recommended
"urls": [ "bzzr://56ab..." ],
// to the source file, protocol is more or less arbitrary, but an
// IPFS URL is recommended
"urls": [ "bzz-raw://7d7a...", "dweb:/ipfs/QmN..." ],
// Optional: SPDX license identifier as given in the source file
"license": "MIT"
},
@ -70,7 +73,7 @@ explanatory purposes.
// Required: Compiler settings
"settings":
{
// Required for Solidity: Sorted list of remappings
// Required for Solidity: Sorted list of import remappings
"remappings": [ ":g=/dir" ],
// Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated
// and are only given for backwards-compatibility.
@ -97,15 +100,15 @@ explanatory purposes.
}
},
"metadata": {
// Reflects the setting used in the input json, defaults to false
// Reflects the setting used in the input json, defaults to "false"
"useLiteralContent": true,
// Reflects the setting used in the input json, defaults to "ipfs"
"bytecodeHash": "ipfs"
},
// Required for Solidity: File and name of the contract or library this
// Required for Solidity: File path and the name of the contract or library this
// metadata is created for.
"compilationTarget": {
"myFile.sol": "MyContract"
"myDirectory/myFile.sol": "MyContract"
},
// Required for Solidity: Addresses for libraries used
"libraries": {
@ -115,12 +118,66 @@ explanatory purposes.
// Required: Generated information about the contract.
"output":
{
// Required: ABI definition of the contract
// Required: ABI definition of the contract. See "Contract ABI Specification"
"abi": [/* ... */],
// Required: NatSpec developer documentation of the contract.
"devdoc": {
"version": 1 // NatSpec version
"kind": "dev",
// Contents of the @author NatSpec field of the contract
"author": "John Doe",
// Contents of the @title NatSpec field of the contract
"title": "MyERC20: an example ERC20"
// Contents of the @dev NatSpec field of the contract
"details": "Interface of the ERC20 standard as defined in the EIP. See https://eips.ethereum.org/EIPS/eip-20 for details",
"methods": {
"transfer(address,uint256)": {
// Contents of the @dev NatSpec field of the method
"details": "Returns a boolean value indicating whether the operation succeeded. Must be called by the token holder address",
// Contents of the @param NatSpec fields of the method
"params": {
"_value": "The amount tokens to be transferred",
"_to": "The receiver address"
}
// Contents of the @return NatSpec field.
"returns": {
// Return var name (here "success") if exists. "_0" as key if return var is unnamed
"success": "a boolean value indicating whether the operation succeeded"
}
}
},
"stateVariables": {
"owner": {
// Contents of the @dev NatSpec field of the state variable
"details": "Must be set during contract creation. Can then only be changed by the owner"
}
}
"events": {
"Transfer(address,address,uint256)": {
"details": "Emitted when `value` tokens are moved from one account (`from`) toanother (`to`)."
"params": {
"from": "The sender address"
"to": "The receiver address"
"value": "The token amount"
}
}
}
},
// Required: NatSpec user documentation of the contract
"userdoc": [/* ... */],
// Required: NatSpec developer documentation of the contract
"devdoc": [/* ... */]
"userdoc": {
"version": 1 // NatSpec version
"kind": "user",
"methods": {
"transfer(address,uint256)": {
"notice": "Transfers `_value` tokens to address `_to`"
}
},
"events": {
"Transfer(address,address,uint256)": {
"notice": "`_value` tokens have been moved from `from` to `to`"
}
}
}
}
}
@ -157,7 +214,7 @@ to the end of the deployed bytecode
0x00 0x33
So in order to retrieve the data, the end of the deployed bytecode can be checked
to match that pattern and use the IPFS hash to retrieve the file.
to match that pattern and the IPFS hash can be used to retrieve the file (if pinned/published).
Whereas release builds of solc use a 3 byte encoding of the version as shown
above (one byte each for major, minor and patch version number), prerelease builds
@ -181,14 +238,15 @@ Usage for Automatic Interface Generation and NatSpec
====================================================
The metadata is used in the following way: A component that wants to interact
with a contract (e.g. Mist or any wallet) retrieves the code of the contract,
from that the IPFS/Swarm hash of a file which is then retrieved. That file
with a contract (e.g. a wallet) retrieves the code of the contract.
It decodes the CBOR encoded section containing the IPFS/Swarm hash of the
metadata file. With that hash, the metadata file is retrieved. That file
is JSON-decoded into a structure like above.
The component can then use the ABI to automatically generate a rudimentary
user interface for the contract.
Furthermore, the wallet can use the NatSpec user documentation to display a confirmation message to the user
Furthermore, the wallet can use the NatSpec user documentation to display a human-readable confirmation message to the user
whenever they interact with the contract, together with requesting
authorization for the transaction signature.

View File

@ -183,7 +183,7 @@ other to be used by the developer.
If the above contract is saved as ``ex1.sol`` then you can generate the
documentation using:
.. code::
.. code-block:: shell
solc --userdoc --devdoc ex1.sol
@ -202,7 +202,7 @@ User Documentation
The above documentation will produce the following user documentation
JSON file as output:
.. code::
.. code-block:: json
{
"version" : 1,
@ -230,7 +230,7 @@ Developer Documentation
Apart from the user documentation file, a developer documentation JSON
file should also be produced and should look like this:
.. code::
.. code-block:: json
{
"version" : 1,

View File

@ -98,7 +98,7 @@ as it uses ``call`` which forwards all remaining gas by default:
}
To avoid re-entrancy, you can use the Checks-Effects-Interactions pattern as
outlined further below:
demonstrated below:
.. code-block:: solidity
@ -116,6 +116,13 @@ outlined further below:
}
}
The Checks-Effects-Interactions pattern ensures that all code paths through a contract complete all required checks
of the supplied parameters before modifying the contract's state (Checks); only then it makes any changes to the state (Effects);
it may make calls to functions in other contracts *after* all planned state changes have been written to
storage (Interactions). This is a common foolproof way to prevent *re-entrancy attacks*, where an externally called
malicious contract is able to double-spend an allowance, double-withdraw a balance, among other things, by using logic that calls back into the
original contract before it has finalized its transaction.
Note that re-entrancy is not only an effect of Ether transfer but of any
function call on another contract. Furthermore, you also have to take
multi-contract situations into account. A called contract could modify the

View File

@ -146,8 +146,7 @@ No:
Maximum Line Length
===================
Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ to a maximum of 79 (or 99)
characters helps readers easily parse the code.
Maximum suggested line length is 120 characters.
Wrapped lines should conform to the following guidelines.
@ -1256,8 +1255,8 @@ Avoiding Naming Collisions
* ``singleTrailingUnderscore_``
This convention is suggested when the desired name collides with that of a
built-in or otherwise reserved name.
This convention is suggested when the desired name collides with that of
an existing state variable, function, built-in or otherwise reserved name.
.. _style_guide_natspec:

View File

@ -188,6 +188,10 @@ Addresses
As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum
test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type.
Explicit conversions from ``bytes20`` or any integer type to ``address`` result in ``address payable``.
Explicit conversions to ``address`` are allowed only from ``bytes20`` and ``uint160``.
An ``address a`` can be converted to ``address payable`` via ``payable(a)``.
An ``address a`` can be converted explicitly to ``address payable`` via ``payable(a)``.
.. note::
Prior to version 0.8.0, it was possible to explicitly convert from any integer type (of any size, signed or unsigned) to ``address`` or ``address payable``.
Starting with in 0.8.0 only conversion from ``uint160`` is allowed.

View File

@ -0,0 +1,57 @@
The following is the order of precedence for operators, listed in order of evaluation.
+------------+-------------------------------------+--------------------------------------------+
| Precedence | Description | Operator |
+============+=====================================+============================================+
| *1* | Postfix increment and decrement | ``++``, ``--`` |
+ +-------------------------------------+--------------------------------------------+
| | New expression | ``new <typename>`` |
+ +-------------------------------------+--------------------------------------------+
| | Array subscripting | ``<array>[<index>]`` |
+ +-------------------------------------+--------------------------------------------+
| | Member access | ``<object>.<member>`` |
+ +-------------------------------------+--------------------------------------------+
| | Function-like call | ``<func>(<args...>)`` |
+ +-------------------------------------+--------------------------------------------+
| | Parentheses | ``(<statement>)`` |
+------------+-------------------------------------+--------------------------------------------+
| *2* | Prefix increment and decrement | ``++``, ``--`` |
+ +-------------------------------------+--------------------------------------------+
| | Unary minus | ``-`` |
+ +-------------------------------------+--------------------------------------------+
| | Unary operations | ``delete`` |
+ +-------------------------------------+--------------------------------------------+
| | Logical NOT | ``!`` |
+ +-------------------------------------+--------------------------------------------+
| | Bitwise NOT | ``~`` |
+------------+-------------------------------------+--------------------------------------------+
| *3* | Exponentiation | ``**`` |
+------------+-------------------------------------+--------------------------------------------+
| *4* | Multiplication, division and modulo | ``*``, ``/``, ``%`` |
+------------+-------------------------------------+--------------------------------------------+
| *5* | Addition and subtraction | ``+``, ``-`` |
+------------+-------------------------------------+--------------------------------------------+
| *6* | Bitwise shift operators | ``<<``, ``>>`` |
+------------+-------------------------------------+--------------------------------------------+
| *7* | Bitwise AND | ``&`` |
+------------+-------------------------------------+--------------------------------------------+
| *8* | Bitwise XOR | ``^`` |
+------------+-------------------------------------+--------------------------------------------+
| *9* | Bitwise OR | ``|`` |
+------------+-------------------------------------+--------------------------------------------+
| *10* | Inequality operators | ``<``, ``>``, ``<=``, ``>=`` |
+------------+-------------------------------------+--------------------------------------------+
| *11* | Equality operators | ``==``, ``!=`` |
+------------+-------------------------------------+--------------------------------------------+
| *12* | Logical AND | ``&&`` |
+------------+-------------------------------------+--------------------------------------------+
| *13* | Logical OR | ``||`` |
+------------+-------------------------------------+--------------------------------------------+
| *14* | Ternary operator | ``<conditional> ? <if-true> : <if-false>`` |
+ +-------------------------------------+--------------------------------------------+
| | Assignment operators | ``=``, ``|=``, ``^=``, ``&=``, ``<<=``, |
| | | ``>>=``, ``+=``, ``-=``, ``*=``, ``/=``, |
| | | ``%=`` |
+------------+-------------------------------------+--------------------------------------------+
| *15* | Comma operator | ``,`` |
+------------+-------------------------------------+--------------------------------------------+

View File

@ -5,7 +5,7 @@ Operators
Arithmetic and bit operators can be applied even if the two operands do not have the same type.
For example, you can compute ``y = x + z``, where ``x`` is a ``uint8`` and ``z`` has
the type ``int32``. In these cases, the following mechanism will be used to determine
the type ``uint32``. In these cases, the following mechanism will be used to determine
the type in which the operation is computed (this is important in case of overflow)
and the type of the operator's result:
@ -18,7 +18,9 @@ and the type of the operator's result:
In case one of the operands is a :ref:`literal number <rational_literals>` it is first converted to its
"mobile type", which is the smallest type that can hold the value
(unsigned types of the same bit-width are considered "smaller" than the signed types).
If both are literal numbers, the operation is computed with arbitrary precision.
If both are literal numbers, the operation is computed with effectively unlimited precision in
that the expression is evaluated to whatever precision is necessary so that none is lost
when the result is used with a non-literal type.
The operator's result type is the same as the type the operation is performed in,
except for comparison operators where the result is always ``bool``.
@ -108,3 +110,11 @@ value it referred to previously.
assert(y.length == 0);
}
}
.. index:: ! operator; precedence
.. _order:
Order of Precedence of Operators
--------------------------------
.. include:: types/operator-precedence-table.rst

View File

@ -85,8 +85,10 @@ Data locations are not only relevant for persistency of data, but also for the s
// The following does not work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer, but there
// is no sensible location it could point to.
// Similarly, "delete y" is not valid, as assignments to local variables
// referencing storage objects can only be made from existing storage objects.
// It would "reset" the pointer, but there is no sensible location it could point to.
// For more details see the documentation of the "delete" operator.
// delete y;
g(x); // calls g, handing over a reference to x
h(x); // calls h and creates an independent, temporary copy in memory
@ -379,8 +381,10 @@ Array Members
uint[2**20] aLotOfIntegers;
// Note that the following is not a pair of dynamic arrays but a
// dynamic array of pairs (i.e. of fixed size arrays of length two).
// Because of that, T[] is always a dynamic array of T, even if T
// itself is an array.
// In Solidity, T[k] and T[] are always arrays with elements of type T,
// even if T itself is an array.
// Because of that, bool[2][] is a dynamic array of elements
// that are bool[2]. This is different from other languages, like C.
// Data location for all state variables is storage.
bool[2][] pairsOfFlags;
@ -468,6 +472,120 @@ Array Members
}
}
.. index:: ! array;dangling storage references
Dangling References to Storage Array Elements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When working with storage arrays, you need to take care to avoid dangling references.
A dangling reference is a reference that points to something that no longer exists or has been
moved without updating the reference. A dangling reference can for example occur, if you store a
reference to an array element in a local variable and then ``.pop()`` from the containing array:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
contract C {
uint[][] s;
function f() public {
// Stores a pointer to the last array element of s.
uint[] storage ptr = s[s.length - 1];
// Removes the last array element of s.
s.pop();
// Writes to the array element that is no longer within the array.
ptr.push(0x42);
// Adding a new element to ``s`` now will not add an empty array, but
// will result in an array of length 1 with ``0x42`` as element.
s.push();
assert(s[s.length - 1][0] == 0x42);
}
}
The write in ``ptr.push(0x42)`` will **not** revert, despite the fact that ``ptr`` no
longer refers to a valid element of ``s``. Since the compiler assumes that unused storage
is always zeroed, a subsequent ``s.push()`` will not explicitly write zeroes to storage,
so the last element of ``s`` after that ``push()`` will have length ``1`` and contain
``0x42`` as its first element.
Note that Solidity does not allow to declare references to value types in storage. These kinds
of explicit dangling references are restricted to nested reference types. However, dangling references
can also occur temporarily when using complex expressions in tuple assignments:
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
contract C {
uint[] s;
uint[] t;
constructor() {
// Push some initial values to the storage arrays.
s.push(0x07);
t.push(0x03);
}
function g() internal returns (uint[] storage) {
s.pop();
return t;
}
function f() public returns (uint[] memory) {
// The following will first evaluate ``s.push()`` to a reference to a new element
// at index 1. Afterwards, the call to ``g`` pops this new element, resulting in
// the left-most tuple element to become a dangling reference. The assignment still
// takes place and will write outside the data area of ``s``.
(s.push(), g()[0]) = (0x42, 0x17);
// A subsequent push to ``s`` will reveal the value written by the previous
// statement, i.e. the last element of ``s`` at the end of this function will have
// the value ``0x42``.
s.push();
return s;
}
}
It is always safer to only assign to storage once per statement and to avoid
complex expressions on the left-hand-side of an assignment.
You need to take particular care when dealing with references to elements of
``bytes`` arrays, since a ``.push()`` on a bytes array may switch :ref:`from short
to long layout in storage<bytes-and-string>`.
.. code-block:: solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;
// This will report a warning
contract C {
bytes x = "012345678901234567890123456789";
function test() external returns(uint) {
(x.push(), x.push()) = (0x01, 0x02);
return x.length;
}
}
Here, when the first ``x.push()`` is evaluated, ``x`` is still stored in short
layout, thereby ``x.push()`` returns a reference to an element in the first storage slot of
``x``. However, the second ``x.push()`` switches the bytes array to large layout.
Now the element that ``x.push()`` referred to is in the data area of the array while
the reference still points at its original location, which is now a part of the length field
and the assignment will effectively garble the length of ``x``.
To be safe, only enlarge bytes arrays by at most one element during a single
assignment and do not simultaneously index-access the array in the same statement.
While the above describes the behaviour of dangling storage references in the
current version of the compiler, any code with dangling references should be
considered to have *undefined behaviour*. In particular, this means that
any future version of the compiler may change the behaviour of code that
involves dangling references.
Be sure to avoid dangling references in your code!
.. index:: ! array;slice
.. _array-slices:

View File

@ -210,6 +210,11 @@ an exception to this rule.
declare its type as ``address payable`` to make this requirement visible. Also,
try to make this distinction or conversion as early as possible.
The distinction between ``address`` and ``address payable`` was introduced with version 0.5.0.
Also starting from that version, contracts are not implicitly convertible to the ``address`` type, but can still be explicitly converted to
``address`` or to ``address payable``, if they have a receive or payable fallback function.
Operators:
* ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>``
@ -223,9 +228,7 @@ Operators:
or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``.
.. note::
The distinction between ``address`` and ``address payable`` was introduced with version 0.5.0.
Also starting from that version, contracts do not derive from the address type, but can still be explicitly converted to
``address`` or to ``address payable``, if they have a receive or payable fallback function.
Mixed-case hexadecimal numbers conforming to `EIP-55 <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md>`_ are automatically treated as literals of the ``address`` type. See :ref:`Address Literals<address_literals>`.
.. _members-of-addresses:
@ -448,8 +451,8 @@ Integer literals are formed from a sequence of digits in the range 0-9.
They are interpreted as decimals. For example, ``69`` means sixty nine.
Octal literals do not exist in Solidity and leading zeros are invalid.
Decimal fractional literals are formed by a ``.`` with at least one number on
one side. Examples include ``1.``, ``.1`` and ``1.3``.
Decimal fractional literals are formed by a ``.`` with at least one number after the decimal point.
Examples include ``.1`` and ``1.3`` (but not ``1.``).
Scientific notation in the form of ``2e10`` is also supported, where the
mantissa can be fractional but the exponent has to be an integer.

View File

@ -384,3 +384,14 @@ The following properties are available for an integer type ``T``:
``type(T).max``
The largest value representable by type ``T``.
Reserved Keywords
=================
These keywords are reserved in Solidity. They might become part of the syntax in the future:
``after``, ``alias``, ``apply``, ``auto``, ``byte``, ``case``, ``copyof``, ``default``,
``define``, ``final``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``,
``mutable``, ``null``, ``of``, ``partial``, ``promise``, ``reference``, ``relocatable``,
``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``typedef``, ``typeof``,
``var``.

View File

@ -613,8 +613,9 @@ Error Types
10. ``Exception``: Unknown failure during compilation - this should be reported as an issue.
11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue.
12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue.
13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible.
14. ``Info``: Information that the compiler thinks the user might find useful, but is not dangerous and does not necessarily need to be addressed.
13. ``YulException``: Error during Yul Code generation - this should be reported as an issue.
14. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible.
15. ``Info``: Information that the compiler thinks the user might find useful, but is not dangerous and does not necessarily need to be addressed.
.. _compiler-tools:

View File

@ -663,10 +663,10 @@ We will use a destructuring notation for the AST nodes.
E(G, L, <var_1, ..., var_n := rhs>: Assignment) =
let G1, L1, v1, ..., vn = E(G, L, rhs)
let L2 be a copy of L1 where L2[$var_i] = vi for i = 1, ..., n
G, L2, regular
G1, L2, regular
E(G, L, <for { i1, ..., in } condition post body>: ForLoop) =
if n >= 1:
let G1, L, mode = E(G, L, i1, ..., in)
let G1, L1, mode = E(G, L, i1, ..., in)
// mode has to be regular or leave due to the syntactic restrictions
if mode is leave then
G1, L1 restricted to variables of L, leave
@ -686,7 +686,7 @@ We will use a destructuring notation for the AST nodes.
else:
G3, L3, mode = E(G2, L2, post)
if mode is leave:
G2, L3, leave
G3, L3, leave
otherwise
E(G3, L3, for {} condition post body)
E(G, L, break: BreakContinue) =
@ -1162,6 +1162,7 @@ An example Yul Object is shown below:
code {
function allocate(size) -> ptr {
ptr := mload(0x40)
// Note that Solidity generated IR code reserves memory offset ``0x60`` as well, but a pure Yul object is free to use memory as it chooses.
if iszero(ptr) { ptr := 0x60 }
mstore(0x40, add(ptr, size))
}
@ -1191,6 +1192,7 @@ An example Yul Object is shown below:
code {
function allocate(size) -> ptr {
ptr := mload(0x40)
// Note that Solidity generated IR code reserves memory offset ``0x60`` as well, but a pure Yul object is free to use memory as it chooses.
if iszero(ptr) { ptr := 0x60 }
mstore(0x40, add(ptr, size))
}
@ -1238,6 +1240,8 @@ and optionally specify the :ref:`expected number of contract executions <optimiz
In Solidity mode, the Yul optimizer is activated together with the regular optimizer.
.. _optimization-step-sequence:
Optimization Step Sequence
--------------------------

View File

@ -52,7 +52,7 @@ AssemblyItem const& Assembly::append(AssemblyItem _i)
{
assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow.");
m_deposit += static_cast<int>(_i.deposit());
m_items.emplace_back(move(_i));
m_items.emplace_back(std::move(_i));
if (!m_items.back().location().isValid() && m_currentSourceLocation.isValid())
m_items.back().setLocation(m_currentSourceLocation);
m_items.back().m_modifierDepth = m_currentModifierDepth;
@ -254,7 +254,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
if (!data.empty())
jsonItem["value"] = data;
jsonItem["source"] = sourceIndex;
code.append(move(jsonItem));
code.append(std::move(jsonItem));
if (item.type() == AssemblyItemType::Tag)
{
@ -265,7 +265,7 @@ Json::Value Assembly::assemblyJSON(map<string, unsigned> const& _sourceIndices,
jumpdest["source"] = sourceIndex;
if (item.m_modifierDepth != 0)
jumpdest["modifierDepth"] = static_cast<int>(item.m_modifierDepth);
code.append(move(jumpdest));
code.append(std::move(jumpdest));
}
}
if (_includeSourceList)
@ -464,7 +464,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
}
if (optimisedItems.size() < m_items.size())
{
m_items = move(optimisedItems);
m_items = std::move(optimisedItems);
count++;
}
}
@ -478,7 +478,7 @@ map<u256, u256> const& Assembly::optimiseInternal(
*this
);
m_tagReplacements = move(tagReplacements);
m_tagReplacements = std::move(tagReplacements);
return *m_tagReplacements;
}

View File

@ -26,6 +26,7 @@
#include <libsolutil/Keccak256.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/AssemblyItem.h>
#include <libsolutil/StackTooDeepString.h>
#include <range/v3/view/reverse.hpp>
@ -47,8 +48,8 @@ vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{
m_breakingItem = nullptr;
m_storeOperations.clear();
m_initialState = move(nextInitialState);
m_state = move(nextState);
m_initialState = std::move(nextInitialState);
m_state = std::move(nextState);
});
map<int, Id> initialStackContents;
@ -163,7 +164,7 @@ AssemblyItems CSECodeGenerator::generateCode(
// Invalid sequenced operation.
// @todo quick fix for now. Proper fix needs to choose representative with higher
// sequence number during dependency analysis.
assertThrow(seqNr >= _initialSequenceNumber, StackTooDeepException, "");
assertThrow(seqNr >= _initialSequenceNumber, StackTooDeepException, util::stackTooDeepString);
sequencedExpressions.insert(make_pair(seqNr, id));
}
@ -471,7 +472,7 @@ void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _locat
{
assertThrow(_fromPosition != c_invalidPosition, OptimizerException, "");
int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables.");
assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString);
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(dupInstruction(static_cast<unsigned>(instructionNum)), _location));
m_stack[m_stackHeight] = m_stack[_fromPosition];
@ -484,7 +485,7 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons
if (_fromPosition == m_stackHeight)
return;
int instructionNum = m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, "Stack too deep, try removing local variables.");
assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString);
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
appendItem(AssemblyItem(swapInstruction(static_cast<unsigned>(instructionNum)), _location));

View File

@ -179,9 +179,15 @@ AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
)
{
assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator.");
for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator, _msizeImportant); ++_iterator)
unsigned const maxChunkSize = 2000;
unsigned chunkSize = 0;
for (
;
_iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator, _msizeImportant) && chunkSize < maxChunkSize;
++_iterator, ++chunkSize
)
feedItem(*_iterator);
if (_iterator != _end)
if (_iterator != _end && chunkSize < maxChunkSize)
m_breakingItem = &(*_iterator++);
return _iterator;
}

View File

@ -244,8 +244,8 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
bigint newGas = gasNeeded(newRoutine);
if (newGas < bestGas)
{
bestGas = move(newGas);
routine = move(newRoutine);
bestGas = std::move(newGas);
routine = std::move(newRoutine);
}
}
return routine;

View File

@ -236,12 +236,12 @@ void ControlFlowGraph::gatherKnowledge()
item.state = _state->copy();
item.blocksSeen = _currentItem.blocksSeen;
item.blocksSeen.insert(_currentItem.blockId);
workQueue.push_back(move(item));
workQueue.push_back(std::move(item));
};
while (!workQueue.empty())
{
WorkQueueItem item = move(workQueue.back());
WorkQueueItem item = std::move(workQueue.back());
workQueue.pop_back();
//@todo we might have to do something like incrementing the sequence number for each JUMPDEST
assertThrow(!!item.blockId, OptimizerException, "");

View File

@ -23,38 +23,59 @@
*/
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/SimplificationRules.h>
#include <boost/container_hash/hash.hpp>
#include <functional>
#include <tuple>
#include <limits>
#include <tuple>
using namespace std;
using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;
bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const
bool ExpressionClasses::Expression::operator==(ExpressionClasses::Expression const& _other) const
{
assertThrow(!!item && !!_other.item, OptimizerException, "");
auto type = item->type();
auto otherType = _other.item->type();
if (type != otherType)
return type < otherType;
return false;
else if (type == Operation)
{
auto instr = item->instruction();
auto otherInstr = _other.item->instruction();
return std::tie(instr, arguments, sequenceNumber) <
return std::tie(instr, arguments, sequenceNumber) ==
std::tie(otherInstr, _other.arguments, _other.sequenceNumber);
}
else
return std::tie(item->data(), arguments, sequenceNumber) <
return std::tie(item->data(), arguments, sequenceNumber) ==
std::tie(_other.item->data(), _other.arguments, _other.sequenceNumber);
}
std::size_t ExpressionClasses::Expression::ExpressionHash::operator()(Expression const& _expression) const
{
assertThrow(!!_expression.item, OptimizerException, "");
std::size_t seed = 0;
auto type = _expression.item->type();
boost::hash_combine(seed, type);
if (type == Operation)
boost::hash_combine(seed, _expression.item->instruction());
else
boost::hash_combine(seed, _expression.item->data());
boost::hash_range(seed, _expression.arguments.begin(), _expression.arguments.end());
boost::hash_combine(seed, _expression.sequenceNumber);
return seed;
}
ExpressionClasses::Id ExpressionClasses::find(
AssemblyItem const& _item,
Ids const& _arguments,

View File

@ -24,13 +24,13 @@
#pragma once
#include <libsolutil/Common.h>
#include <libevmasm/AssemblyItem.h>
#include <vector>
#include <map>
#include <libsolutil/Common.h>
#include <memory>
#include <set>
#include <unordered_set>
#include <vector>
namespace solidity::langutil
{
@ -61,9 +61,15 @@ public:
/// Storage modification sequence, only used for storage and memory operations.
unsigned sequenceNumber = 0;
/// Behaves as if this was a tuple of (item->type(), item->data(), arguments, sequenceNumber).
bool operator<(Expression const& _other) const;
bool operator==(Expression const& _other) const;
struct ExpressionHash
{
std::size_t operator()(Expression const& _expression) const;
};
};
/// Retrieves the id of the expression equivalence class resulting from the given item applied to the
/// given classes, might also create a new one.
/// @param _copyItem if true, copies the assembly item to an internal storage instead of just
@ -122,7 +128,7 @@ private:
/// Expression equivalence class representatives - we only store one item of an equivalence.
std::vector<Expression> m_representatives;
/// All expression ever encountered.
std::set<Expression> m_expressions;
std::unordered_set<Expression, Expression::ExpressionHash> m_expressions;
std::vector<std::shared_ptr<AssemblyItem>> m_spareAssemblyItems;
};

View File

@ -184,7 +184,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
if (*value)
{
// Note: msb() counts from 0 and throws on 0 as input.
unsigned const significantByteCount = (boost::multiprecision::msb(*value) + 1 + 7) / 8;
unsigned const significantByteCount = (static_cast<unsigned>(boost::multiprecision::msb(*value)) + 1u + 7u) / 8u;
gas += GasCosts::expByteGas(m_evmVersion) * significantByteCount;
}
}

View File

@ -257,7 +257,7 @@ void Inliner::optimise()
if (auto exitItem = shouldInline(*tag, nextItem, *inlinableBlock))
{
newItems += inlinableBlock->items | ranges::views::drop_last(1);
newItems.emplace_back(move(*exitItem));
newItems.emplace_back(std::move(*exitItem));
// We are removing one push tag to the block we inline.
--inlinableBlock->pushTagCount;
@ -277,5 +277,5 @@ void Inliner::optimise()
newItems.emplace_back(item);
}
m_items = move(newItems);
m_items = std::move(newItems);
}

View File

@ -252,7 +252,7 @@ void KnownState::reduceToCommonKnowledge(KnownState const& _other, bool _combine
map<int, Id> shiftedStack;
for (auto const& stackElement: m_stackElements)
shiftedStack[stackElement.first - stackDiff] = stackElement.second;
m_stackElements = move(shiftedStack);
m_stackElements = std::move(shiftedStack);
m_stackHeight = _other.m_stackHeight;
}
@ -333,7 +333,7 @@ KnownState::StoreOperation KnownState::storeInStorage(
for (auto const& storageItem: m_storageContent)
if (m_expressionClasses->knownToBeDifferent(storageItem.first, _slot) || storageItem.second == _value)
storageContents.insert(storageItem);
m_storageContent = move(storageContents);
m_storageContent = std::move(storageContents);
AssemblyItem item(Instruction::SSTORE, _location);
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);
@ -365,7 +365,7 @@ KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, Source
for (auto const& memoryItem: m_memoryContent)
if (m_expressionClasses->knownToBeDifferentBy32(memoryItem.first, _slot))
memoryContents.insert(memoryItem);
m_memoryContent = move(memoryContents);
m_memoryContent = std::move(memoryContents);
AssemblyItem item(Instruction::MSTORE, _location);
Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber);

View File

@ -132,7 +132,7 @@ public:
/// @returns true if the knowledge about the state of both objects is (known to be) equal.
bool operator==(KnownState const& _other) const;
/// Retrieves the current equivalence class fo the given stack element (or generates a new
/// Retrieves the current equivalence class for the given stack element (or generates a new
/// one if it does not exist yet).
Id stackElement(int _stackHeight, langutil::SourceLocation const& _location);
/// @returns the stackElement relative to the current stack height.

View File

@ -44,7 +44,7 @@ GasMeter::GasConsumption PathGasMeter::estimateMax(
auto path = make_unique<GasPath>();
path->index = _startIndex;
path->state = _state->copy();
queue(move(path));
queue(std::move(path));
GasMeter::GasConsumption gas;
while (!m_queue.empty() && !gas.isInfinite)
@ -60,14 +60,14 @@ void PathGasMeter::queue(std::unique_ptr<GasPath>&& _newPath)
)
return;
m_highestGasUsagePerJumpdest[_newPath->index] = _newPath->gas;
m_queue[_newPath->index] = move(_newPath);
m_queue[_newPath->index] = std::move(_newPath);
}
GasMeter::GasConsumption PathGasMeter::handleQueueItem()
{
assertThrow(!m_queue.empty(), OptimizerException, "");
unique_ptr<GasPath> path = move(m_queue.rbegin()->second);
unique_ptr<GasPath> path = std::move(m_queue.rbegin()->second);
m_queue.erase(--m_queue.end());
shared_ptr<KnownState> state = path->state;
@ -129,7 +129,7 @@ GasMeter::GasConsumption PathGasMeter::handleQueueItem()
newPath->largestMemoryAccess = meter.largestMemoryAccess();
newPath->state = state->copy();
newPath->visitedJumpdests = path->visitedJumpdests;
queue(move(newPath));
queue(std::move(newPath));
}
if (branchStops)

View File

@ -275,11 +275,12 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4_5(
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
bool _forYulOptimizer,
Pattern A,
Pattern B,
Pattern,
Pattern X,
Pattern
Pattern Y
)
{
using Word = typename Pattern::Word;
@ -287,6 +288,28 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
std::vector<SimplificationRule<Pattern>> rules;
// The libevmasm optimizer does not support rules resulting in opcodes with more than two arguments.
if (_forYulOptimizer)
{
// Replace MOD(MUL(X, Y), A) with MULMOD(X, Y, A) iff A=2**N
rules.push_back({
Builtins::MOD(Builtins::MUL(X, Y), A),
[=]() -> Pattern { return Builtins::MULMOD(X, Y, A); },
[=] {
return A.d() > 0 && ((A.d() & (A.d() - 1)) == 0);
}
});
// Replace MOD(ADD(X, Y), A) with ADDMOD(X, Y, A) iff A=2**N
rules.push_back({
Builtins::MOD(Builtins::ADD(X, Y), A),
[=]() -> Pattern { return Builtins::ADDMOD(X, Y, A); },
[=] {
return A.d() > 0 && ((A.d() & (A.d() - 1)) == 0);
}
});
}
// Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1
for (size_t i = 0; i < Pattern::WordSize; ++i)
{
@ -798,7 +821,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
rules += simplificationRuleListPart3(A, B, C, W, X);
rules += simplificationRuleListPart4(A, B, C, W, X);
rules += simplificationRuleListPart4_5(A, B, C, W, X);
rules += simplificationRuleListPart5(A, B, C, W, X);
rules += simplificationRuleListPart5(_evmVersion.has_value(), A, B, C, W, X);
rules += simplificationRuleListPart6(A, B, C, W, X);
rules += simplificationRuleListPart7(A, B, C, W, X);
rules += simplificationRuleListPart8(A, B, C, W, X);

View File

@ -73,9 +73,15 @@ public:
CharStream() = default;
CharStream(std::string _source, std::string _name):
m_source(std::move(_source)), m_name(std::move(_name)) {}
CharStream(std::string _source, std::string _name, bool _importedFromAST):
m_source(std::move(_source)),
m_name(std::move(_name)),
m_importedFromAST(_importedFromAST)
{ }
size_t position() const { return m_position; }
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }
bool isImportedFromAST() const { return m_importedFromAST; }
char get(size_t _charsForward = 0) const { return m_source[m_position + _charsForward]; }
char advanceAndGet(size_t _chars = 1);
@ -138,6 +144,7 @@ public:
private:
std::string m_source;
std::string m_name;
bool m_importedFromAST{false};
size_t m_position{0};
};

View File

@ -34,7 +34,7 @@ namespace solidity::langutil
/**
* A version specifier of the EVM we want to compile to.
* Defaults to the latest version deployed on Ethereum mainnet at the time of compiler release.
* Defaults to the latest version deployed on Ethereum Mainnet at the time of compiler release.
*/
class EVMVersion:
boost::less_than_comparable<EVMVersion>,

View File

@ -76,6 +76,7 @@ string to_string(ScannerError _errorCode)
case ScannerError::IllegalHexDigit: return "Hexadecimal digit missing or invalid.";
case ScannerError::IllegalCommentTerminator: return "Expected multi-line comment-terminator.";
case ScannerError::IllegalEscapeSequence: return "Invalid escape sequence.";
case ScannerError::UnicodeCharacterInNonUnicodeString: return "Invalid character in string. If you are trying to use Unicode characters, use a unicode\"...\" string literal.";
case ScannerError::IllegalCharacterInString: return "Invalid character in string.";
case ScannerError::IllegalStringEndQuote: return "Expected string end-quote.";
case ScannerError::IllegalNumberSeparator: return "Invalid use of number separator '_'.";
@ -424,7 +425,7 @@ Token Scanner::scanMultiLineDocComment()
while (!isSourcePastEndOfInput())
{
//handle newlines in multline comments
// handle newlines in multiline comments
if (atEndOfLine())
{
skipWhitespace();
@ -844,7 +845,11 @@ Token Scanner::scanString(bool const _isUnicode)
// We are using a manual range and not isprint() to avoid
// any potential complications with locale.
if (!_isUnicode && (static_cast<unsigned>(c) <= 0x1f || static_cast<unsigned>(c) >= 0x7f))
return setError(ScannerError::IllegalCharacterInString);
{
if (m_kind == ScannerKind::Yul)
return setError(ScannerError::IllegalCharacterInString);
return setError(ScannerError::UnicodeCharacterInNonUnicodeString);
}
addLiteralChar(c);
}
}

View File

@ -81,6 +81,7 @@ enum class ScannerError
IllegalHexDigit,
IllegalCommentTerminator,
IllegalEscapeSequence,
UnicodeCharacterInNonUnicodeString,
IllegalCharacterInString,
IllegalStringEndQuote,
IllegalNumberSeparator,
@ -160,6 +161,8 @@ public:
/// Called by the parser during FunctionDefinition parsing to clear the current comment
void clearCurrentCommentLiteral() { m_skippedComments[Current].literal.clear(); }
ScannerKind scannerKind() const { return m_kind; }
///@}
///@{

View File

@ -124,6 +124,9 @@ void SourceReferenceFormatter::printSourceLocation(SourceReference const& _ref)
string_view text = _ref.text;
if (m_charStreamProvider.charStream(_ref.sourceName).isImportedFromAST())
return;
if (!_ref.multiline)
{
size_t const locationLength = static_cast<size_t>(_ref.endColumn - _ref.startColumn);

View File

@ -44,7 +44,7 @@ CHCSmtLib2Interface::CHCSmtLib2Interface(
):
CHCSolverInterface(_queryTimeout),
m_smtlib2(make_unique<SMTLib2Interface>(_queryResponses, _smtCallback, m_queryTimeout)),
m_queryResponses(move(_queryResponses)),
m_queryResponses(std::move(_queryResponses)),
m_smtCallback(_smtCallback)
{
reset();
@ -195,7 +195,7 @@ void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const
void CHCSmtLib2Interface::write(string _data)
{
m_accumulatedOutput += move(_data) + "\n";
m_accumulatedOutput += std::move(_data) + "\n";
}
string CHCSmtLib2Interface::querySolver(string const& _input)

View File

@ -19,6 +19,7 @@
#include <libsmtutil/CVC4Interface.h>
#include <libsolutil/CommonIO.h>
#include <libsolutil/Exceptions.h>
#include <cvc4/util/bitvector.h>
@ -277,7 +278,7 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
return m_context.mkExpr(CVC4::kind::APPLY_CONSTRUCTOR, c, arguments);
}
smtAssert(false, "");
smtAssert(false);
}
catch (CVC4::TypeCheckingException const& _e)
{
@ -288,7 +289,10 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
smtAssert(false, _e.what());
}
smtAssert(false, "");
smtAssert(false);
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort)

View File

@ -45,8 +45,8 @@ SMTLib2Interface::SMTLib2Interface(
optional<unsigned> _queryTimeout
):
SolverInterface(_queryTimeout),
m_queryResponses(move(_queryResponses)),
m_smtCallback(move(_smtCallback))
m_queryResponses(std::move(_queryResponses)),
m_smtCallback(std::move(_smtCallback))
{
reset();
}
@ -264,7 +264,7 @@ string SMTLib2Interface::toSmtLibSort(vector<SortPointer> const& _sorts)
void SMTLib2Interface::write(string _data)
{
smtAssert(!m_accumulatedOutput.empty(), "");
m_accumulatedOutput.back() += move(_data) + "\n";
m_accumulatedOutput.back() += std::move(_data) + "\n";
}
string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _expressionsToEvaluate)

View File

@ -41,7 +41,7 @@ SMTPortfolio::SMTPortfolio(
SolverInterface(_queryTimeout)
{
if (_enabledSolvers.smtlib2)
m_solvers.emplace_back(make_unique<SMTLib2Interface>(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout));
m_solvers.emplace_back(make_unique<SMTLib2Interface>(std::move(_smtlib2Responses), std::move(_smtCallback), m_queryTimeout));
#ifdef HAVE_Z3
if (_enabledSolvers.z3 && Z3Interface::available())
m_solvers.emplace_back(make_unique<Z3Interface>(m_queryTimeout));

View File

@ -101,7 +101,7 @@ tuple<CheckResult, Expression, CHCSolverInterface::CexGraph> Z3CHCInterface::que
{
result = CheckResult::UNSATISFIABLE;
auto invariants = m_z3Interface->fromZ3Expr(m_solver.get_answer());
return {result, move(invariants), {}};
return {result, std::move(invariants), {}};
}
case z3::check_result::unknown:
{

View File

@ -20,6 +20,7 @@
#include <libsolutil/CommonData.h>
#include <libsolutil/CommonIO.h>
#include <libsolutil/Exceptions.h>
#ifdef HAVE_Z3_DLOPEN
#include <libsmtutil/Z3Loader.h>
@ -263,14 +264,17 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
return constructor(args);
}
smtAssert(false, "");
smtAssert(false);
}
catch (z3::exception const& _e)
{
smtAssert(false, _e.msg());
}
smtAssert(false, "");
smtAssert(false);
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
Expression Z3Interface::fromZ3Expr(z3::expr const& _expr)
@ -378,7 +382,10 @@ Expression Z3Interface::fromZ3Expr(z3::expr const& _expr)
)
return Expression(_expr.decl().name().str(), arguments, fromZ3Sort(_expr.get_sort()));
smtAssert(false, "");
smtAssert(false);
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
z3::sort Z3Interface::z3Sort(Sort const& _sort)

View File

@ -56,7 +56,7 @@ string takeOverAllocation(char const* _data)
for (auto iter = begin(solidityAllocations); iter != end(solidityAllocations); ++iter)
if (iter->data() == _data)
{
string chunk = move(*iter);
string chunk = std::move(*iter);
solidityAllocations.erase(iter);
return chunk;
}
@ -109,7 +109,7 @@ ReadCallback::Callback wrapReadCallback(CStyleReadFileCallback _readCallback, vo
string compile(string _input, CStyleReadFileCallback _readCallback, void* _readContext)
{
StandardCompiler compiler(wrapReadCallback(_readCallback, _readContext));
return compiler.compile(move(_input));
return compiler.compile(std::move(_input));
}
}

View File

@ -53,8 +53,8 @@ set(sources
ast/ASTAnnotations.h
ast/ASTEnums.h
ast/ASTForward.h
ast/ASTJsonConverter.cpp
ast/ASTJsonConverter.h
ast/ASTJsonExporter.cpp
ast/ASTJsonExporter.h
ast/ASTUtils.cpp
ast/ASTUtils.h
ast/ASTJsonImporter.cpp
@ -159,10 +159,14 @@ set(sources
lsp/FileRepository.h
lsp/GotoDefinition.cpp
lsp/GotoDefinition.h
lsp/RenameSymbol.cpp
lsp/RenameSymbol.h
lsp/HandlerBase.cpp
lsp/HandlerBase.h
lsp/LanguageServer.cpp
lsp/LanguageServer.h
lsp/SemanticTokensBuilder.cpp
lsp/SemanticTokensBuilder.h
lsp/Transport.cpp
lsp/Transport.h
lsp/Utils.cpp

View File

@ -49,7 +49,7 @@ bool fitsPrecisionExp(bigint const& _base, bigint const& _exp)
size_t const bitsMax = 4096;
unsigned mostSignificantBaseBit = boost::multiprecision::msb(_base);
size_t mostSignificantBaseBit = static_cast<size_t>(boost::multiprecision::msb(_base));
if (mostSignificantBaseBit == 0) // _base == 1
return true;
if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096

View File

@ -17,6 +17,7 @@
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/analysis/ControlFlowBuilder.h>
#include <libsolidity/ast/ASTUtils.h>
#include <libyul/AST.h>
#include <libyul/backends/evm/EVMDialect.h>
@ -617,11 +618,7 @@ bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDecl
solAssert(tupleExpression->components().size() > i, "");
expression = tupleExpression->components()[i].get();
}
while (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
if (tupleExpression->components().size() == 1)
expression = tupleExpression->components().front().get();
else
break;
expression = resolveOuterUnaryTuples(expression);
m_currentNode->variableOccurrences.emplace_back(
*var,
VariableOccurrence::Kind::Assignment,

View File

@ -144,9 +144,7 @@ bool DeclarationContainer::registerDeclaration(
vector<Declaration const*> DeclarationContainer::resolveName(
ASTString const& _name,
bool _recursive,
bool _alsoInvisible,
bool _onlyVisibleAsUnqualifiedNames
ResolvingSettings _settings
) const
{
solAssert(!_name.empty(), "Attempt to resolve empty name.");
@ -154,22 +152,22 @@ vector<Declaration const*> DeclarationContainer::resolveName(
if (m_declarations.count(_name))
{
if (_onlyVisibleAsUnqualifiedNames)
if (_settings.onlyVisibleAsUnqualifiedNames)
result += m_declarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector;
else
result += m_declarations.at(_name);
}
if (_alsoInvisible && m_invisibleDeclarations.count(_name))
if (_settings.alsoInvisible && m_invisibleDeclarations.count(_name))
{
if (_onlyVisibleAsUnqualifiedNames)
if (_settings.onlyVisibleAsUnqualifiedNames)
result += m_invisibleDeclarations.at(_name) | ranges::views::filter(&Declaration::isVisibleAsUnqualifiedName) | ranges::to_vector;
else
result += m_invisibleDeclarations.at(_name);
}
if (result.empty() && _recursive && m_enclosingContainer)
result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible, _onlyVisibleAsUnqualifiedNames);
if (result.empty() && _settings.recursive && m_enclosingContainer)
result = m_enclosingContainer->resolveName(_name, _settings);
return result;
}
@ -209,7 +207,10 @@ void DeclarationContainer::populateHomonyms(back_insert_iterator<Homonyms> _it)
for (auto [name, location]: m_homonymCandidates)
{
vector<Declaration const*> const& declarations = m_enclosingContainer->resolveName(name, true, true);
ResolvingSettings settings;
settings.recursive = true;
settings.alsoInvisible = true;
vector<Declaration const*> const& declarations = m_enclosingContainer->resolveName(name, std::move(settings));
if (!declarations.empty())
_it = make_pair(location, declarations);
}

View File

@ -27,9 +27,27 @@
#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceLocation.h>
#include <memory>
namespace solidity::frontend
{
/**
* Settings for how the function DeclarationContainer::resolveName operates.
*/
struct ResolvingSettings
{
/// if true and there are no matching declarations in the current container,
/// recursively searches the enclosing containers as well.
bool recursive = false;
/// if true, include invisible declaration in the results.
bool alsoInvisible = false;
/// if true, do not include declarations which can never actually be referenced using their
/// name alone (without being qualified with the name of scope in which they are declared).
bool onlyVisibleAsUnqualifiedNames = false;
};
/**
* Container that stores mappings between names and declarations. It also contains a link to the
* enclosing scope.
@ -58,18 +76,8 @@ public:
/// Finds all declarations that in the current scope can be referred to using specified name.
/// @param _name the name to look for.
/// @param _recursive if true and there are no matching declarations in the current container,
/// recursively searches the enclosing containers as well.
/// @param _alsoInvisible if true, include invisible declaration in the results.
/// @param _onlyVisibleAsUnqualifiedNames if true, do not include declarations which can never
/// actually be referenced using their name alone (without being qualified with the name
/// of scope in which they are declared).
std::vector<Declaration const*> resolveName(
ASTString const& _name,
bool _recursive = false,
bool _alsoInvisible = false,
bool _onlyVisibleAsUnqualifiedNames = false
) const;
/// @param _settings see ResolvingSettings
std::vector<Declaration const*> resolveName(ASTString const& _name, ResolvingSettings _settings = ResolvingSettings{}) const;
ASTNode const* enclosingNode() const { return m_enclosingNode; }
DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }

View File

@ -61,7 +61,7 @@ CallGraph FunctionCallGraphBuilder::buildCreationGraph(ContractDefinition const&
builder.m_currentNode = CallGraph::SpecialNode::Entry;
builder.processQueue();
return move(builder.m_graph);
return std::move(builder.m_graph);
}
CallGraph FunctionCallGraphBuilder::buildDeployedGraph(
@ -109,7 +109,7 @@ CallGraph FunctionCallGraphBuilder::buildDeployedGraph(
builder.m_currentNode = CallGraph::SpecialNode::Entry;
builder.processQueue();
return move(builder.m_graph);
return std::move(builder.m_graph);
}
bool FunctionCallGraphBuilder::visit(FunctionCall const& _functionCall)

View File

@ -83,7 +83,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
if (!imp->symbolAliases().empty())
for (auto const& alias: imp->symbolAliases())
{
auto declarations = scope->second->resolveName(alias.symbol->name(), false);
auto declarations = scope->second->resolveName(alias.symbol->name());
if (declarations.empty())
{
m_errorReporter.declarationError(
@ -176,34 +176,52 @@ vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _na
auto iterator = m_scopes.find(_scope);
if (iterator == end(m_scopes))
return vector<Declaration const*>({});
return iterator->second->resolveName(_name, false);
return iterator->second->resolveName(_name);
}
vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles) const
{
return m_currentScope->resolveName(_name, true, _includeInvisibles);
ResolvingSettings settings;
settings.recursive = true;
settings.alsoInvisible = _includeInvisibles;
return m_currentScope->resolveName(_name, std::move(settings));
}
Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path) const
{
if (auto declarations = pathFromCurrentScopeWithAllDeclarations(_path); !declarations.empty())
return declarations.back();
return nullptr;
}
std::vector<Declaration const*> NameAndTypeResolver::pathFromCurrentScopeWithAllDeclarations(std::vector<ASTString> const& _path) const
{
solAssert(!_path.empty(), "");
vector<Declaration const*> candidates = m_currentScope->resolveName(
_path.front(),
/* _recursive */ true,
/* _alsoInvisible */ false,
/* _onlyVisibleAsUnqualifiedNames */ true
);
vector<Declaration const*> pathDeclarations;
ResolvingSettings settings;
settings.recursive = true;
settings.alsoInvisible = false;
settings.onlyVisibleAsUnqualifiedNames = true;
vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), std::move(settings));
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
{
if (!m_scopes.count(candidates.front()))
return nullptr;
candidates = m_scopes.at(candidates.front())->resolveName(_path[i], false);
return {};
pathDeclarations.push_back(candidates.front());
candidates = m_scopes.at(candidates.front())->resolveName(_path[i]);
}
if (candidates.size() == 1)
return candidates.front();
{
pathDeclarations.push_back(candidates.front());
return pathDeclarations;
}
else
return nullptr;
return {};
}
void NameAndTypeResolver::warnHomonymDeclarations() const

View File

@ -93,6 +93,10 @@ public:
/// Should only be called during the initial resolving phase.
/// @note Returns a null pointer if any component in the path was not unique or not found.
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const;
/// Resolves a path starting from the "current" scope, but also searches parent scopes.
/// Should only be called during the initial resolving phase.
/// @note Returns an empty vector if any component in the path was non-unique or not found. Otherwise, all declarations along the path are returned.
std::vector<Declaration const*> pathFromCurrentScopeWithAllDeclarations(std::vector<ASTString> const& _path) const;
/// Generate and store warnings about declarations with the same name.
void warnHomonymDeclarations() const;

View File

@ -173,14 +173,15 @@ void ReferencesResolver::endVisit(ModifierDefinition const&)
void ReferencesResolver::endVisit(IdentifierPath const& _path)
{
Declaration const* declaration = m_resolver.pathFromCurrentScope(_path.path());
if (!declaration)
std::vector<Declaration const*> declarations = m_resolver.pathFromCurrentScopeWithAllDeclarations(_path.path());
if (declarations.empty())
{
m_errorReporter.fatalDeclarationError(7920_error, _path.location(), "Identifier not found or not unique.");
return;
}
_path.annotation().referencedDeclaration = declaration;
_path.annotation().referencedDeclaration = declarations.back();
_path.annotation().pathDeclarations = std::move(declarations);
}
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
@ -275,7 +276,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier)
return;
}
m_yulAnnotation->externalReferences[&_identifier].suffix = move(suffix);
m_yulAnnotation->externalReferences[&_identifier].suffix = std::move(suffix);
m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front();
}

View File

@ -40,9 +40,10 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <range/v3/view/zip.hpp>
#include <range/v3/view/drop_exactly.hpp>
#include <range/v3/algorithm/count_if.hpp>
#include <range/v3/view/drop_exactly.hpp>
#include <range/v3/view/enumerate.hpp>
#include <range/v3/view/zip.hpp>
#include <memory>
#include <vector>
@ -105,26 +106,79 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
{
TupleType const& lhs = dynamic_cast<TupleType const&>(*type(_assignment.leftHandSide()));
TupleType const& rhs = dynamic_cast<TupleType const&>(*type(_assignment.rightHandSide()));
if (lhs.components().size() != rhs.components().size())
{
solAssert(m_errorReporter.hasErrors(), "");
return;
}
size_t storageToStorageCopies = 0;
size_t toStorageCopies = 0;
for (size_t i = 0; i < lhs.components().size(); ++i)
size_t storageByteArrayPushes = 0;
size_t storageByteAccesses = 0;
auto count = [&](TupleExpression const& _lhs, TupleType const& _rhs, auto _recurse) -> void {
TupleType const& lhsType = dynamic_cast<TupleType const&>(*type(_lhs));
TupleExpression const* lhsResolved = dynamic_cast<TupleExpression const*>(resolveOuterUnaryTuples(&_lhs));
if (lhsType.components().size() != _rhs.components().size() || lhsResolved->components().size() != _rhs.components().size())
{
solAssert(m_errorReporter.hasErrors(), "");
return;
}
for (auto&& [index, componentType]: lhsType.components() | ranges::views::enumerate)
{
if (ReferenceType const* ref = dynamic_cast<ReferenceType const*>(componentType))
{
if (ref && ref->dataStoredIn(DataLocation::Storage) && !ref->isPointer())
{
toStorageCopies++;
if (_rhs.components()[index]->dataStoredIn(DataLocation::Storage))
storageToStorageCopies++;
}
}
else if (FixedBytesType const* bytesType = dynamic_cast<FixedBytesType const*>(componentType))
{
if (bytesType && bytesType->numBytes() == 1)
{
if (FunctionCall const* lhsCall = dynamic_cast<FunctionCall const*>(resolveOuterUnaryTuples(lhsResolved->components().at(index).get())))
{
FunctionType const& callType = dynamic_cast<FunctionType const&>(*type(lhsCall->expression()));
if (callType.kind() == FunctionType::Kind::ArrayPush)
{
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*callType.selfType());
if (arrayType.isByteArray() && arrayType.dataStoredIn(DataLocation::Storage))
{
++storageByteAccesses;
++storageByteArrayPushes;
}
}
}
else if (IndexAccess const* indexAccess = dynamic_cast<IndexAccess const*>(resolveOuterUnaryTuples(lhsResolved->components().at(index).get())))
{
if (ArrayType const* arrayType = dynamic_cast<ArrayType const*>(type(indexAccess->baseExpression())))
if (arrayType->isByteArray() && arrayType->dataStoredIn(DataLocation::Storage))
++storageByteAccesses;
}
}
}
else if (TupleType const* tupleType = dynamic_cast<TupleType const*>(componentType))
if (auto const* lhsNested = dynamic_cast<TupleExpression const*>(lhsResolved->components().at(index).get()))
if (auto const* rhsNestedType = dynamic_cast<TupleType const*>(_rhs.components().at(index)))
_recurse(
*lhsNested,
*rhsNestedType,
_recurse
);
}
};
TupleExpression const* lhsTupleExpression = dynamic_cast<TupleExpression const*>(&_assignment.leftHandSide());
if (!lhsTupleExpression)
{
ReferenceType const* ref = dynamic_cast<ReferenceType const*>(lhs.components()[i]);
if (!ref || !ref->dataStoredIn(DataLocation::Storage) || ref->isPointer())
continue;
toStorageCopies++;
if (rhs.components()[i]->dataStoredIn(DataLocation::Storage))
storageToStorageCopies++;
solAssert(m_errorReporter.hasErrors());
return;
}
count(
*lhsTupleExpression,
dynamic_cast<TupleType const&>(*type(_assignment.rightHandSide())),
count
);
if (storageToStorageCopies >= 1 && toStorageCopies >= 2)
m_errorReporter.warning(
7238_error,
@ -134,6 +188,16 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
"is executed and thus may have unexpected effects. It is safer to perform the copies "
"separately or assign to storage pointers first."
);
if (storageByteArrayPushes >= 1 && storageByteAccesses >= 2)
m_errorReporter.warning(
7239_error,
_assignment.location(),
"This assignment involves multiple accesses to a bytes array in storage while simultaneously enlarging it. "
"When a bytes array is enlarged, it may transition from short storage layout to long storage layout, "
"which invalidates all references to its elements. It is safer to only enlarge byte arrays in a single "
"operation, one element at a time."
);
}
TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
@ -158,7 +222,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
arguments.front()->location(),
"The first argument to \"abi.decode\" must be implicitly convertible to "
"bytes memory or bytes calldata, but is of type " +
type(*arguments.front())->toString() +
type(*arguments.front())->humanReadableName() +
"."
);
@ -202,7 +266,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
m_errorReporter.typeError(
9611_error,
typeArgument->location(),
"Decoding type " + actualType->toString(false) + " not supported."
"Decoding type " + actualType->humanReadableName() + " not supported."
);
if (auto referenceType = dynamic_cast<ReferenceType const*>(actualType))
@ -262,7 +326,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio
arguments.front()->location(),
"Invalid type for argument in the function call. "
"An enum type, contract type or an integer type is required, but " +
type(*arguments.front())->toString(true) + " provided."
type(*arguments.front())->humanReadableName() + " provided."
);
return {TypeProvider::meta(dynamic_cast<TypeType const&>(*firstArgType).actualType())};
@ -311,9 +375,9 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
(*arguments)[i]->location(),
"Invalid type for argument in constructor call. "
"Invalid implicit conversion from " +
type(*(*arguments)[i])->toString() +
type(*(*arguments)[i])->humanReadableName() +
" to " +
parameterTypes[i]->toString() +
parameterTypes[i]->humanReadableName() +
" requested.",
result.message()
);
@ -567,7 +631,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
m_errorReporter.fatalTypeError(
4061_error,
_variable.location(),
"Type " + varType->toString(true) + " is only valid in storage because it contains a (nested) mapping."
"Type " + varType->humanReadableName() + " is only valid in storage because it contains a (nested) mapping."
);
}
else if (_variable.visibility() >= Visibility::Public)
@ -578,7 +642,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
vector<string> unsupportedTypes;
for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes())
if (!typeSupportedByOldABIEncoder(*param, false /* isLibrary */))
unsupportedTypes.emplace_back(param->toString());
unsupportedTypes.emplace_back(param->humanReadableName());
if (!unsupportedTypes.empty())
m_errorReporter.typeError(
2763_error,
@ -720,9 +784,9 @@ void TypeChecker::visitManually(
arguments[i]->location(),
"Invalid type for argument in modifier invocation. "
"Invalid implicit conversion from " +
type(*arguments[i])->toString() +
type(*arguments[i])->humanReadableName() +
" to " +
type(*(*parameters)[i])->toString() +
type(*(*parameters)[i])->humanReadableName() +
" requested.",
result.message()
);
@ -1052,9 +1116,9 @@ void TypeChecker::endVisit(TryStatement const& _tryStatement)
6509_error,
parameter->location(),
"Invalid type, expected " +
returnType->toString(false) +
returnType->humanReadableName() +
" but got " +
parameter->annotation().type->toString() +
parameter->annotation().type->humanReadableName() +
"."
);
}
@ -1195,9 +1259,9 @@ void TypeChecker::endVisit(Return const& _return)
5992_error,
_return.expression()->location(),
"Return argument type " +
type(*_return.expression())->toString() +
type(*_return.expression())->humanReadableName() +
" is not implicitly convertible to expected type " +
TupleType(returnTypes).toString(false) + ".",
TupleType(returnTypes).humanReadableName() + ".",
result.message()
);
}
@ -1213,9 +1277,9 @@ void TypeChecker::endVisit(Return const& _return)
6359_error,
_return.expression()->location(),
"Return argument type " +
type(*_return.expression())->toString() +
type(*_return.expression())->humanReadableName() +
" is not implicitly convertible to expected type (type of first return variable) " +
expected->toString() + ".",
expected->humanReadableName() + ".",
result.message()
);
}
@ -1319,9 +1383,9 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (!result)
{
auto errorMsg = "Type " +
valueComponentType->toString() +
valueComponentType->humanReadableName() +
" is not implicitly convertible to expected type " +
var.annotation().type->toString();
var.annotation().type->humanReadableName();
if (
valueComponentType->category() == Type::Category::RationalNumber &&
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
@ -1340,7 +1404,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
_statement.location(),
errorMsg +
". Try converting to type " +
valueComponentType->mobileType()->toString() +
valueComponentType->mobileType()->humanReadableName() +
" or use an explicit conversion."
);
}
@ -1423,9 +1487,9 @@ bool TypeChecker::visit(Conditional const& _conditional)
1080_error,
_conditional.location(),
"True expression's type " +
trueType->toString() +
trueType->humanReadableName() +
" does not match false expression's type " +
falseType->toString() +
falseType->humanReadableName() +
"."
);
// even we can't find a common type, we have to set a type here,
@ -1535,9 +1599,10 @@ bool TypeChecker::visit(Assignment const& _assignment)
"Operator " +
string(TokenTraits::toString(_assignment.assignmentOperator())) +
" not compatible with types " +
t->toString() +
t->humanReadableName() +
" and " +
type(_assignment.rightHandSide())->toString()
type(_assignment.rightHandSide())->humanReadableName() +
"."
);
}
return false;
@ -1567,7 +1632,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
if (components.size() == 1)
_tuple.annotation().type = type(*components[0]);
else
_tuple.annotation().type = TypeProvider::tuple(move(types));
_tuple.annotation().type = TypeProvider::tuple(std::move(types));
// If some of the components are not LValues, the error is reported above.
_tuple.annotation().isLValue = true;
_tuple.annotation().isPure = false;
@ -1628,7 +1693,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
m_errorReporter.fatalTypeError(
1545_error,
_tuple.location(),
"Type " + inlineArrayType->toString(true) + " is only valid in storage."
"Type " + inlineArrayType->humanReadableName() + " is only valid in storage."
);
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
@ -1638,7 +1703,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
if (components.size() == 1)
_tuple.annotation().type = type(*components[0]);
else
_tuple.annotation().type = TypeProvider::tuple(move(types));
_tuple.annotation().type = TypeProvider::tuple(std::move(types));
}
_tuple.annotation().isLValue = false;
@ -1660,9 +1725,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation)
TypeResult result = subExprType->unaryOperatorResult(op);
if (!result)
{
string description = "Unary operator " + string(TokenTraits::toString(op)) + " cannot be applied to type " + subExprType->toString();
if (!result.message().empty())
description += ". " + result.message();
string description = "Unary operator " + string(TokenTraits::toString(op)) + " cannot be applied to type " + subExprType->humanReadableName() + "." + (!result.message().empty() ? " " + result.message() : "");
if (modifying)
// Cannot just report the error, ignore the unary operator, and continue,
// because the sub-expression was already processed with requireLValue()
@ -1694,10 +1757,10 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
"Operator " +
string(TokenTraits::toString(_operation.getOperator())) +
" not compatible with types " +
leftType->toString() +
leftType->humanReadableName() +
" and " +
rightType->toString() +
(!result.message().empty() ? ". " + result.message() : "")
rightType->humanReadableName() + "." +
(!result.message().empty() ? " " + result.message() : "")
);
commonType = leftType;
}
@ -1738,9 +1801,9 @@ void TypeChecker::endVisit(BinaryOperation const& _operation)
"The result type of the " +
operation +
" operation is equal to the type of the first operand (" +
commonType->toString() +
commonType->humanReadableName() +
") ignoring the (larger) type of the second operand (" +
rightType->toString() +
rightType->humanReadableName() +
") which might be unexpected. Silence this warning by either converting "
"the first or the second operand to the type of the other."
);
@ -1836,7 +1899,7 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
_functionCall.location(),
ssl,
"Explicit type conversion not allowed from non-payable \"address\" to \"" +
resultType->toString() +
resultType->humanReadableName() +
"\", which has a payable fallback function."
);
}
@ -1850,9 +1913,9 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
5030_error,
_functionCall.location(),
"Explicit type conversion not allowed from \"" +
argType->toString() +
argType->humanReadableName() +
"\" to \"" +
resultType->toString() +
resultType->humanReadableName() +
"\". To obtain the address of the contract of the function, " +
"you can use the .address member of the function."
);
@ -1861,9 +1924,9 @@ Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType(
9640_error,
_functionCall.location(),
"Explicit type conversion not allowed from \"" +
argType->toString() +
argType->humanReadableName() +
"\" to \"" +
resultType->toString() +
resultType->humanReadableName() +
"\".",
result.message()
);
@ -2121,7 +2184,7 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
5511_error,
arguments.front()->location(),
"Expected first argument to be a function pointer, not \"" +
type(*arguments.front())->toString() +
type(*arguments.front())->humanReadableName() +
"\"."
);
return;
@ -2133,14 +2196,28 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
)
{
string msg = "Expected regular external function type, or external view on public function.";
if (externalFunctionType->kind() == FunctionType::Kind::Internal)
msg += " Provided internal function.";
else if (externalFunctionType->kind() == FunctionType::Kind::DelegateCall)
msg += " Cannot use library functions for abi.encodeCall.";
else if (externalFunctionType->kind() == FunctionType::Kind::Creation)
msg += " Provided creation function.";
else
msg += " Cannot use special function.";
switch (externalFunctionType->kind())
{
case FunctionType::Kind::Internal:
msg += " Provided internal function.";
break;
case FunctionType::Kind::DelegateCall:
msg += " Cannot use library functions for abi.encodeCall.";
break;
case FunctionType::Kind::Creation:
msg += " Provided creation function.";
break;
case FunctionType::Kind::Event:
msg += " Cannot use events for abi.encodeCall.";
break;
case FunctionType::Kind::Error:
msg += " Cannot use errors for abi.encodeCall.";
break;
default:
msg += " Cannot use special function.";
}
SecondarySourceLocation ssl{};
if (externalFunctionType->hasDeclaration())
@ -2151,10 +2228,14 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
externalFunctionType->declaration().scope() == m_currentContract
)
msg += " Did you forget to prefix \"this.\"?";
else if (util::contains(
m_currentContract->annotation().linearizedBaseContracts,
externalFunctionType->declaration().scope()
) && externalFunctionType->declaration().scope() != m_currentContract)
else if (
m_currentContract &&
externalFunctionType->declaration().scope() != m_currentContract &&
util::contains(
m_currentContract->annotation().linearizedBaseContracts,
externalFunctionType->declaration().scope()
)
)
msg += " Functions from base contracts have to be external.";
}
@ -2168,8 +2249,17 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
auto const* tupleType = dynamic_cast<TupleType const*>(type(*arguments[1]));
if (tupleType)
{
auto const& argumentTuple = dynamic_cast<TupleExpression const&>(*arguments[1].get());
callArguments = decltype(callArguments){argumentTuple.components().begin(), argumentTuple.components().end()};
if (TupleExpression const* argumentTuple = dynamic_cast<TupleExpression const*>(arguments[1].get()))
callArguments = decltype(callArguments){argumentTuple->components().begin(), argumentTuple->components().end()};
else
{
m_errorReporter.typeError(
9062_error,
arguments[1]->location(),
"Expected an inline tuple, not an expression of a tuple type."
);
return;
}
}
else
callArguments.push_back(arguments[1]);
@ -2210,9 +2300,9 @@ void TypeChecker::typeCheckABIEncodeCallFunction(FunctionCall const& _functionCa
"Cannot implicitly convert component at position " +
to_string(i) +
" from \"" +
argType.toString() +
argType.humanReadableName() +
"\" to \"" +
externalFunctionType->parameterTypes()[i]->toString() +
externalFunctionType->parameterTypes()[i]->humanReadableName() +
"\"" +
(result.message().empty() ? "." : ": " + result.message())
);
@ -2272,7 +2362,7 @@ void TypeChecker::typeCheckBytesConcatFunction(
argument->location(),
"Invalid type for argument in the bytes.concat function call. "
"bytes or fixed bytes type is required, but " +
argumentType->toString(true) + " provided."
argumentType->humanReadableName() + " provided."
);
}
}
@ -2457,9 +2547,9 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
string msg =
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
type(*paramArgMap[i])->toString() +
type(*paramArgMap[i])->humanReadableName() +
" to " +
parameterTypes[i]->toString() +
parameterTypes[i]->humanReadableName() +
" requested.";
if (!result.message().empty())
msg += " " + result.message();
@ -2521,7 +2611,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
m_errorReporter.typeError(
2443_error,
paramArgMap[i]->location(),
"The type of this parameter, " + parameterTypes[i]->toString(true) + ", "
"The type of this parameter, " + parameterTypes[i]->humanReadableName() + ", "
"is only supported in ABI coder v2. "
"Use \"pragma abicoder v2;\" to enable the feature."
);
@ -2535,7 +2625,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
m_errorReporter.typeError(
2428_error,
_functionCall.location(),
"The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->toString(true) + ", "
"The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->humanReadableName() + ", "
"is only supported in ABI coder v2. "
"Use \"pragma abicoder v2;\" to enable the feature."
);
@ -2715,8 +2805,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
funcCallAnno.type = returnTypes.size() == 1 ?
move(returnTypes.front()) :
TypeProvider::tuple(move(returnTypes));
std::move(returnTypes.front()) :
TypeProvider::tuple(std::move(returnTypes));
break;
}
@ -2834,7 +2924,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions)
_functionCallOptions.location(),
kind == FunctionType::Kind::Creation ?
"Cannot set option \"value\", since the constructor of " +
expressionFunctionType->returnParameterTypes().front()->toString() +
expressionFunctionType->returnParameterTypes().front()->humanReadableName() +
" is not payable." :
"Cannot set option \"value\" on a non-payable function type."
);
@ -2974,14 +3064,14 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
4994_error,
_memberAccess.location(),
"Member \"" + memberName + "\" is not available in " +
exprType->toString() +
exprType->humanReadableName() +
" outside of storage."
);
}
auto [errorId, description] = [&]() -> tuple<ErrorId, string> {
string errorMsg = "Member \"" + memberName + "\" not found or not visible "
"after argument-dependent lookup in " + exprType->toString() + ".";
"after argument-dependent lookup in " + exprType->humanReadableName() + ".";
if (auto const* funType = dynamic_cast<FunctionType const*>(exprType))
{
@ -2992,7 +3082,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
if (funType->kind() == FunctionType::Kind::Creation)
return {
8827_error,
"Constructor for " + t.front()->toString() + " must be payable for member \"value\" to be available."
"Constructor for " + t.front()->humanReadableName() + " must be payable for member \"value\" to be available."
};
else if (
funType->kind() == FunctionType::Kind::DelegateCall ||
@ -3031,7 +3121,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"Expected address not-payable as members were not found"
);
return { 9862_error, "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->toString() + "\"." };
return { 9862_error, "\"send\" and \"transfer\" are only available for objects of type \"address payable\", not \"" + exprType->humanReadableName() + "\"." };
}
}
@ -3049,7 +3139,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
6675_error,
_memberAccess.location(),
"Member \"" + memberName + "\" not unique "
"after argument-dependent lookup in " + exprType->toString() +
"after argument-dependent lookup in " + exprType->humanReadableName() +
(memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".")
);
@ -3063,7 +3153,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
solAssert(
!funType->bound() || exprType->isImplicitlyConvertibleTo(*funType->selfType()),
"Function \"" + memberName + "\" cannot be called on an object of type " +
exprType->toString() + " (expected " + funType->selfType()->toString() + ")."
exprType->humanReadableName() + " (expected " + funType->selfType()->humanReadableName() + ")."
);
if (
@ -3161,6 +3251,13 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
annotation.isPure = isPure;
}
if (
auto const* varDecl = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration);
!annotation.isPure.set() &&
varDecl &&
varDecl->isConstant()
)
annotation.isPure = true;
if (auto magicType = dynamic_cast<MagicType const*>(exprType))
{
@ -3328,7 +3425,7 @@ bool TypeChecker::visit(IndexAccess const& _access)
m_errorReporter.fatalTypeError(
2614_error,
_access.baseExpression().location(),
"Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")"
"Indexed expression has to be a type, mapping or array (is " + baseType->humanReadableName() + ")"
);
}
_access.annotation().type = resultType;
@ -3483,7 +3580,7 @@ bool TypeChecker::visit(Identifier const& _identifier)
// Try to re-construct function definition
string description;
for (auto const& param: declaration->functionType(true)->parameterTypes())
description += (description.empty() ? "" : ", ") + param->toString(false);
description += (description.empty() ? "" : ", ") + param->humanReadableName();
description = "function " + _identifier.name() + "(" + description + ")";
ssl.append("Candidate: " + description, declaration->location());
@ -3704,7 +3801,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
path->location(),
"The function \"" + joinHumanReadable(path->path(), ".") + "\" " +
"does not have any parameters, and therefore cannot be bound to the type \"" +
(normalizedType ? normalizedType->toString(true) : "*") + "\"."
(normalizedType ? normalizedType->humanReadableName() : "*") + "\"."
);
FunctionType const* functionType = dynamic_cast<FunctionType const&>(*functionDefinition.type()).asBoundFunction();
@ -3717,9 +3814,9 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
3100_error,
path->location(),
"The function \"" + joinHumanReadable(path->path(), ".") + "\" "+
"cannot be bound to the type \"" + _usingFor.typeName()->annotation().type->toString() +
"cannot be bound to the type \"" + _usingFor.typeName()->annotation().type->humanReadableName() +
"\" because the type cannot be implicitly converted to the first argument" +
" of the function (\"" + functionType->selfType()->toString() + "\")" +
" of the function (\"" + functionType->selfType()->humanReadableName() + "\")" +
(
result.message().empty() ?
"." :
@ -3774,9 +3871,9 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
if (!result)
{
auto errorMsg = "Type " +
type(_expression)->toString() +
type(_expression)->humanReadableName() +
" is not implicitly convertible to expected type " +
_expectedType.toString();
_expectedType.humanReadableName();
if (
type(_expression)->category() == Type::Category::RationalNumber &&
dynamic_cast<RationalNumberType const*>(type(_expression))->isFractional() &&
@ -3795,7 +3892,7 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
_expression.location(),
errorMsg +
". Try converting to type " +
type(_expression)->mobileType()->toString() +
type(_expression)->mobileType()->humanReadableName() +
" or use an explicit conversion.",
result.message()
);

View File

@ -29,6 +29,8 @@
#include <libsolidity/ast/TypeProvider.h>
#include <libsolutil/Keccak256.h>
#include <range/v3/view/tail.hpp>
#include <boost/algorithm/string.hpp>
#include <functional>
@ -38,6 +40,17 @@ using namespace std;
using namespace solidity;
using namespace solidity::frontend;
namespace
{
TryCatchClause const* findClause(vector<ASTPointer<TryCatchClause>> const& _clauses, optional<string> _errorName = {})
{
for (auto const& clause: ranges::views::tail(_clauses))
if (_errorName.has_value() ? clause->errorName() == _errorName : clause->errorName().empty())
return clause.get();
return nullptr;
}
}
ASTNode::ASTNode(int64_t _id, SourceLocation _location):
m_id(static_cast<size_t>(_id)),
m_location(std::move(_location))
@ -239,7 +252,7 @@ vector<ErrorDefinition const*> ContractDefinition::interfaceErrors(bool _require
result +=
(*annotation().creationCallGraph)->usedErrors +
(*annotation().deployedCallGraph)->usedErrors;
return util::convertContainer<vector<ErrorDefinition const*>>(move(result));
return util::convertContainer<vector<ErrorDefinition const*>>(std::move(result));
}
vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::interfaceFunctionList(bool _includeInheritedFunctions) const
@ -981,26 +994,14 @@ TryCatchClause const* TryStatement::successClause() const
return m_clauses[0].get();
}
TryCatchClause const* TryStatement::panicClause() const
{
for (size_t i = 1; i < m_clauses.size(); ++i)
if (m_clauses[i]->errorName() == "Panic")
return m_clauses[i].get();
return nullptr;
TryCatchClause const* TryStatement::panicClause() const {
return findClause(m_clauses, "Panic");
}
TryCatchClause const* TryStatement::errorClause() const
{
for (size_t i = 1; i < m_clauses.size(); ++i)
if (m_clauses[i]->errorName() == "Error")
return m_clauses[i].get();
return nullptr;
TryCatchClause const* TryStatement::errorClause() const {
return findClause(m_clauses, "Error");
}
TryCatchClause const* TryStatement::fallbackClause() const
{
for (size_t i = 1; i < m_clauses.size(); ++i)
if (m_clauses[i]->errorName().empty())
return m_clauses[i].get();
return nullptr;
TryCatchClause const* TryStatement::fallbackClause() const {
return findClause(m_clauses);
}

View File

@ -358,7 +358,7 @@ public:
):
Declaration(_id, _location, _unitAlias, std::move(_unitAliasLocation)),
m_path(std::move(_path)),
m_symbolAliases(move(_symbolAliases))
m_symbolAliases(std::move(_symbolAliases))
{ }
void accept(ASTVisitor& _visitor) override;
@ -587,10 +587,19 @@ private:
class IdentifierPath: public ASTNode
{
public:
IdentifierPath(int64_t _id, SourceLocation const& _location, std::vector<ASTString> _path):
ASTNode(_id, _location), m_path(std::move(_path)) {}
IdentifierPath(
int64_t _id,
SourceLocation const& _location,
std::vector<ASTString> _path,
std::vector<SourceLocation> _pathLocations
):
ASTNode(_id, _location), m_path(std::move(_path)), m_pathLocations(std::move(_pathLocations))
{
solAssert(m_pathLocations.size() == m_path.size());
}
std::vector<ASTString> const& path() const { return m_path; }
std::vector<SourceLocation > const& pathLocations() const { return m_pathLocations; }
IdentifierPathAnnotation& annotation() const override
{
return initAnnotation<IdentifierPathAnnotation>();
@ -600,6 +609,8 @@ public:
void accept(ASTConstVisitor& _visitor) const override;
private:
std::vector<ASTString> m_path;
// Corresponding locations for m_path. Array has same length and indices as m_path.
std::vector<SourceLocation> m_pathLocations;
};
class InheritanceSpecifier: public ASTNode
@ -1492,7 +1503,7 @@ public:
):
Statement(_id, _location, _docString),
m_dialect(_dialect),
m_flags(move(_flags)),
m_flags(std::move(_flags)),
m_operations(std::move(_operations))
{}
void accept(ASTVisitor& _visitor) override;
@ -2094,9 +2105,14 @@ public:
SourceLocation const& _location,
ASTPointer<Expression> _expression,
std::vector<ASTPointer<Expression>> _arguments,
std::vector<ASTPointer<ASTString>> _names
std::vector<ASTPointer<ASTString>> _names,
std::vector<SourceLocation> _nameLocations
):
Expression(_id, _location), m_expression(std::move(_expression)), m_arguments(std::move(_arguments)), m_names(std::move(_names)) {}
Expression(_id, _location), m_expression(std::move(_expression)), m_arguments(std::move(_arguments)), m_names(std::move(_names)), m_nameLocations(std::move(_nameLocations))
{
solAssert(m_nameLocations.size() == m_names.size());
}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -2109,6 +2125,7 @@ public:
/// in the order they were written.
/// If this is not a named call, this is empty.
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
std::vector<SourceLocation> const& nameLocations() const { return m_nameLocations; }
FunctionCallAnnotation& annotation() const override;
@ -2116,6 +2133,7 @@ private:
ASTPointer<Expression> m_expression;
std::vector<ASTPointer<Expression>> m_arguments;
std::vector<ASTPointer<ASTString>> m_names;
std::vector<SourceLocation> m_nameLocations;
};
/**
@ -2179,19 +2197,27 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> _expression,
ASTPointer<ASTString> _memberName
ASTPointer<ASTString> _memberName,
SourceLocation _memberLocation
):
Expression(_id, _location), m_expression(std::move(_expression)), m_memberName(std::move(_memberName)) {}
Expression(_id, _location),
m_expression(std::move(_expression)),
m_memberName(std::move(_memberName)),
m_memberLocation(std::move(_memberLocation))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
ASTString const& memberName() const { return *m_memberName; }
SourceLocation const& memberLocation() const { return m_memberLocation; }
MemberAccessAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_expression;
ASTPointer<ASTString> m_memberName;
SourceLocation m_memberLocation;
};
/**

View File

@ -256,6 +256,9 @@ struct IdentifierPathAnnotation: ASTAnnotation
Declaration const* referencedDeclaration = nullptr;
/// What kind of lookup needs to be done (static, virtual, super) find the declaration.
util::SetOnce<VirtualLookup> requiredLookup;
/// Declaration of each path element.
std::vector<Declaration const*> pathDeclarations;
};
struct ExpressionAnnotation: ASTAnnotation

View File

@ -20,7 +20,7 @@
* Converts the AST into json format
*/
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libsolidity/ast/ASTJsonExporter.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
@ -37,8 +37,6 @@
#include <boost/algorithm/string/join.hpp>
#include <range/v3/algorithm/sort.hpp>
#include <utility>
#include <vector>
#include <algorithm>
@ -74,27 +72,27 @@ void addIfSet(std::vector<pair<string, Json::Value>>& _attributes, string const&
namespace solidity::frontend
{
ASTJsonConverter::ASTJsonConverter(CompilerStack::State _stackState, map<string, unsigned> _sourceIndices):
ASTJsonExporter::ASTJsonExporter(CompilerStack::State _stackState, map<string, unsigned> _sourceIndices):
m_stackState(_stackState),
m_sourceIndices(std::move(_sourceIndices))
{
}
void ASTJsonConverter::setJsonNode(
void ASTJsonExporter::setJsonNode(
ASTNode const& _node,
string const& _nodeName,
initializer_list<pair<string, Json::Value>>&& _attributes
)
{
ASTJsonConverter::setJsonNode(
ASTJsonExporter::setJsonNode(
_node,
_nodeName,
std::vector<pair<string, Json::Value>>(std::move(_attributes))
);
}
void ASTJsonConverter::setJsonNode(
void ASTJsonExporter::setJsonNode(
ASTNode const& _node,
string const& _nodeType,
std::vector<pair<string, Json::Value>>&& _attributes
@ -111,7 +109,7 @@ void ASTJsonConverter::setJsonNode(
m_currentValue[e.first] = std::move(e.second);
}
optional<size_t> ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const
optional<size_t> ASTJsonExporter::sourceIndexFromLocation(SourceLocation const& _location) const
{
if (_location.sourceName && m_sourceIndices.count(*_location.sourceName))
return m_sourceIndices.at(*_location.sourceName);
@ -119,7 +117,7 @@ optional<size_t> ASTJsonConverter::sourceIndexFromLocation(SourceLocation const&
return nullopt;
}
string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const
string ASTJsonExporter::sourceLocationToString(SourceLocation const& _location) const
{
optional<size_t> sourceIndexOpt = sourceIndexFromLocation(_location);
int length = -1;
@ -128,20 +126,30 @@ string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location)
return to_string(_location.start) + ":" + to_string(length) + ":" + (sourceIndexOpt.has_value() ? to_string(sourceIndexOpt.value()) : "-1");
}
string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePath)
Json::Value ASTJsonExporter::sourceLocationsToJson(vector<SourceLocation> const& _sourceLocations) const
{
Json::Value locations = Json::arrayValue;
for (SourceLocation const& location: _sourceLocations)
locations.append(sourceLocationToString(location));
return locations;
}
string ASTJsonExporter::namePathToString(std::vector<ASTString> const& _namePath)
{
return boost::algorithm::join(_namePath, ".");
}
Json::Value ASTJsonConverter::typePointerToJson(Type const* _tp, bool _short)
Json::Value ASTJsonExporter::typePointerToJson(Type const* _tp, bool _withoutDataLocation)
{
Json::Value typeDescriptions(Json::objectValue);
typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString(_short)) : Json::nullValue;
typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString(_withoutDataLocation)) : Json::nullValue;
typeDescriptions["typeIdentifier"] = _tp ? Json::Value(_tp->identifier()) : Json::nullValue;
return typeDescriptions;
}
Json::Value ASTJsonConverter::typePointerToJson(std::optional<FuncCallArguments> const& _tps)
Json::Value ASTJsonExporter::typePointerToJson(std::optional<FuncCallArguments> const& _tps)
{
if (_tps)
{
@ -154,7 +162,7 @@ Json::Value ASTJsonConverter::typePointerToJson(std::optional<FuncCallArguments>
return Json::nullValue;
}
void ASTJsonConverter::appendExpressionAttributes(
void ASTJsonExporter::appendExpressionAttributes(
std::vector<pair<string, Json::Value>>& _attributes,
ExpressionAnnotation const& _annotation
)
@ -174,7 +182,7 @@ void ASTJsonConverter::appendExpressionAttributes(
_attributes += exprAttributes;
}
Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const
Json::Value ASTJsonExporter::inlineAssemblyIdentifierToJson(pair<yul::Identifier const*, InlineAssemblyAnnotation::ExternalIdentifierInfo> _info) const
{
Json::Value tuple(Json::objectValue);
tuple["src"] = sourceLocationToString(nativeLocationOf(*_info.first));
@ -190,18 +198,18 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair<yul::Identifie
return tuple;
}
void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node, util::JsonFormat const& _format)
void ASTJsonExporter::print(ostream& _stream, ASTNode const& _node, util::JsonFormat const& _format)
{
_stream << util::jsonPrint(toJson(_node), _format);
}
Json::Value ASTJsonConverter::toJson(ASTNode const& _node)
Json::Value ASTJsonExporter::toJson(ASTNode const& _node)
{
_node.accept(*this);
return util::removeNullMembers(std::move(m_currentValue));
}
bool ASTJsonConverter::visit(SourceUnit const& _node)
bool ASTJsonExporter::visit(SourceUnit const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue),
@ -228,7 +236,7 @@ bool ASTJsonConverter::visit(SourceUnit const& _node)
return false;
}
bool ASTJsonConverter::visit(PragmaDirective const& _node)
bool ASTJsonExporter::visit(PragmaDirective const& _node)
{
Json::Value literals(Json::arrayValue);
for (auto const& literal: _node.literals())
@ -239,7 +247,7 @@ bool ASTJsonConverter::visit(PragmaDirective const& _node)
return false;
}
bool ASTJsonConverter::visit(ImportDirective const& _node)
bool ASTJsonExporter::visit(ImportDirective const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("file", _node.path()),
@ -267,7 +275,7 @@ bool ASTJsonConverter::visit(ImportDirective const& _node)
return false;
}
bool ASTJsonConverter::visit(ContractDefinition const& _node)
bool ASTJsonExporter::visit(ContractDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
@ -292,16 +300,22 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(IdentifierPath const& _node)
bool ASTJsonExporter::visit(IdentifierPath const& _node)
{
Json::Value nameLocations = Json::arrayValue;
for (SourceLocation location: _node.pathLocations())
nameLocations.append(sourceLocationToString(location));
setJsonNode(_node, "IdentifierPath", {
make_pair("name", namePathToString(_node.path())),
make_pair("nameLocations", nameLocations),
make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration))
});
return false;
}
bool ASTJsonConverter::visit(InheritanceSpecifier const& _node)
bool ASTJsonExporter::visit(InheritanceSpecifier const& _node)
{
setJsonNode(_node, "InheritanceSpecifier", {
make_pair("baseName", toJson(_node.name())),
@ -310,7 +324,7 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node)
return false;
}
bool ASTJsonConverter::visit(UsingForDirective const& _node)
bool ASTJsonExporter::visit(UsingForDirective const& _node)
{
vector<pair<string, Json::Value>> attributes = {
make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue)
@ -322,20 +336,20 @@ bool ASTJsonConverter::visit(UsingForDirective const& _node)
{
Json::Value functionNode;
functionNode["function"] = toJson(*function);
functionList.append(move(functionNode));
functionList.append(std::move(functionNode));
}
attributes.emplace_back("functionList", move(functionList));
attributes.emplace_back("functionList", std::move(functionList));
}
else
attributes.emplace_back("libraryName", toJson(*_node.functionsOrLibrary().front()));
attributes.emplace_back("global", _node.global());
setJsonNode(_node, "UsingForDirective", move(attributes));
setJsonNode(_node, "UsingForDirective", std::move(attributes));
return false;
}
bool ASTJsonConverter::visit(StructDefinition const& _node)
bool ASTJsonExporter::visit(StructDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
@ -352,7 +366,7 @@ bool ASTJsonConverter::visit(StructDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(EnumDefinition const& _node)
bool ASTJsonExporter::visit(EnumDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
@ -367,7 +381,7 @@ bool ASTJsonConverter::visit(EnumDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(EnumValue const& _node)
bool ASTJsonExporter::visit(EnumValue const& _node)
{
setJsonNode(_node, "EnumValue", {
make_pair("name", _node.name()),
@ -376,7 +390,7 @@ bool ASTJsonConverter::visit(EnumValue const& _node)
return false;
}
bool ASTJsonConverter::visit(UserDefinedValueTypeDefinition const& _node)
bool ASTJsonExporter::visit(UserDefinedValueTypeDefinition const& _node)
{
solAssert(_node.underlyingType(), "");
std::vector<pair<string, Json::Value>> attributes = {
@ -391,7 +405,7 @@ bool ASTJsonConverter::visit(UserDefinedValueTypeDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(ParameterList const& _node)
bool ASTJsonExporter::visit(ParameterList const& _node)
{
setJsonNode(_node, "ParameterList", {
make_pair("parameters", toJson(_node.parameters()))
@ -399,7 +413,7 @@ bool ASTJsonConverter::visit(ParameterList const& _node)
return false;
}
bool ASTJsonConverter::visit(OverrideSpecifier const& _node)
bool ASTJsonExporter::visit(OverrideSpecifier const& _node)
{
setJsonNode(_node, "OverrideSpecifier", {
make_pair("overrides", toJson(_node.overrides()))
@ -407,7 +421,7 @@ bool ASTJsonConverter::visit(OverrideSpecifier const& _node)
return false;
}
bool ASTJsonConverter::visit(FunctionDefinition const& _node)
bool ASTJsonExporter::visit(FunctionDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
@ -445,7 +459,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
bool ASTJsonExporter::visit(VariableDeclaration const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
@ -473,7 +487,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
return false;
}
bool ASTJsonConverter::visit(ModifierDefinition const& _node)
bool ASTJsonExporter::visit(ModifierDefinition const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
@ -491,7 +505,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(ModifierInvocation const& _node)
bool ASTJsonExporter::visit(ModifierInvocation const& _node)
{
std::vector<pair<string, Json::Value>> attributes{
make_pair("modifierName", toJson(_node.name())),
@ -504,11 +518,11 @@ bool ASTJsonConverter::visit(ModifierInvocation const& _node)
else if (dynamic_cast<ContractDefinition const*>(declaration))
attributes.emplace_back("kind", "baseConstructorSpecifier");
}
setJsonNode(_node, "ModifierInvocation", move(attributes));
setJsonNode(_node, "ModifierInvocation", std::move(attributes));
return false;
}
bool ASTJsonConverter::visit(EventDefinition const& _node)
bool ASTJsonExporter::visit(EventDefinition const& _node)
{
m_inEvent = true;
std::vector<pair<string, Json::Value>> _attributes = {
@ -529,7 +543,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(ErrorDefinition const& _node)
bool ASTJsonExporter::visit(ErrorDefinition const& _node)
{
std::vector<pair<string, Json::Value>> _attributes = {
make_pair("name", _node.name()),
@ -544,7 +558,7 @@ bool ASTJsonConverter::visit(ErrorDefinition const& _node)
return false;
}
bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
bool ASTJsonExporter::visit(ElementaryTypeName const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.typeName().toString()),
@ -558,7 +572,7 @@ bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
return false;
}
bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
bool ASTJsonExporter::visit(UserDefinedTypeName const& _node)
{
setJsonNode(_node, "UserDefinedTypeName", {
make_pair("pathNode", toJson(_node.pathNode())),
@ -568,7 +582,7 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
return false;
}
bool ASTJsonConverter::visit(FunctionTypeName const& _node)
bool ASTJsonExporter::visit(FunctionTypeName const& _node)
{
setJsonNode(_node, "FunctionTypeName", {
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
@ -580,7 +594,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node)
return false;
}
bool ASTJsonConverter::visit(Mapping const& _node)
bool ASTJsonExporter::visit(Mapping const& _node)
{
setJsonNode(_node, "Mapping", {
make_pair("keyType", toJson(_node.keyType())),
@ -590,7 +604,7 @@ bool ASTJsonConverter::visit(Mapping const& _node)
return false;
}
bool ASTJsonConverter::visit(ArrayTypeName const& _node)
bool ASTJsonExporter::visit(ArrayTypeName const& _node)
{
setJsonNode(_node, "ArrayTypeName", {
make_pair("baseType", toJson(_node.baseType())),
@ -600,7 +614,7 @@ bool ASTJsonConverter::visit(ArrayTypeName const& _node)
return false;
}
bool ASTJsonConverter::visit(InlineAssembly const& _node)
bool ASTJsonExporter::visit(InlineAssembly const& _node)
{
vector<pair<string, Json::Value>> externalReferences;
@ -613,7 +627,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
Json::Value externalReferencesJson = Json::arrayValue;
ranges::sort(externalReferences);
std::sort(externalReferences.begin(), externalReferences.end());
for (Json::Value& it: externalReferences | ranges::views::values)
externalReferencesJson.append(std::move(it));
@ -631,14 +645,14 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
flags.append(*flag);
else
flags.append(Json::nullValue);
attributes.emplace_back(make_pair("flags", move(flags)));
attributes.emplace_back(make_pair("flags", std::move(flags)));
}
setJsonNode(_node, "InlineAssembly", move(attributes));
setJsonNode(_node, "InlineAssembly", std::move(attributes));
return false;
}
bool ASTJsonConverter::visit(Block const& _node)
bool ASTJsonExporter::visit(Block const& _node)
{
setJsonNode(_node, _node.unchecked() ? "UncheckedBlock" : "Block", {
make_pair("statements", toJson(_node.statements()))
@ -646,13 +660,13 @@ bool ASTJsonConverter::visit(Block const& _node)
return false;
}
bool ASTJsonConverter::visit(PlaceholderStatement const& _node)
bool ASTJsonExporter::visit(PlaceholderStatement const& _node)
{
setJsonNode(_node, "PlaceholderStatement", {});
return false;
}
bool ASTJsonConverter::visit(IfStatement const& _node)
bool ASTJsonExporter::visit(IfStatement const& _node)
{
setJsonNode(_node, "IfStatement", {
make_pair("condition", toJson(_node.condition())),
@ -662,7 +676,7 @@ bool ASTJsonConverter::visit(IfStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(TryCatchClause const& _node)
bool ASTJsonExporter::visit(TryCatchClause const& _node)
{
setJsonNode(_node, "TryCatchClause", {
make_pair("errorName", _node.errorName()),
@ -672,7 +686,7 @@ bool ASTJsonConverter::visit(TryCatchClause const& _node)
return false;
}
bool ASTJsonConverter::visit(TryStatement const& _node)
bool ASTJsonExporter::visit(TryStatement const& _node)
{
setJsonNode(_node, "TryStatement", {
make_pair("externalCall", toJson(_node.externalCall())),
@ -681,7 +695,7 @@ bool ASTJsonConverter::visit(TryStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(WhileStatement const& _node)
bool ASTJsonExporter::visit(WhileStatement const& _node)
{
setJsonNode(
_node,
@ -694,7 +708,7 @@ bool ASTJsonConverter::visit(WhileStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(ForStatement const& _node)
bool ASTJsonExporter::visit(ForStatement const& _node)
{
setJsonNode(_node, "ForStatement", {
make_pair("initializationExpression", toJsonOrNull(_node.initializationExpression())),
@ -705,19 +719,19 @@ bool ASTJsonConverter::visit(ForStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(Continue const& _node)
bool ASTJsonExporter::visit(Continue const& _node)
{
setJsonNode(_node, "Continue", {});
return false;
}
bool ASTJsonConverter::visit(Break const& _node)
bool ASTJsonExporter::visit(Break const& _node)
{
setJsonNode(_node, "Break", {});
return false;
}
bool ASTJsonConverter::visit(Return const& _node)
bool ASTJsonExporter::visit(Return const& _node)
{
setJsonNode(_node, "Return", {
make_pair("expression", toJsonOrNull(_node.expression())),
@ -726,13 +740,13 @@ bool ASTJsonConverter::visit(Return const& _node)
return false;
}
bool ASTJsonConverter::visit(Throw const& _node)
bool ASTJsonExporter::visit(Throw const& _node)
{
setJsonNode(_node, "Throw", {});
return false;
}
bool ASTJsonConverter::visit(EmitStatement const& _node)
bool ASTJsonExporter::visit(EmitStatement const& _node)
{
setJsonNode(_node, "EmitStatement", {
make_pair("eventCall", toJson(_node.eventCall()))
@ -740,7 +754,7 @@ bool ASTJsonConverter::visit(EmitStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(RevertStatement const& _node)
bool ASTJsonExporter::visit(RevertStatement const& _node)
{
setJsonNode(_node, "RevertStatement", {
make_pair("errorCall", toJson(_node.errorCall()))
@ -748,7 +762,7 @@ bool ASTJsonConverter::visit(RevertStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
bool ASTJsonExporter::visit(VariableDeclarationStatement const& _node)
{
Json::Value varDecs(Json::arrayValue);
for (auto const& v: _node.declarations())
@ -761,7 +775,7 @@ bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(ExpressionStatement const& _node)
bool ASTJsonExporter::visit(ExpressionStatement const& _node)
{
setJsonNode(_node, "ExpressionStatement", {
make_pair("expression", toJson(_node.expression()))
@ -769,7 +783,7 @@ bool ASTJsonConverter::visit(ExpressionStatement const& _node)
return false;
}
bool ASTJsonConverter::visit(Conditional const& _node)
bool ASTJsonExporter::visit(Conditional const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("condition", toJson(_node.condition())),
@ -781,7 +795,7 @@ bool ASTJsonConverter::visit(Conditional const& _node)
return false;
}
bool ASTJsonConverter::visit(Assignment const& _node)
bool ASTJsonExporter::visit(Assignment const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("operator", TokenTraits::toString(_node.assignmentOperator())),
@ -793,7 +807,7 @@ bool ASTJsonConverter::visit(Assignment const& _node)
return false;
}
bool ASTJsonConverter::visit(TupleExpression const& _node)
bool ASTJsonExporter::visit(TupleExpression const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("isInlineArray", Json::Value(_node.isInlineArray())),
@ -804,7 +818,7 @@ bool ASTJsonConverter::visit(TupleExpression const& _node)
return false;
}
bool ASTJsonConverter::visit(UnaryOperation const& _node)
bool ASTJsonExporter::visit(UnaryOperation const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("prefix", _node.isPrefixOperation()),
@ -816,7 +830,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node)
return false;
}
bool ASTJsonConverter::visit(BinaryOperation const& _node)
bool ASTJsonExporter::visit(BinaryOperation const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("operator", TokenTraits::toString(_node.getOperator())),
@ -829,7 +843,7 @@ bool ASTJsonConverter::visit(BinaryOperation const& _node)
return false;
}
bool ASTJsonConverter::visit(FunctionCall const& _node)
bool ASTJsonExporter::visit(FunctionCall const& _node)
{
Json::Value names(Json::arrayValue);
for (auto const& name: _node.names())
@ -837,6 +851,7 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
std::vector<pair<string, Json::Value>> attributes = {
make_pair("expression", toJson(_node.expression())),
make_pair("names", std::move(names)),
make_pair("nameLocations", sourceLocationsToJson(_node.nameLocations())),
make_pair("arguments", toJson(_node.arguments())),
make_pair("tryCall", _node.annotation().tryCall)
};
@ -852,7 +867,7 @@ bool ASTJsonConverter::visit(FunctionCall const& _node)
return false;
}
bool ASTJsonConverter::visit(FunctionCallOptions const& _node)
bool ASTJsonExporter::visit(FunctionCallOptions const& _node)
{
Json::Value names(Json::arrayValue);
for (auto const& name: _node.names())
@ -869,7 +884,7 @@ bool ASTJsonConverter::visit(FunctionCallOptions const& _node)
return false;
}
bool ASTJsonConverter::visit(NewExpression const& _node)
bool ASTJsonExporter::visit(NewExpression const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("typeName", toJson(_node.typeName()))
@ -879,10 +894,11 @@ bool ASTJsonConverter::visit(NewExpression const& _node)
return false;
}
bool ASTJsonConverter::visit(MemberAccess const& _node)
bool ASTJsonExporter::visit(MemberAccess const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("memberName", _node.memberName()),
make_pair("memberLocation", Json::Value(sourceLocationToString(_node.memberLocation()))),
make_pair("expression", toJson(_node.expression())),
make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)),
};
@ -891,7 +907,7 @@ bool ASTJsonConverter::visit(MemberAccess const& _node)
return false;
}
bool ASTJsonConverter::visit(IndexAccess const& _node)
bool ASTJsonExporter::visit(IndexAccess const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("baseExpression", toJson(_node.baseExpression())),
@ -902,7 +918,7 @@ bool ASTJsonConverter::visit(IndexAccess const& _node)
return false;
}
bool ASTJsonConverter::visit(IndexRangeAccess const& _node)
bool ASTJsonExporter::visit(IndexRangeAccess const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("baseExpression", toJson(_node.baseExpression())),
@ -914,7 +930,7 @@ bool ASTJsonConverter::visit(IndexRangeAccess const& _node)
return false;
}
bool ASTJsonConverter::visit(Identifier const& _node)
bool ASTJsonExporter::visit(Identifier const& _node)
{
Json::Value overloads(Json::arrayValue);
for (auto const& dec: _node.annotation().overloadedDeclarations)
@ -929,7 +945,7 @@ bool ASTJsonConverter::visit(Identifier const& _node)
return false;
}
bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
bool ASTJsonExporter::visit(ElementaryTypeNameExpression const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("typeName", toJson(_node.type()))
@ -939,7 +955,7 @@ bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
return false;
}
bool ASTJsonConverter::visit(Literal const& _node)
bool ASTJsonExporter::visit(Literal const& _node)
{
Json::Value value{_node.value()};
if (!util::validateUTF8(_node.value()))
@ -961,7 +977,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
return false;
}
bool ASTJsonConverter::visit(StructuredDocumentation const& _node)
bool ASTJsonExporter::visit(StructuredDocumentation const& _node)
{
Json::Value text{*_node.text()};
std::vector<pair<string, Json::Value>> attributes = {
@ -973,12 +989,12 @@ bool ASTJsonConverter::visit(StructuredDocumentation const& _node)
void ASTJsonConverter::endVisit(EventDefinition const&)
void ASTJsonExporter::endVisit(EventDefinition const&)
{
m_inEvent = false;
}
string ASTJsonConverter::location(VariableDeclaration::Location _location)
string ASTJsonExporter::location(VariableDeclaration::Location _location)
{
switch (_location)
{
@ -995,7 +1011,7 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location)
return {};
}
string ASTJsonConverter::contractKind(ContractKind _kind)
string ASTJsonExporter::contractKind(ContractKind _kind)
{
switch (_kind)
{
@ -1011,7 +1027,7 @@ string ASTJsonConverter::contractKind(ContractKind _kind)
return {};
}
string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
string ASTJsonExporter::functionCallKind(FunctionCallKind _kind)
{
switch (_kind)
{
@ -1026,7 +1042,7 @@ string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
}
}
string ASTJsonConverter::literalTokenKind(Token _token)
string ASTJsonExporter::literalTokenKind(Token _token)
{
switch (_token)
{
@ -1046,12 +1062,12 @@ string ASTJsonConverter::literalTokenKind(Token _token)
}
}
string ASTJsonConverter::type(Expression const& _expression)
string ASTJsonExporter::type(Expression const& _expression)
{
return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
}
string ASTJsonConverter::type(VariableDeclaration const& _varDecl)
string ASTJsonExporter::type(VariableDeclaration const& _varDecl)
{
return _varDecl.annotation().type ? _varDecl.annotation().type->toString() : "Unknown";
}

View File

@ -48,13 +48,13 @@ namespace solidity::frontend
/**
* Converter of the AST into JSON format
*/
class ASTJsonConverter: public ASTConstVisitor
class ASTJsonExporter: public ASTConstVisitor
{
public:
/// Create a converter to JSON for the given abstract syntax tree.
/// @a _stackState state of the compiler stack to avoid outputting incomplete data
/// @a _sourceIndices is used to abbreviate source names in source locations.
explicit ASTJsonConverter(
explicit ASTJsonExporter(
CompilerStack::State _stackState,
std::map<std::string, unsigned> _sourceIndices = std::map<std::string, unsigned>()
);
@ -144,6 +144,7 @@ private:
/// Maps source location to an index, if source is valid and a mapping does exist, otherwise returns std::nullopt.
std::optional<size_t> sourceIndexFromLocation(langutil::SourceLocation const& _location) const;
std::string sourceLocationToString(langutil::SourceLocation const& _location) const;
Json::Value sourceLocationsToJson(std::vector<langutil::SourceLocation> const& _sourceLocations) const;
static std::string namePathToString(std::vector<ASTString> const& _namePath);
static Json::Value idOrNull(ASTNode const* _pt)
{
@ -183,7 +184,7 @@ private:
return json;
}
static Json::Value typePointerToJson(Type const* _tp, bool _short = false);
static Json::Value typePointerToJson(Type const* _tp, bool _withoutDataLocation = false);
static Json::Value typePointerToJson(std::optional<FuncCallArguments> const& _tps);
void appendExpressionAttributes(
std::vector<std::pair<std::string, Json::Value>> &_attributes,

View File

@ -95,6 +95,20 @@ SourceLocation const ASTJsonImporter::createSourceLocation(Json::Value const& _n
return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceNames);
}
optional<vector<SourceLocation>> ASTJsonImporter::createSourceLocations(Json::Value const& _node) const
{
vector<SourceLocation> locations;
if (_node.isMember("nameLocations") && _node["nameLocations"].isArray())
{
for (auto const& val: _node["nameLocations"])
locations.emplace_back(langutil::parseSourceLocation(val.asString(), m_sourceNames));
return locations;
}
return nullopt;
}
SourceLocation ASTJsonImporter::createNameSourceLocation(Json::Value const& _node)
{
astAssert(member(_node, "nameLocation").isString(), "'nameLocation' must be a string");
@ -229,6 +243,9 @@ ASTPointer<ASTNode> ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js
return createDocumentation(_json);
else
astAssert(false, "Unknown type of ASTNode: " + nodeType);
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
// ============ functions to instantiate the AST-Nodes from Json-Nodes ==============
@ -282,7 +299,7 @@ ASTPointer<ImportDirective> ASTJsonImporter::createImportDirective(Json::Value c
path,
unitAlias,
createNameSourceLocation(_node),
move(symbolAliases)
std::move(symbolAliases)
);
astAssert(_node["absolutePath"].isString(), "Expected 'absolutePath' to be a string!");
@ -322,6 +339,7 @@ ASTPointer<IdentifierPath> ASTJsonImporter::createIdentifierPath(Json::Value con
astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
vector<ASTString> namePath;
vector<SourceLocation> namePathLocations;
vector<string> strs;
string nameString = member(_node, "name").asString();
boost::algorithm::split(strs, nameString, boost::is_any_of("."));
@ -331,7 +349,23 @@ ASTPointer<IdentifierPath> ASTJsonImporter::createIdentifierPath(Json::Value con
astAssert(!s.empty(), "Expected non-empty string for IdentifierPath element.");
namePath.emplace_back(s);
}
return createASTNode<IdentifierPath>(_node, namePath);
if (_node.isMember("nameLocations") && _node["nameLocations"].isArray())
for (auto const& val: _node["nameLocations"])
namePathLocations.emplace_back(langutil::parseSourceLocation(val.asString(), m_sourceNames));
else
namePathLocations.resize(namePath.size());
astAssert(
namePath.size() == namePathLocations.size(),
"SourceLocations don't match name paths."
);
return createASTNode<IdentifierPath>(
_node,
namePath,
namePathLocations
);
}
ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Json::Value const& _node)
@ -357,7 +391,7 @@ ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Val
return createASTNode<UsingForDirective>(
_node,
move(functions),
std::move(functions),
!_node.isMember("libraryName"),
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
memberAsBool(_node, "global")
@ -652,7 +686,7 @@ ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json::Value con
_node,
nullOrASTString(_node, "documentation"),
dialect,
move(flags),
std::move(flags),
operations
);
}
@ -873,11 +907,17 @@ ASTPointer<FunctionCall> ASTJsonImporter::createFunctionCall(Json::Value const&
astAssert(name.isString(), "Expected 'names' members to be strings!");
names.push_back(make_shared<ASTString>(name.asString()));
}
optional<vector<SourceLocation>> sourceLocations = createSourceLocations(_node);
return createASTNode<FunctionCall>(
_node,
convertJsonToASTNode<Expression>(member(_node, "expression")),
arguments,
names
names,
sourceLocations ?
*sourceLocations :
vector<SourceLocation>(names.size())
);
}
@ -911,10 +951,15 @@ ASTPointer<NewExpression> ASTJsonImporter::createNewExpression(Json::Value const
ASTPointer<MemberAccess> ASTJsonImporter::createMemberAccess(Json::Value const& _node)
{
SourceLocation memberLocation;
if (member(_node, "memberLocation").isString())
memberLocation = solidity::langutil::parseSourceLocation(_node["memberLocation"].asString(), m_sourceNames);
return createASTNode<MemberAccess>(
_node,
convertJsonToASTNode<Expression>(member(_node, "expression")),
memberAsASTString(_node, "memberName")
memberAsASTString(_node, "memberName"),
std::move(memberLocation)
);
}
@ -1073,6 +1118,9 @@ Visibility ASTJsonImporter::visibility(Json::Value const& _node)
return Visibility::External;
else
astAssert(false, "Unknown visibility declaration");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
VariableDeclaration::Location ASTJsonImporter::location(Json::Value const& _node)
@ -1092,6 +1140,9 @@ VariableDeclaration::Location ASTJsonImporter::location(Json::Value const& _node
return VariableDeclaration::Location::CallData;
else
astAssert(false, "Unknown location declaration");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _node)
@ -1125,6 +1176,9 @@ Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _no
return Literal::SubDenomination::Year;
else
astAssert(false, "Unknown subdenomination");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
StateMutability ASTJsonImporter::stateMutability(Json::Value const& _node)
@ -1142,6 +1196,9 @@ StateMutability ASTJsonImporter::stateMutability(Json::Value const& _node)
return StateMutability::Payable;
else
astAssert(false, "Unknown stateMutability");
// FIXME: Workaround for spurious GCC 12.1 warning (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105794)
util::unreachable();
}
}

View File

@ -59,6 +59,7 @@ private:
ASTPointer<T> createASTNode(Json::Value const& _node, Args&&... _args);
/// @returns the sourceLocation-object created from the string in the JSON node
langutil::SourceLocation const createSourceLocation(Json::Value const& _node);
std::optional<std::vector<langutil::SourceLocation>> createSourceLocations(Json::Value const& _node) const;
/// Creates an ASTNode for a given JSON-ast of unknown type
/// @returns Pointer to a new created ASTNode
ASTPointer<ASTNode> convertJsonToASTNode(Json::Value const& _ast);

View File

@ -85,4 +85,14 @@ VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration cons
return rootDecl;
}
Expression const* resolveOuterUnaryTuples(Expression const* _expr)
{
while (auto const* tupleExpression = dynamic_cast<TupleExpression const*>(_expr))
if (tupleExpression->components().size() == 1)
_expr = tupleExpression->components().front().get();
else
break;
return _expr;
}
}

View File

@ -38,4 +38,8 @@ bool isConstantVariableRecursive(VariableDeclaration const& _varDecl);
/// Returns the innermost AST node that covers the given location or nullptr if not found.
ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit);
/// @returns @a _expr itself, in case it is not a unary tuple expression. Otherwise it descends recursively
/// into unary tuples and returns the contained expression.
Expression const* resolveOuterUnaryTuples(Expression const* _expr);
}

View File

@ -404,7 +404,7 @@ TupleType const* TypeProvider::tuple(vector<Type const*> members)
if (members.empty())
return &m_emptyTuple;
return createAndGet<TupleType>(move(members));
return createAndGet<TupleType>(std::move(members));
}
ReferenceType const* TypeProvider::withLocation(ReferenceType const* _type, DataLocation _location, bool _isPointer)

View File

@ -110,6 +110,14 @@ util::Result<TypePointers> transformParametersToExternal(TypePointers const& _pa
return transformed;
}
string toStringInParentheses(TypePointers const& _types, bool _withoutDataLocation)
{
return '(' + util::joinHumanReadable(
_types | ranges::views::transform([&](auto const* _type) { return _type->toString(_withoutDataLocation); }),
","
) + ')';
}
}
MemberList::Member::Member(Declaration const* _declaration, Type const* _type):
@ -117,7 +125,7 @@ MemberList::Member::Member(Declaration const* _declaration, Type const* _type):
{}
MemberList::Member::Member(Declaration const* _declaration, Type const* _type, string _name):
name(move(_name)),
name(std::move(_name)),
type(_type),
declaration(_declaration)
{
@ -297,7 +305,7 @@ MemberList const& Type::members(ASTNode const* _currentScope) const
MemberList::MemberMap members = nativeMembers(_currentScope);
if (_currentScope)
members += boundFunctions(*this, *_currentScope);
m_members[_currentScope] = make_unique<MemberList>(move(members));
m_members[_currentScope] = make_unique<MemberList>(std::move(members));
}
return *m_members[_currentScope];
}
@ -1785,7 +1793,7 @@ vector<tuple<string, Type const*>> ArrayType::makeStackItems() const
solAssert(false, "");
}
string ArrayType::toString(bool _short) const
string ArrayType::toString(bool _withoutDataLocation) const
{
string ret;
if (isString())
@ -1794,16 +1802,34 @@ string ArrayType::toString(bool _short) const
ret = "bytes";
else
{
ret = baseType()->toString(_short) + "[";
ret = baseType()->toString(_withoutDataLocation) + "[";
if (!isDynamicallySized())
ret += length().str();
ret += "]";
}
if (!_short)
if (!_withoutDataLocation)
ret += " " + stringForReferencePart();
return ret;
}
string ArrayType::humanReadableName() const
{
string ret;
if (isString())
ret = "string";
else if (isByteArrayOrString())
ret = "bytes";
else
{
ret = baseType()->toString(true) + "[";
if (!isDynamicallySized())
ret += length().str();
ret += "]";
}
ret += " " + stringForReferencePart();
return ret;
}
string ArrayType::canonicalName() const
{
string ret;
@ -1986,9 +2012,14 @@ bool ArraySliceType::operator==(Type const& _other) const
return false;
}
string ArraySliceType::toString(bool _short) const
string ArraySliceType::toString(bool _withoutDataLocation) const
{
return m_arrayType.toString(_short) + " slice";
return m_arrayType.toString(_withoutDataLocation) + " slice";
}
string ArraySliceType::humanReadableName() const
{
return m_arrayType.humanReadableName() + " slice";
}
Type const* ArraySliceType::mobileType() const
@ -2252,10 +2283,10 @@ bool StructType::containsNestedMapping() const
return m_struct.annotation().containsNestedMapping.value();
}
string StructType::toString(bool _short) const
string StructType::toString(bool _withoutDataLocation) const
{
string ret = "struct " + *m_struct.annotation().canonicalName;
if (!_short)
if (!_withoutDataLocation)
ret += " " + stringForReferencePart();
return ret;
}
@ -2607,7 +2638,7 @@ bool UserDefinedValueType::operator==(Type const& _other) const
return other.definition() == definition();
}
string UserDefinedValueType::toString(bool /* _short */) const
string UserDefinedValueType::toString(bool /* _withoutDataLocation */) const
{
return *definition().annotation().canonicalName;
}
@ -2655,13 +2686,24 @@ bool TupleType::operator==(Type const& _other) const
return false;
}
string TupleType::toString(bool _short) const
string TupleType::toString(bool _withoutDataLocation) const
{
if (components().empty())
return "tuple()";
string str = "tuple(";
for (auto const& t: components())
str += (t ? t->toString(_short) : "") + ",";
str += (t ? t->toString(_withoutDataLocation) : "") + ",";
str.pop_back();
return str + ")";
}
string TupleType::humanReadableName() const
{
if (components().empty())
return "tuple()";
string str = "tuple(";
for (auto const& t: components())
str += (t ? t->humanReadableName() : "") + ",";
str.pop_back();
return str + ")";
}
@ -2699,7 +2741,7 @@ Type const* TupleType::mobileType() const
else
mobiles.push_back(nullptr);
}
return TypeProvider::tuple(move(mobiles));
return TypeProvider::tuple(std::move(mobiles));
}
FunctionType::FunctionType(FunctionDefinition const& _function, Kind _kind):
@ -3094,7 +3136,20 @@ string FunctionType::canonicalName() const
return "function";
}
string FunctionType::toString(bool _short) const
string FunctionType::humanReadableName() const
{
switch (m_kind)
{
case Kind::Error:
return "error " + m_declaration->name() + toStringInParentheses(m_parameterTypes, /* _withoutDataLocation */ true);
case Kind::Event:
return "event " + m_declaration->name() + toStringInParentheses(m_parameterTypes, /* _withoutDataLocation */ true);
default:
return toString(/* _withoutDataLocation */ false);
}
}
string FunctionType::toString(bool _withoutDataLocation) const
{
string name = "function ";
if (m_kind == Kind::Declaration)
@ -3105,20 +3160,15 @@ string FunctionType::toString(bool _short) const
name += *contract->annotation().canonicalName + ".";
name += functionDefinition->name();
}
name += '(';
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
name += ")";
name += toStringInParentheses(m_parameterTypes, _withoutDataLocation);
if (m_stateMutability != StateMutability::NonPayable)
name += " " + stateMutabilityToString(m_stateMutability);
if (m_kind == Kind::External)
name += " external";
if (!m_returnParameterTypes.empty())
{
name += " returns (";
for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it)
name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ",");
name += ")";
name += " returns ";
name += toStringInParentheses(m_returnParameterTypes, _withoutDataLocation);
}
return name;
}
@ -3339,6 +3389,12 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
}
case Kind::Error:
return {{"selector", TypeProvider::fixedBytes(4)}};
case Kind::Event:
{
if (!(dynamic_cast<EventDefinition const&>(declaration()).isAnonymous()))
return {{"selector", TypeProvider::fixedBytes(32)}};
return MemberList::MemberMap();
}
default:
return MemberList::MemberMap();
}
@ -3704,9 +3760,9 @@ bool MappingType::operator==(Type const& _other) const
return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType;
}
string MappingType::toString(bool _short) const
string MappingType::toString(bool _withoutDataLocation) const
{
return "mapping(" + keyType()->toString(_short) + " => " + valueType()->toString(_short) + ")";
return "mapping(" + keyType()->toString(_withoutDataLocation) + " => " + valueType()->toString(_withoutDataLocation) + ")";
}
string MappingType::canonicalName() const
@ -3931,11 +3987,11 @@ bool ModifierType::operator==(Type const& _other) const
return true;
}
string ModifierType::toString(bool _short) const
string ModifierType::toString(bool _withoutDataLocation) const
{
string name = "modifier (";
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
name += (*it)->toString(_withoutDataLocation) + (it + 1 == m_parameterTypes.end() ? "" : ",");
return name + ")";
}
@ -4131,7 +4187,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
return {};
}
string MagicType::toString(bool _short) const
string MagicType::toString(bool _withoutDataLocation) const
{
switch (m_kind)
{
@ -4145,7 +4201,7 @@ string MagicType::toString(bool _short) const
return "abi";
case Kind::MetaType:
solAssert(m_typeArgument, "");
return "type(" + m_typeArgument->toString(_short) + ")";
return "type(" + m_typeArgument->toString(_withoutDataLocation) + ")";
}
solAssert(false, "Unknown kind of magic.");
return {};

View File

@ -335,10 +335,11 @@ public:
return members(_currentScope).memberType(_name);
}
virtual std::string toString(bool _short) const = 0;
virtual std::string toString(bool _withoutDataLocation) const = 0;
std::string toString() const { return toString(false); }
/// @returns the canonical name of this type for use in library function signatures.
virtual std::string canonicalName() const { return toString(true); }
virtual std::string humanReadableName() const { return toString(); }
/// @returns the signature of this type in external functions, i.e. `uint256` for integers
/// or `(uint256,bytes8)[2]` for an array of structs. If @a _structsByName,
/// structs are given by canonical name like `ContractName.StructName[2]`.
@ -427,7 +428,7 @@ public:
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override;
u256 literalValue(Literal const* _literal) const override;
@ -470,7 +471,7 @@ public:
bool isValueType() const override { return true; }
bool nameable() const override { return true; }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
Type const* encodingType() const override { return this; }
TypeResult interfaceType(bool) const override { return this; }
@ -517,7 +518,7 @@ public:
bool isValueType() const override { return true; }
bool nameable() const override { return true; }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
Type const* encodingType() const override { return this; }
TypeResult interfaceType(bool) const override { return this; }
@ -567,7 +568,7 @@ public:
bool canBeStored() const override { return false; }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
u256 literalValue(Literal const* _literal) const override;
Type const* mobileType() const override;
@ -831,7 +832,8 @@ public:
bool containsNestedMapping() const override { return m_baseType->containsNestedMapping(); }
bool nameable() const override { return true; }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string humanReadableName() const override;
std::string canonicalName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
@ -895,7 +897,8 @@ public:
unsigned calldataEncodedTailSize() const override { return 32; }
bool isDynamicallySized() const override { return true; }
bool isDynamicallyEncoded() const override { return true; }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string humanReadableName() const override;
Type const* mobileType() const override;
BoolResult validForLocation(DataLocation _loc) const override { return m_arrayType.validForLocation(_loc); }
@ -939,7 +942,7 @@ public:
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
bool isValueType() const override { return !isSuper(); }
bool nameable() const override { return !isSuper(); }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
@ -1001,7 +1004,7 @@ public:
u256 storageSize() const override;
bool containsNestedMapping() const override;
bool nameable() const override { return true; }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
@ -1063,7 +1066,7 @@ public:
}
unsigned storageBytes() const override;
bool leftAligned() const override { return false; }
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override;
bool isValueType() const override { return true; }
bool nameable() const override { return true; }
@ -1150,7 +1153,7 @@ public:
return false;
}
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override;
std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); }
@ -1176,7 +1179,8 @@ public:
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
std::string toString(bool) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string humanReadableName() const override;
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool hasSimpleZeroValueInMemory() const override { return false; }
@ -1378,7 +1382,8 @@ public:
TypeResult unaryOperatorResult(Token _operator) const override;
TypeResult binaryOperatorResult(Token, Type const*) const override;
std::string canonicalName() const override;
std::string toString(bool _short) const override;
std::string humanReadableName() const override;
std::string toString(bool _withoutDataLocation) const override;
unsigned calldataEncodedSize(bool _padded) const override;
bool canBeStored() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
u256 storageSize() const override;
@ -1512,7 +1517,7 @@ public:
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
std::string canonicalName() const override;
bool containsNestedMapping() const override { return true; }
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
@ -1553,7 +1558,7 @@ public:
bool canBeStored() const override { return false; }
u256 storageSize() const override;
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
std::string toString(bool _withoutDataLocation) const override { return "type(" + m_actualType->toString(_withoutDataLocation) + ")"; }
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
@ -1580,7 +1585,7 @@ public:
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string richIdentifier() const override;
bool operator==(Type const& _other) const override;
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
protected:
std::vector<std::tuple<std::string, Type const*>> makeStackItems() const override { return {}; }
private:
@ -1606,7 +1611,7 @@ public:
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
protected:
std::vector<std::tuple<std::string, Type const*>> makeStackItems() const override { return {}; }
@ -1645,7 +1650,7 @@ public:
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
std::string toString(bool _short) const override;
std::string toString(bool _withoutDataLocation) const override;
Kind kind() const { return m_kind; }

View File

@ -468,7 +468,8 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
_to.identifier() +
_options.toFunctionNameSuffix();
return createFunction(functionName, [&]() {
bool needsPadding = _options.padded && fromArrayType.isByteArrayOrString();
bool bytesOrString = fromArrayType.isByteArrayOrString();
bool needsPadding = _options.padded && bytesOrString;
if (fromArrayType.isDynamicallySized())
{
Whiskers templ(R"(
@ -498,7 +499,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
);
templ("readableTypeNameFrom", _from.toString(true));
templ("readableTypeNameTo", _to.toString(true));
templ("copyFun", m_utils.copyToMemoryFunction(true));
templ("copyFun", m_utils.copyToMemoryFunction(true, /*cleanup*/bytesOrString));
templ("lengthPadded", needsPadding ? m_utils.roundUpFunction() + "(length)" : "length");
return templ.render();
}
@ -514,7 +515,7 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup(
templ("functionName", functionName);
templ("readableTypeNameFrom", _from.toString(true));
templ("readableTypeNameTo", _to.toString(true));
templ("copyFun", m_utils.copyToMemoryFunction(true));
templ("copyFun", m_utils.copyToMemoryFunction(true, /*cleanup*/bytesOrString));
templ("byteLength", toCompactHexWithPrefix(fromArrayType.length() * fromArrayType.calldataStride()));
return templ.render();
}
@ -662,7 +663,7 @@ string ABIFunctions::abiEncodingFunctionMemoryByteArray(
templ("functionName", functionName);
templ("lengthFun", m_utils.arrayLengthFunction(_from));
templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options));
templ("copyFun", m_utils.copyToMemoryFunction(false));
templ("copyFun", m_utils.copyToMemoryFunction(false, /*cleanup*/true));
templ("lengthPadded", _options.padded ? m_utils.roundUpFunction() + "(length)" : "length");
return templ.render();
});
@ -699,7 +700,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray(
case 0 {
// short byte array
mstore(pos, and(slotValue, not(0xff)))
ret := add(pos, <lengthPaddedShort>)
ret := add(pos, mul(<lengthPaddedShort>, iszero(iszero(length))))
}
case 1 {
// long byte array
@ -1296,7 +1297,7 @@ string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const
templ("functionName", functionName);
templ("allocate", m_utils.allocationFunction());
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
templ("copyToMemFun", m_utils.copyToMemoryFunction(!_fromMemory));
templ("copyToMemFun", m_utils.copyToMemoryFunction(!_fromMemory, /*cleanup*/true));
return templ.render();
});
}

View File

@ -243,7 +243,7 @@ private:
std::string abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunction for calldata array types.
std::string abiDecodingFunctionCalldataArray(ArrayType const& _type);
/// Part of @a abiDecodingFunctionArrayWithAvailableLength
/// Part of @a abiDecodingFunctionArrayAvailableLength
std::string abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunction for calldata struct types.
std::string abiDecodingFunctionCalldataStruct(StructType const& _type);

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