diff --git a/.circleci/build_win.ps1 b/.circleci/build_win.ps1 new file mode 100644 index 000000000000..0a2c38edd845 --- /dev/null +++ b/.circleci/build_win.ps1 @@ -0,0 +1,18 @@ +$ErrorActionPreference = "Stop" + +cd "$PSScriptRoot\.." + +if ("$Env:FORCE_RELEASE" -Or "$Env:CIRCLE_TAG") { + New-Item prerelease.txt -type file + Write-Host "Building release version." +} + +mkdir build +cd build +$boost_dir=(Resolve-Path $PSScriptRoot\..\deps\boost\lib\cmake\Boost-*) +..\deps\cmake\bin\cmake -G "Visual Studio 16 2019" -DBoost_DIR="$boost_dir\" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_INSTALL_PREFIX="$PSScriptRoot\..\upload" .. +if ( -not $? ) { throw "CMake configure failed." } +msbuild solidity.sln /p:Configuration=Release /m:5 /v:minimal +if ( -not $? ) { throw "Build failed." } +..\deps\cmake\bin\cmake --build . -j 5 --target install --config Release +if ( -not $? ) { throw "Install target failed." } diff --git a/.circleci/config.yml b/.circleci/config.yml index f92a9421204b..a8ccc4a41b4d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,25 +7,25 @@ # - ems: Emscripten version: 2.1 parameters: - ubuntu-1804-docker-image: - type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1804-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9ab317e583c395e50884ba82e9f99811c374344cea4c550725be8ec836e07acc" ubuntu-2004-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:cbfa42d8ecbe94391ba8837e218869242666de7a0da6ccac065a856c85b6a6a0" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-4 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:aca1372dcc5edadd3db13ff1aa6807727d79e08082a48eb7cc05444c1b516ace" ubuntu-2004-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7a4d5271b5552139d9f2caefc50d42f401bf74132cf8f253e199e11c80ab42de" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-4 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:0954edfb48a7efa6922b4d6adf536a2fc483ca34ad62f95ec54c33a616a66974" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:efaabb3c143f64171be596932c62013bcfd7f73b1fbcb832025a34dd2b6e6922" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-6 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:990ed3aaac3fcb87ca9b63a021a91ed89dfd165b341aa7b5c6457cdbe132dfb3" emscripten-docker-image: type: string - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc" + # solbuildpackpusher/solidity-buildpack-deps:emscripten-2 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:23dad3b34deae8107c8551804ef299f6a89c23ed506e8118fac151e2bdc9018c" + +orbs: + win: circleci/windows@2.2.0 defaults: @@ -64,6 +64,10 @@ defaults: path: build/solc/solc destination: solc + # windows artifacts + - artifact_solc_windows: &artifact_solc_windows + path: upload/ + # compiled tool executable target - artifacts_tools: &artifacts_tools path: build/tools/solidity-upgrade @@ -82,8 +86,11 @@ defaults: root: build paths: - test/tools/ossfuzz/abiv2_proto_ossfuzz + - test/tools/ossfuzz/abiv2_isabelle_ossfuzz - test/tools/ossfuzz/const_opt_ossfuzz + - test/tools/ossfuzz/solc_noopt_mutator_ossfuzz - test/tools/ossfuzz/solc_noopt_ossfuzz + - test/tools/ossfuzz/solc_opt_mutator_ossfuzz - test/tools/ossfuzz/solc_opt_ossfuzz - test/tools/ossfuzz/strictasm_assembly_ossfuzz - test/tools/ossfuzz/strictasm_diff_ossfuzz @@ -107,14 +114,17 @@ defaults: - run_soltest: &run_soltest name: soltest + no_output_timeout: 30m command: ./.circleci/soltest.sh - run_soltest_all: &run_soltest_all name: soltest_all + no_output_timeout: 30m command: ./.circleci/soltest_all.sh - run_cmdline_tests: &run_cmdline_tests name: command line tests + no_output_timeout: 30m command: ./test/cmdlineTests.sh - run_docs_pragma_min_version: &run_docs_pragma_min_version @@ -163,7 +173,6 @@ defaults: at: build - run: <<: *run_soltest - no_output_timeout: 30m - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results @@ -175,7 +184,6 @@ defaults: at: build - run: <<: *run_soltest - no_output_timeout: 30m - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results @@ -207,6 +215,11 @@ defaults: requires: - b_ubu_release + - workflow_archlinux: &workflow_archlinux + <<: *workflow_trigger_on_tags + requires: + - b_archlinux + - workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov <<: *workflow_trigger_on_tags requires: @@ -237,6 +250,16 @@ defaults: requires: - b_ubu_ossfuzz + - workflow_win: &workflow_win + <<: *workflow_trigger_on_tags + requires: + - b_win + + - workflow_win_release: &workflow_win_release + <<: *workflow_trigger_on_tags + requires: + - b_win_release + # -------------------------------------------------------------------------- # Notification Templates - gitter_notify_failure: &gitter_notify_failure @@ -309,6 +332,9 @@ jobs: - run: name: checking shell scripts command: ./scripts/chk_shellscripts/chk_shellscripts.sh + - run: + name: Check for broken symlinks + command: ./scripts/check_symlinks.sh chk_errorcodes: docker: @@ -321,7 +347,7 @@ jobs: chk_pylint: docker: - - image: buildpack-deps:eoan + - image: buildpack-deps:focal steps: - checkout - run: @@ -337,7 +363,7 @@ jobs: chk_antlr_grammar: docker: - - image: buildpack-deps:eoan + - image: buildpack-deps:focal steps: - checkout - run: @@ -410,6 +436,7 @@ jobs: CC: clang CXX: clang++ CMAKE_OPTIONS: -DSANITIZE=address + MAKEFLAGS: -j 3 steps: - checkout - run: *run_build @@ -433,28 +460,31 @@ jobs: <<: *build_ubuntu2004 environment: FORCE_RELEASE: ON + MAKEFLAGS: -j 10 - b_ubu18: &build_ubuntu1804 - docker: - - image: << pipeline.parameters.ubuntu-1804-docker-image >> + b_ubu_static: + <<: *build_ubuntu2004 environment: - CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 - CMAKE_BUILD_TYPE: RelWithDebugInfo + MAKEFLAGS: -j 10 + CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DUSE_Z3_DLOPEN=ON -DUSE_CVC4=OFF -DSOLC_STATIC_STDLIBS=ON steps: - checkout - run: *run_build + - run: + name: strip binary + command: strip build/solc/solc - store_artifacts: *artifacts_solc - - persist_to_workspace: *artifacts_executables b_ubu_codecov: <<: *build_ubuntu2004 environment: COVERAGE: ON CMAKE_BUILD_TYPE: Debug + MAKEFLAGS: -j 10 steps: - checkout - run: *run_build - - persist_to_workspace: *artifacts_build_dir + - persist_to_workspace: *artifacts_executables t_ubu_codecov: <<: *test_ubuntu2004 @@ -483,7 +513,8 @@ jobs: <<: *build_ubuntu2004 environment: CMAKE_BUILD_TYPE: Debug - CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF + CMAKE_OPTIONS: -DCMAKE_CXX_STANDARD=20 -DUSE_CVC4=OFF + MAKEFLAGS: -j 10 steps: - checkout - run: *run_build @@ -495,7 +526,7 @@ jobs: CC: clang CXX: clang++ TERM: xterm - CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/libfuzzer.cmake + MAKEFLAGS: -j 3 steps: - checkout - run: *setup_prerelease_commit_hash @@ -524,6 +555,7 @@ jobs: - image: archlinux/base environment: TERM: xterm + MAKEFLAGS: -j 3 steps: - run: name: Install build dependencies @@ -540,16 +572,19 @@ jobs: environment: TERM: xterm CMAKE_BUILD_TYPE: Release + MAKEFLAGS: -j 5 steps: - checkout - restore_cache: keys: - - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + - dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }} + # DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh + # WARNING! If you do edit anything here instead, remember to invalidate the cache manually. - run: name: Install build dependencies command: ./.circleci/osx_install_dependencies.sh - save_cache: - key: dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + key: dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }} paths: - /usr/local/bin - /usr/local/sbin @@ -560,7 +595,12 @@ jobs: - run: *run_build - store_artifacts: *artifacts_solc - store_artifacts: *artifacts_tools - - persist_to_workspace: *artifacts_build_dir + - persist_to_workspace: + root: . + paths: + - build/solc/solc + - build/test/soltest + - build/test/tools/solfuzzer t_osx_soltest: macos: @@ -573,9 +613,9 @@ jobs: - checkout - restore_cache: keys: - - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + - dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }} - attach_workspace: - at: build + at: . - run: *run_soltest - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results @@ -589,9 +629,9 @@ jobs: - checkout - restore_cache: keys: - - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + - dependencies-osx-{{ checksum ".circleci/osx_install_dependencies.sh" }} - attach_workspace: - at: build + at: . - run: *run_cmdline_tests - store_artifacts: *artifacts_test_results @@ -607,7 +647,7 @@ jobs: - run: name: Build command: | - scripts/travis-emscripten/build_emscripten.sh + scripts/ci/build_emscripten.sh - store_artifacts: path: emscripten_build/libsolc/soljson.js destination: soljson.js @@ -625,6 +665,7 @@ jobs: <<: *build_ubuntu2004 environment: CMAKE_OPTIONS: -DSANITIZE=address + MAKEFLAGS: -j 10 CMAKE_BUILD_TYPE: Release steps: - checkout @@ -648,6 +689,25 @@ jobs: t_ubu_soltest: &t_ubu_soltest <<: *test_ubuntu2004 + t_archlinux_soltest: &t_archlinux_soltest + docker: + - image: archlinux/base + environment: + EVM: constantinople + OPTIMIZE: 0 + TERM: xterm + steps: + - run: + name: Install runtime dependencies + command: | + pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake z3 cvc4 git openssh tar + - checkout + - attach_workspace: + at: build + - run: *run_soltest + - store_test_results: *store_test_results + - store_artifacts: *artifacts_test_results + t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul docker: - image: << pipeline.parameters.ubuntu-2004-docker-image >> @@ -701,7 +761,6 @@ jobs: at: build - run: <<: *run_cmdline_tests - no_output_timeout: 30m - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results @@ -737,111 +796,179 @@ jobs: apt-get install -qqy --no-install-recommends nodejs npm cvc4 - run: name: Test solcjs + no_output_timeout: 30m command: | node --version npm --version test/externalTests/solc-js/solc-js.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt) - t_ems_compile_ext_gnosis: + t_ems_ext: + parameters: + project: + type: string + compile_only: + type: integer + default: 0 + nodejs_version: + type: integer + default: 14 + gitter_notify: + type: boolean + default: no docker: - - image: circleci/node:10 + - image: circleci/node:<> environment: TERM: xterm + COMPILE_ONLY: <> steps: - checkout - attach_workspace: at: /tmp/workspace - run: - name: External GnosisSafe compilation + name: Install dependencies + command: | + # lsof is used by Colony in its stop-blockchain-client.sh script + sudo apt-get -qy install lsof + - run: + name: External <> tests command: | - export COMPILE_ONLY=1 - test/externalTests/gnosis.sh /tmp/workspace/soljson.js || test/externalTests/gnosis.sh /tmp/workspace/soljson.js + test/externalTests/<>.sh /tmp/workspace/soljson.js + - when: + condition: <> + steps: + - run: *gitter_notify_failure + - run: *gitter_notify_success + + b_win: &b_win + executor: + name: win/default + shell: powershell.exe + steps: + - checkout + - restore_cache: + keys: + - dependencies-win-{{ checksum "scripts/install_deps.ps1" }} + # DO NOT EDIT between here and save_cache, but rather edit .\scripts\install_deps.ps1 + # WARNING! If you do edit anything here instead, remember to invalidate the cache manually. + - run: + name: "Installing dependencies" + command: .\scripts\install_deps.ps1 + - save_cache: + key: dependencies-win-{{ checksum "scripts/install_deps.ps1" }} + paths: + - .\deps + - run: + name: "Building solidity" + command: .circleci/build_win.ps1 + - run: + name: "Run solc.exe to make sure build was successful." + command: .\build\solc\Release\solc.exe --version + - store_artifacts: *artifact_solc_windows + - persist_to_workspace: + root: build + paths: + - .\solc\*\solc.exe + - .\test\*\soltest.exe - t_ems_test_ext_gnosis: - docker: - - image: circleci/node:10 + b_win_release: + <<: *b_win environment: - TERM: xterm + FORCE_RELEASE: ON + + t_win: &t_win + executor: + name: win/default + shell: powershell.exe steps: - checkout - attach_workspace: - at: /tmp/workspace + at: build - run: - name: External GnosisSafe tests - command: | - test/externalTests/gnosis.sh /tmp/workspace/soljson.js || test/externalTests/gnosis.sh /tmp/workspace/soljson.js - - run: *gitter_notify_failure - - run: *gitter_notify_success + name: "Install evmone" + command: scripts/install_evmone.ps1 + - run: + name: "Run soltest" + command: .circleci/soltest.ps1 + - store_test_results: *store_test_results + - store_artifacts: *artifacts_test_results + + t_win_release: + <<: *t_win - t_ems_compile_ext_zeppelin: + b_bytecode_ubu: docker: - - image: circleci/node:10 - environment: - TERM: xterm + - image: << pipeline.parameters.ubuntu-2004-docker-image >> steps: - checkout - attach_workspace: - at: /tmp/workspace - - run: - name: External Zeppelin compilation - command: | - export COMPILE_ONLY=1 - test/externalTests/zeppelin.sh /tmp/workspace/soljson.js || test/externalTests/zeppelin.sh /tmp/workspace/soljson.js + at: build + - run: scripts/bytecodecompare/storebytecode.sh && cp -v report.txt bytecode-report-ubuntu.txt + - store_artifacts: + path: report.txt + - persist_to_workspace: + root: . + paths: + - bytecode-report-ubuntu.txt - t_ems_test_ext_zeppelin: - docker: - - image: circleci/node:10 + b_bytecode_osx: + macos: + xcode: "11.0.0" environment: TERM: xterm steps: - checkout - attach_workspace: - at: /tmp/workspace - - run: - name: External Zeppelin tests - command: | - test/externalTests/zeppelin.sh /tmp/workspace/soljson.js || test/externalTests/zeppelin.sh /tmp/workspace/soljson.js - - run: *gitter_notify_failure - - run: *gitter_notify_success + at: . + - run: scripts/bytecodecompare/storebytecode.sh && cp -v report.txt bytecode-report-osx.txt + - store_artifacts: + path: report.txt + - persist_to_workspace: + root: . + paths: + - bytecode-report-osx.txt - t_ems_compile_ext_colony: - docker: - - image: circleci/node:10 - environment: - TERM: xterm + b_bytecode_win: + executor: + name: win/default + shell: cmd.exe steps: - checkout - attach_workspace: - at: /tmp/workspace - - run: - name: Install test dependencies - command: | - sudo apt-get -qy install lsof - - run: - name: External ColonyNetworks compilation - command: | - export COMPILE_ONLY=1 - test/externalTests/colony.sh /tmp/workspace/soljson.js || test/externalTests/colony.sh /tmp/workspace/soljson.js + at: build + - run: python scripts\isolate_tests.py test\ + - run: python scripts\bytecodecompare\prepare_report.py build\solc\Release\solc.exe + - run: cp report.txt bytecode-report-windows.txt + - store_artifacts: + path: report.txt + - persist_to_workspace: + root: . + paths: + - bytecode-report-windows.txt - t_ems_test_ext_colony: + b_bytecode_ems: docker: - - image: circleci/node:10 + - image: circleci/node:14 environment: - TERM: xterm + SOLC_EMSCRIPTEN: "On" steps: - checkout - attach_workspace: - at: /tmp/workspace - - run: - name: Install test dependencies - command: | - sudo apt-get -qy install lsof - - run: - name: External ColonyNetworks tests - command: | - test/externalTests/colony.sh /tmp/workspace/soljson.js || test/externalTests/colony.sh /tmp/workspace/soljson.js - - run: *gitter_notify_failure - - run: *gitter_notify_success + at: emscripten_build/libsolc + - run: scripts/bytecodecompare/storebytecode.sh && cp -v report.txt bytecode-report-emscripten.txt + - store_artifacts: + path: report.txt + - persist_to_workspace: + root: . + paths: + - bytecode-report-emscripten.txt + + t_bytecode_compare: + docker: + - image: << pipeline.parameters.ubuntu-2004-docker-image >> + steps: + - attach_workspace: + at: . + - run: diff --report-identical-files --from-file bytecode-report-emscripten.txt bytecode-report-ubuntu.txt bytecode-report-osx.txt bytecode-report-windows.txt workflows: version: 2 @@ -861,7 +988,6 @@ workflows: # build-only - b_docs: *workflow_trigger_on_tags - - b_archlinux: *workflow_trigger_on_tags - b_ubu_cxx20: *workflow_trigger_on_tags - b_ubu_ossfuzz: *workflow_trigger_on_tags @@ -870,9 +996,15 @@ workflows: - t_osx_cli: *workflow_osx - t_osx_soltest: *workflow_osx + # ArchLinux build and tests + - b_archlinux: *workflow_trigger_on_tags + - t_archlinux_soltest: *workflow_archlinux + + # Static build + - b_ubu_static: *workflow_trigger_on_tags + # Ubuntu build and tests - b_ubu: *workflow_trigger_on_tags - - b_ubu18: *workflow_trigger_on_tags - t_ubu_cli: *workflow_ubuntu2004 - t_ubu_soltest: *workflow_ubuntu2004 - t_ubu_soltest_enforce_yul: *workflow_ubuntu2004 @@ -884,19 +1016,88 @@ workflows: - t_ubu_release_cli: *workflow_ubuntu2004_release - t_ubu_release_soltest: *workflow_ubuntu2004_release - # ASan build and tests - - b_ubu_asan: *workflow_trigger_on_tags - - b_ubu_asan_clang: *workflow_trigger_on_tags - - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan - - t_ubu_asan_constantinople_clang: *workflow_ubuntu2004_asan_clang - - t_ubu_asan_cli: *workflow_ubuntu2004_asan - - # Emscripten build and selected tests + # Emscripten build and tests that take 15 minutes or less - b_ems: *workflow_trigger_on_tags - t_ems_solcjs: *workflow_emscripten - - t_ems_compile_ext_colony: *workflow_emscripten - - t_ems_compile_ext_gnosis: *workflow_emscripten - - t_ems_compile_ext_zeppelin: *workflow_emscripten + + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_compile_ext_colony + project: colony + compile_only: 1 + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_compile_ext_gnosis + project: gnosis + compile_only: 1 + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_compile_ext_gnosis_v2 + project: gnosis-v2 + compile_only: 1 + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_compile_ext_zeppelin + project: zeppelin + compile_only: 1 + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_compile_ext_ens + project: ens + compile_only: 1 + # NOTE: One of the dependencies (fsevents) fails to build its native extension on node.js 12+. + nodejs_version: 10 + + # FIXME: Gnosis tests are pretty flaky right now. They often fail on CircleCI due to random ProviderError + # and there are also other less frequent problems. See https://github.com/gnosis/safe-contracts/issues/216. + #- t_ems_ext: + # <<: *workflow_emscripten + # name: t_ems_test_ext_gnosis + # project: gnosis + # # NOTE: Tests do not start on node.js 14 ("ganache-cli exited early with code 1"). + # nodejs_version: 12 + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_test_ext_gnosis_v2 + project: gnosis-v2 + # NOTE: Tests do not start on node.js 14 ("ganache-cli exited early with code 1"). + nodejs_version: 12 + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_test_ext_zeppelin + project: zeppelin + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_test_ext_ens + project: ens + # NOTE: One of the dependencies (fsevents) fails to build its native extension on node.js 12+. + nodejs_version: 10 + + # Windows build and tests + - b_win: *workflow_trigger_on_tags + - b_win_release: *workflow_trigger_on_tags + - t_win: *workflow_win + - t_win_release: *workflow_win_release + + # Bytecode comparison: + - b_bytecode_ubu: + requires: + - b_ubu + - b_bytecode_win: + requires: + - b_win + - b_bytecode_osx: + requires: + - b_osx + - b_bytecode_ems: + requires: + - b_ems + - t_bytecode_compare: + requires: + - b_bytecode_ubu + - b_bytecode_win + - b_bytecode_osx + - b_bytecode_ems nightly: @@ -917,3 +1118,18 @@ workflows: # Code Coverage enabled build and tests - b_ubu_codecov: *workflow_trigger_on_tags - t_ubu_codecov: *workflow_ubuntu2004_codecov + + # ASan build and tests + - b_ubu_asan: *workflow_trigger_on_tags + - b_ubu_asan_clang: *workflow_trigger_on_tags + - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan + - t_ubu_asan_constantinople_clang: *workflow_ubuntu2004_asan_clang + - t_ubu_asan_cli: *workflow_ubuntu2004_asan + + # Emscripten build and tests that take more than 15 minutes to execute + - b_ems: *workflow_trigger_on_tags + - t_ems_ext: + <<: *workflow_emscripten + name: t_ems_test_ext_colony + project: colony + gitter_notify: yes diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 06468013980c..9050b05a0a62 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -4,7 +4,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -43,17 +43,21 @@ then ./scripts/install_obsolete_jsoncpp_1_7_4.sh # z3 - wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.8/z3-4.8.8-x64-osx-10.14.6.zip - unzip z3-4.8.8-x64-osx-10.14.6.zip - rm -f z3-4.8.8-x64-osx-10.14.6.zip - cp z3-4.8.8-x64-osx-10.14.6/bin/libz3.a /usr/local/lib - cp z3-4.8.8-x64-osx-10.14.6/bin/z3 /usr/local/bin - cp z3-4.8.8-x64-osx-10.14.6/include/* /usr/local/include - rm -rf z3-4.8.8-x64-osx-10.14.6 + wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.9/z3-4.8.9-x64-osx-10.14.6.zip + unzip z3-4.8.9-x64-osx-10.14.6.zip + rm -f z3-4.8.9-x64-osx-10.14.6.zip + cp z3-4.8.9-x64-osx-10.14.6/bin/libz3.a /usr/local/lib + cp z3-4.8.9-x64-osx-10.14.6/bin/z3 /usr/local/bin + cp z3-4.8.9-x64-osx-10.14.6/include/* /usr/local/include + rm -rf z3-4.8.9-x64-osx-10.14.6 # evmone wget https://github.com/ethereum/evmone/releases/download/v0.4.0/evmone-0.4.0-darwin-x86_64.tar.gz tar xzpf evmone-0.4.0-darwin-x86_64.tar.gz -C /usr/local rm -f evmone-0.4.0-darwin-x86_64.tar.gz -fi + # hera + wget https://github.com/ewasm/hera/releases/download/v0.3.2/hera-0.3.2-darwin-x86_64.tar.gz + tar xzpf hera-0.3.2-darwin-x86_64.tar.gz -C /usr/local + rm -f hera-0.3.2-darwin-x86_64.tar.gz +fi diff --git a/.circleci/soltest.ps1 b/.circleci/soltest.ps1 new file mode 100755 index 000000000000..6b67adb1392a --- /dev/null +++ b/.circleci/soltest.ps1 @@ -0,0 +1,12 @@ +$ErrorActionPreference = "Stop" + +cd "$PSScriptRoot\.." + +.\build\solc\Release\solc.exe --version +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 +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." } \ No newline at end of file diff --git a/.circleci/soltest.sh b/.circleci/soltest.sh index 41ab71875026..74353d8c2c1f 100755 --- a/.circleci/soltest.sh +++ b/.circleci/soltest.sh @@ -4,14 +4,14 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # Configuration Environment Variables: # # EVM=version_string Specifies EVM version to compile for (such as homestead, etc) # OPTIMIZE=1 Enables backend optimizer -# ABI_ENCODER_V2=1 Enables ABI encoder version 2 +# ABI_ENCODER_V1=1 Forcibly enables ABI coder version 1 # SOLTEST_FLAGS= Appends to default SOLTEST_ARGS # # ------------------------------------------------------------------------------ @@ -36,7 +36,6 @@ set -e OPTIMIZE=${OPTIMIZE:-"0"} EVM=${EVM:-"invalid"} -WORKDIR=${CIRCLE_WORKING_DIRECTORY:-.} REPODIR="$(realpath $(dirname $0)/..)" source "${REPODIR}/scripts/common.sh" @@ -49,7 +48,7 @@ ulimit -s 16384 get_logfile_basename() { local filename="${EVM}" test "${OPTIMIZE}" = "1" && filename="${filename}_opt" - test "${ABI_ENCODER_V2}" = "1" && filename="${filename}_abiv2" + test "${ABI_ENCODER_V1}" = "1" && filename="${filename}_abiv1" echo -ne "${filename}" } @@ -57,7 +56,7 @@ get_logfile_basename() { BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml ${BOOST_TEST_ARGS}" SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS" test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize" -test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2" +test "${ABI_ENCODER_V1}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv1" echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS} -- ${SOLTEST_ARGS}" diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 7b1fb053bd69..e1bf1c82244a 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -4,7 +4,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -30,7 +30,7 @@ REPODIR="$(realpath $(dirname $0)/..)" EVM_VALUES=(homestead byzantium constantinople petersburg istanbul) OPTIMIZE_VALUES=(0 1) -STEPS=$(( 1 + ${#EVM_VALUES[@]} * ${#OPTIMIZE_VALUES[@]} )) +STEPS=$(( 2 + ${#EVM_VALUES[@]} * ${#OPTIMIZE_VALUES[@]} )) if (( $CIRCLE_NODE_TOTAL )) && (( $CIRCLE_NODE_TOTAL > 1 )) then @@ -57,20 +57,29 @@ echo "Running steps $RUN_STEPS..." STEP=1 -[[ " $RUN_STEPS " =~ " $STEP " ]] && EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V2=1 "${REPODIR}/.circleci/soltest.sh" +# Run SMTChecker tests separately, as the heaviest expected run. +[[ " $RUN_STEPS " =~ " $STEP " ]] && EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V1=1 BOOST_TEST_ARGS="-t smtCheckerTests/*" "${REPODIR}/.circleci/soltest.sh" +STEP=$(($STEP + 1)) + +# Run without SMTChecker tests. +[[ " $RUN_STEPS " =~ " $STEP " ]] && EVM=istanbul OPTIMIZE=1 ABI_ENCODER_V1=1 BOOST_TEST_ARGS="-t !smtCheckerTests" "${REPODIR}/.circleci/soltest.sh" STEP=$(($STEP + 1)) for OPTIMIZE in ${OPTIMIZE_VALUES[@]} do for EVM in ${EVM_VALUES[@]} do - [[ " $RUN_STEPS " =~ " $STEP " ]] && EVM="$EVM" OPTIMIZE="$OPTIMIZE" BOOST_TEST_ARGS="-t !@nooptions" "${REPODIR}/.circleci/soltest.sh" + # run tests against hera ewasm evmc vm, only if OPTIMIZE == 0 and evm version is byzantium + EWASM_ARGS="" + [ "${EVM}" = "byzantium" ] && [ "${OPTIMIZE}" = "0" ] && EWASM_ARGS="--ewasm" + + [[ " $RUN_STEPS " =~ " $STEP " ]] && EVM="$EVM" OPTIMIZE="$OPTIMIZE" SOLTEST_FLAGS="$SOLTEST_FLAGS $EWASM_ARGS" BOOST_TEST_ARGS="-t !@nooptions" "${REPODIR}/.circleci/soltest.sh" STEP=$(($STEP + 1)) done done if (($STEP != $STEPS + 1)) then - echo "Step counter not properly adjusted!" >2 + echo "Step counter not properly adjusted!" >&2 exit 1 fi diff --git a/.editorconfig b/.editorconfig index d80fb3570f9d..0bb3fce2eceb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,10 +14,10 @@ indent_size = 4 indent_style = space indent_size = 4 -[std/**.sol] +[*.{sol,yul}] indent_style = space indent_size = 4 -[*.{txt,cmake}] +[*.{txt,cmake,json}] indent_style = tab indent_size = 4 diff --git a/.github/workflows/buildpack-deps.yml b/.github/workflows/buildpack-deps.yml index 2e2ea547fc54..535d15239a94 100644 --- a/.github/workflows/buildpack-deps.yml +++ b/.github/workflows/buildpack-deps.yml @@ -6,7 +6,6 @@ on: paths: - 'scripts/docker/buildpack-deps/Dockerfile.emscripten' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz' - - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu1804' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2004' @@ -23,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - image_variant: [emscripten, ubuntu1604.clang.ossfuzz, ubuntu1804, ubuntu2004.clang, ubuntu2004] + image_variant: [emscripten, ubuntu1604.clang.ossfuzz, ubuntu2004.clang, ubuntu2004] steps: - uses: actions/checkout@v2 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3d00a974de7d..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,250 +0,0 @@ -#------------------------------------------------------------------------------ -# TravisCI configuration file for solidity. -# -# The documentation for solidity is hosted at: -# -# http://solidity.readthedocs.org -# -# ------------------------------------------------------------------------------ -# This file is part of solidity. -# -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2016-2017 solidity contributors. -#------------------------------------------------------------------------------ - -language: cpp - -branches: - # We need to whitelist the branches which we want to have "push" automation, - # this includes tags (which are treated as branches by travis). - # Pull request automation is not constrained to this set of branches. - only: - - develop - - release - - /^v[0-9]/ - -env: - global: - - ENCRYPTION_LABEL="6d4541b72666" - - SOLC_BUILD_TYPE=RelWithDebInfo - - SOLC_EMSCRIPTEN=Off - # FIXME: Pushing solcjson.js to solc-bin disabled until we fix the cause of #9261 - - SOLC_PUBLISH_EMSCRIPTEN=Off - - SOLC_INSTALL_DEPS_TRAVIS=On - - SOLC_RELEASE=On - - SOLC_TESTS=On - - SOLC_STOREBYTECODE=Off - - SOLC_DOCKER=Off - - MAKEFLAGS="-j 4" - -matrix: - include: - - os: linux - dist: trusty - sudo: required - compiler: gcc - env: - - ZIP_SUFFIX=ubuntu-trusty - - SOLC_STOREBYTECODE=On - before_install: - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo add-apt-repository -y ppa:mhier/libboost-latest - - sudo apt-get update -qq - install: - - sudo apt-get install -qq g++-8 gcc-8 - - sudo apt-get install -qq libboost1.67-dev - - sudo apt-get install -qq libleveldb1 - - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 - - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90 - - - os: linux - dist: trusty - sudo: required - compiler: clang - env: - - ZIP_SUFFIX=ubuntu-trusty-clang - - SOLC_STOREBYTECODE=On - before_install: - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo add-apt-repository -y ppa:mhier/libboost-latest - - sudo apt-get update -qq - install: - - sudo apt-get install -qq g++-8 gcc-8 - - sudo apt-get install -qq libboost1.67-dev - - sudo apt-get install -qq libleveldb1 - - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 90 - - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 90 - - # Docker target, which generates a statically linked alpine image - - os: linux - dist: trusty - sudo: required - services: - - docker - env: - - SOLC_DOCKER=On - - SOLC_INSTALL_DEPS_TRAVIS=Off - - SOLC_RELEASE=Off - - SOLC_TESTS=Off - - # Emscripten target, which compiles 'solc' to javascript and uploads the resulting .js - # files to https://github.com/ethereum/solc-bin. These binaries are used in Browser-Solidity - # and in other Ethereum web-based development contexts. - - os: linux - dist: trusty - sudo: required - compiler: gcc - node_js: - - "10" - services: - - docker - before_install: - - nvm install 10 - - nvm use 10 - - docker pull solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc - env: - - SOLC_EMSCRIPTEN=On - - SOLC_INSTALL_DEPS_TRAVIS=Off - - SOLC_RELEASE=Off - - SOLC_TESTS=Off - - ZIP_SUFFIX=emscripten - - SOLC_STOREBYTECODE=On - # Travis doesn't seem to support "dynamic" cache keys where we could include - # the hashes of certain files. Our CircleCI configuration contains the hash of - # relevant emscripten files. - # - # It is important to invalidate the cache with each emscripten update, because - # dependencies, such as boost, might be broken otherwise. - # - # This key here has no significant on anything, apart from caching. Please keep - # it in sync with the version above. - - EMSCRIPTEN_VERSION_KEY="1.39.3" - - # OS X Mavericks (10.9) - # https://en.wikipedia.org/wiki/OS_X_Mavericks - # -# Disabled because of problems on travis. -# - os: osx -# osx_image: beta-xcode6.2 -# env: -# - ZIP_SUFFIX=osx-mavericks - - # OS X Yosemite (10.10) - # https://en.wikipedia.org/wiki/OS_X_Yosemite - # -# - os: osx -# osx_image: xcode7.1 -# env: -# # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" -# # https://github.com/ethereum/solidity/issues/894 -# - SOLC_TESTS=Off -# - ZIP_SUFFIX=osx-yosemite - - # OS X El Capitan (10.11) - # https://en.wikipedia.org/wiki/OS_X_El_Capitan - # -# - os: osx -# osx_image: xcode7.3 -# env: -# # The use of Debug config here ONLY for El Capitan is a workaround for "The Heisenbug" -# # See https://github.com/ethereum/webthree-umbrella/issues/565 -# - SOLC_BUILD_TYPE=Debug -# # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" -# # https://github.com/ethereum/solidity/issues/894 -# - SOLC_TESTS=Off -# - ZIP_SUFFIX=osx-elcapitan - - # macOS Sierra (10.12) - # https://en.wikipedia.org/wiki/MacOS_Sierra - # -# - os: osx -# osx_image: xcode8 -# env: -# # Look like "The Heisenbug" is occurring here too, so we'll do the same workaround. -# # See https://travis-ci.org/ethereum/solidity/jobs/150240930 -# - SOLC_BUILD_TYPE=Debug -# # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" -# # https://github.com/ethereum/solidity/issues/894 -# - SOLC_TESTS=Off -# - ZIP_SUFFIX=macos-sierra - -git: - depth: 2 - -cache: - ccache: true - directories: - - boost_1_70_0_install - - $HOME/.local - -install: - - test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh) - - test "$TRAVIS_OS_NAME" != "linux" || (sudo scripts/install_cmake.sh) - -before_script: - # Disable tests unless run on the release branch, on tags or with daily cron - - if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" -a "$TRAVIS_EVENT_TYPE" != cron ]; then SOLC_TESTS=Off; fi - - if [ "$TRAVIS_BRANCH" = release -o -n "$TRAVIS_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi - - echo -n "$TRAVIS_COMMIT" > commit_hash.txt - - test $SOLC_EMSCRIPTEN != On || (scripts/build_emscripten.sh) - - test $SOLC_DOCKER != On || (scripts/docker_build.sh) - - test $SOLC_RELEASE != On || (scripts/build.sh $SOLC_BUILD_TYPE -DBoost_USE_STATIC_LIBS=OFF && scripts/create_source_tarball.sh) - -script: - - test $SOLC_EMSCRIPTEN != On -o $SOLC_TESTS != On || (scripts/test_emscripten.sh) - - test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh) - - test $SOLC_STOREBYTECODE != On || (cd $TRAVIS_BUILD_DIR && scripts/bytecodecompare/storebytecode.sh) - -deploy: - # This is the deploy target for the Emscripten build. - # It publishes the JS file which was compiled as part of the earlier 'build_emscripten.sh' - # step to https://github.com/ethereum/solc-bin/tree/gh-pages/bin. - # Both the build and deploy steps for Emscripten are only run within the Ubuntu - # configurations (not for macOS). That is controlled by conditionals within the bash - # scripts because TravisCI doesn't provide much in the way of conditional logic. - - - provider: script - script: test $SOLC_PUBLISH_EMSCRIPTEN != On || $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh) - skip_cleanup: true - on: - branch: - - develop - - release - # This is the deploy target for the dockerfile. If we are pushing into a develop branch, it will be tagged - # as a nightly and appended the commit of the branch it was pushed in. If we are pushing to master it will - # be tagged as "stable" and given the version tag as well. - - provider: script - script: test $SOLC_DOCKER != On || (scripts/docker_deploy.sh) - skip_cleanup: true - on: - branch: - - develop - - release - - /^v\d/ - # This is the deploy target for the native build (Linux and macOS) - # which generates the source tarball. - # - # This runs for each tag that is created and adds the corresponding files. - - provider: releases - api_key: - secure: PWH37xVBCF0XiSjl+eH7XIdkrfxZXjzvqF4PiBOnD3VnFz+odrdnIwBmCeBYTHTWF8efpp8fmzWJk2UVq1JcpyZiC+SVxO8dx91W2ia1a+wKrEQuDgkUrZBkl5IQNCv0QS81DDQhliyZEaYh8wHO/7RReyMpGpw2U2u85WkFiZ+LdlHEZPfzUeh9lxQ9n8qwFL8Rja+Q05d4cQ8zaVEtofJJT4T6DUWhc3TzuxDYxOmjwg37rC9CkGSLn6VadSh8b3j5R0SZupFsAEvBL/imBLP9r9ewoo7o4p6By3jwiIgH9yNg7LM618xbffcNaYF/KtLBi9uPHfqF7hRD4PlECz+D0PR78nQItOX5HKm1QMg5kCnghRVCA0IVjpV5fiYQnMLM7dCRv34I5b3zLpa69wQ/GLYB2FViqNUfvPeiZTEeIJ2OmATlFx8AH2JoqpY1XJknWb35+vMfa8LSiJJW++SLWeV+ncC92hrvyZ1cy3trepRRZIfyYepxHifnfdWMkddQUJk5b2WS5Fy/TJLZNPeombnpvRhUC38dsYItarKeXTc6k4oADCEDZ2rgGIcEiqRxXV11Y5xHJekLDWzUs+YJNcCuL4pnAP//LOnbnH2w9rLpwhQYSl0anCd097NivAXQJXO2JI/byIYz1kiCVQWnW6EM8+72mLOklf/Qr8k= - - overwrite: true - file_glob: true - file: $TRAVIS_BUILD_DIR/upload/* - skip_cleanup: true - on: - all_branches: true - tags: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 31a36aae21c8..28a6eb2bc442 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9.0) +cmake_minimum_required(VERSION 3.13.0) set(ETH_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}/cmake" CACHE PATH "The the path to the cmake directory") list(APPEND CMAKE_MODULE_PATH ${ETH_CMAKE_DIR}) @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.7.0") +set(PROJECT_VERSION "0.8.0") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) @@ -22,6 +22,7 @@ if (IS_BIG_ENDIAN) 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) # Setup cccache. include(EthCcache) @@ -51,8 +52,27 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens include(EthOptions) configure_project(TESTS) -find_package(Z3 4.6.0) -if (${Z3_FOUND}) +find_package(Z3 4.8.0) +if(${USE_Z3_DLOPEN}) + add_definitions(-DHAVE_Z3) + add_definitions(-DHAVE_Z3_DLOPEN) + find_package(Python3 COMPONENTS Interpreter) + if(${Z3_FOUND}) + get_target_property(Z3_HEADER_HINTS z3::libz3 INTERFACE_INCLUDE_DIRECTORIES) + endif() + find_path(Z3_HEADER_PATH z3.h HINTS ${Z3_HEADER_HINTS}) + if(Z3_HEADER_PATH) + set(Z3_FOUND TRUE) + else() + message(SEND_ERROR "Dynamic loading of Z3 requires Z3 headers to be present at build time.") + endif() + if(NOT ${Python3_FOUND}) + message(SEND_ERROR "Dynamic loading of Z3 requires Python 3 to be present at build time.") + endif() + if(${SOLC_LINK_STATIC}) + message(SEND_ERROR "solc cannot be linked statically when dynamically loading Z3.") + endif() +elseif (${Z3_FOUND}) add_definitions(-DHAVE_Z3) message("Z3 SMT solver found. This enables optional SMT checking with Z3.") endif() diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 78b2ab310312..1d897a53f61b 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -236,7 +236,7 @@ Example: #include ``` -See [this issue](http://stackoverflow.com/questions/614302/c-header-order/614333#614333 "C header order") for the reason: this makes it easier to find missing includes in header files. +See [this issue](https://stackoverflow.com/questions/614302/c-header-order/614333#614333 "C header order") for the reason: this makes it easier to find missing includes in header files. ## 13. Recommended reading diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b591f4e193e..e071a2290dab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ # Contribution Guidelines -Please see our contribution guidelines in [the Solidity documentation](http://solidity.readthedocs.io/en/latest/contributing.html). +Please see our contribution guidelines in [the Solidity documentation](https://docs.soliditylang.org/en/latest/contributing.html). Thank you for your help! diff --git a/Changelog.md b/Changelog.md index 14d8f5eddcd8..1767150c99a5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,251 @@ +### 0.8.0 (2020-12-16) + +Breaking Changes: + * Code Generator: All arithmetic is checked by default. These checks can be disabled using ``unchecked { ... }``. + * Code Generator: Cause a panic if a byte array in storage is accessed whose length is encoded incorrectly. + * Code Generator: Use ``revert`` with error signature ``Panic(uint256)`` and error codes instead of invalid opcode on failing assertions. + * Command Line Interface: JSON fields `abi`, `devdoc`, `userdoc` and `storage-layout` are now sub-objects rather than strings. + * Command Line Interface: Remove the ``--old-reporter`` option. + * Command Line Interface: Remove the legacy ``--ast-json`` option. Only the ``--ast-compact-json`` option is supported now. + * General: Enable ABI coder v2 by default. + * General: Remove global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``. + * Parser: Exponentiation is right associative. ``a**b**c`` is parsed as ``a**(b**c)``. + * Scanner: Remove support for the ``\b``, ``\f``, and ``\v`` escape sequences. + * Standard JSON: Remove the ``legacyAST`` option. + * Type Checker: Function call options can only be given once. + * Type System: Declarations with the name ``this``, ``super`` and ``_`` are disallowed, with the exception of public functions and events. + * Type System: Disallow ``msg.data`` in ``receive()`` function. + * Type System: Disallow ``type(super)``. + * Type System: Disallow enums with more than 256 members. + * Type System: Disallow explicit conversions from negative literals and literals larger than ``type(uint160).max`` to ``address`` type. + * Type System: Disallow the ``byte`` type. It was an alias to ``bytes1``. + * Type System: Explicit conversion to ``address`` type always returns a non-payable ``address`` type. In particular, ``address(u)``, ``address(b)``, ``address(c)`` and ``address(this)`` have the type ``address`` instead of ``address payable`` (Here ``u``, ``b``, and ``c`` are arbitrary variables of type ``uint160``, ``bytes20`` and contract type respectively.) + * Type System: Explicit conversions between two types are disallowed if it changes more than one of sign, width or kind at the same time. + * Type System: Explicit conversions from literals to enums are only allowed if the value fits in the enum. + * Type System: Explicit conversions from literals to integer type is as strict as implicit conversions. + * Type System: Introduce ``address(...).code`` to retrieve the code as ``bytes memory``. The size can be obtained via ``address(...).code.length``, but it will currently always include copying the code. + * Type System: Introduce ``block.chainid`` for retrieving the current chain id. + * Type System: Support ``address(...).codehash`` to retrieve the codehash of an account. + * Type System: The global variables ``tx.origin`` and ``msg.sender`` have type ``address`` instead of ``address payable``. + * Type System: Unary negation can only be used on signed integers, not on unsigned integers. + * View Pure Checker: Mark ``chainid`` as view. + * Yul: Disallow the use of reserved identifiers, such as EVM instructions, even if they are not available in the given dialect / EVM version. + * Yul: The ``assignimmutable`` builtin in the "EVM with objects" dialect takes the base offset of the code to modify as an additional argument. + +Language Features: + * Super constructors can now be called using the member notation e.g. ``M.C(123)``. + +Bugfixes: + * Type Checker: Perform proper truncating integer arithmetic when using constants in array length expressions. + +AST Changes: + * New AST Node ``IdentifierPath`` replacing in many places the ``UserDefinedTypeName``. + * New AST Node ``UncheckedBlock`` used for ``unchecked { ... }``. + +### 0.7.6 (2020-12-16) + +Language Features: + * Code generator: Support conversion from calldata slices to memory and storage arrays. + * Code generator: Support copying dynamically encoded structs from calldata to memory. + * Code generator: Support copying of nested arrays from calldata to memory. + * Scanner: Generate a parser error when comments or unicode strings contain an unbalanced or underflowing set of unicode direction override markers (LRO, RLO, LRE, RLE, PDF). + * The fallback function can now also have a single ``calldata`` argument (equaling ``msg.data``) and return ``bytes memory`` (which will not be ABI-encoded but returned as-is). + * Wasm backend: Add ``i32.select`` and ``i64.select`` instructions. + +Compiler Features: + * Build System: Optionally support dynamic loading of Z3 and use that mechanism for Linux release builds. + * Code Generator: Avoid memory allocation for default value if it is not used. + * SMTChecker: Apply constant evaluation on binary arithmetic expressions. + * SMTChecker: Create underflow and overflow verification targets for increment/decrement in the CHC engine. + * SMTChecker: Report struct values in counterexamples from CHC engine. + * SMTChecker: Support early returns in the CHC engine. + * SMTChecker: Support getters. + * SMTChecker: Support named arguments in function calls. + * SMTChecker: Support struct constructor. + * Standard-Json: Move the recently introduced ``modelCheckerSettings`` key to ``settings.modelChecker``. + * Standard-Json: Properly filter the requested output artifacts. + +Bugfixes: + * Code generator: Do not pad empty string literals with a single 32-byte zero field in the ABI coder v1. + * NatSpec: Fix segfault when inheriting return parameter documentation for modifiers with no parameters. + * SMTChecker: Fix cast string literals to byte arrays. + * SMTChecker: Fix internal compiler error when doing bitwise compound assignment with string literals. + * SMTChecker: Fix internal error when trying to generate counterexamples with old z3. + * SMTChecker: Fix segmentation fault that could occur on certain SMT-enabled sources when no SMT solver was available. + * SMTChecker: Fix internal error when ``bytes.push()`` is used as the LHS of an assignment. + * Type Checker: ``super`` is not available in libraries. + * Type Checker: Disallow leading zeroes in sized-types (e.g. ``bytes000032``), but allow them to be treated as identifiers. + * Yul Optimizer: Fix a bug in NameSimplifier where a new name created by NameSimplifier could also be created by NameDispenser. + * Yul Optimizer: Removed NameSimplifier from optimization steps available to users. + +### 0.7.5 (2020-11-18) + +Language Features: + * Ability to select the abi coder using ``pragma abicoder v1`` and ``pragma abicoder v2``. + * Inline Assembly: Use ``.offset`` and ``.length`` for calldata variables of dynamic array type to access their calldata offset and length (number of elements). Both of them can also be assigned to. + * Immutable variables with literal number values are considered pure. + +Compiler Features: + * Assembler: Perform linking in assembly mode when library addresses are provided. + * Command Line Interface: New option ``--experimental-via-ir`` allows switching compilation process to go through the Yul intermediate representation. This is highly experimental and is used for development purposes. + * Command Line Interface: New option ``--model-checker-timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker. + * Command Line Interface: Report error if file could not be read in ``--standard-json`` mode. + * Command Line interface: Report proper error for each output file which could not be written. Previously an exception was thrown, and execution aborted, on the first error. + * SMTChecker: Add division by zero checks in the CHC engine. + * SMTChecker: More precise analysis of external calls using ``this``. + * SMTChecker: Support ``selector`` for expressions with value known at compile-time. + * Standard JSON: New option ``modelCheckerSettings.timeout`` sets a timeout in milliseconds for each individual query performed by the SMTChecker. + * Standard JSON: New option ``settings.viaIR`` allows the same switch as ``--experimental-via-ir`` on the commandline. + + +Bugfixes: + * Code generator: Fix missing creation dependency tracking for abstract contracts. + * Command Line Interface: Fix write error when the directory passed to ``--output-dir`` ends with a slash. + * Command Line Interface: Reject duplicate libraries in ``--libraries`` option instead of arbitrarily choosing one. + * NatSpec: Fix internal error when inheriting return parameter documentation but the parameter names differ between base and inherited. + * SMTChecker: Fix CHC false positives when branches are used inside modifiers. + * SMTChecker: Fix false negative in modifier applied multiple times. + * SMTChecker: Fix incorrect counterexamples reported by the CHC engine. + * SMTChecker: Fix internal error in the BMC engine when inherited contract from a different source unit has private state variables. + * SMTChecker: Fix internal error on conversion from string literal to byte. + * SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment. + * SMTChecker: Fix internal error when assigning state variable via contract's name. + * SMTChecker: Fix internal error when using tuples of rational literals inside the conditional operator. + * SMTChecker: Fix lack of reporting potential violations when using only the CHC engine. + * Standard JSON: Fix library addresses specified in ``libraries`` being used for linking even if the file names do not match. + +AST Changes: + * New member ``suffix`` for inline assembly identifiers. Currently supported values are ``"slot"``, ``"offset"`` and ``"length"`` to access the components of a Solidity variable. + + +### 0.7.4 (2020-10-19) + +Important Bugfixes: + * Code Generator: Fix data corruption bug when copying empty byte arrays from memory or calldata to storage. + + +Language Features: + * Constants can be defined at file level. + + +Compiler Features: + * Command Line Interface: New option ``--model-checker-engine`` allows to choose a specific SMTChecker engine. Options are ``all`` (default), ``bmc``, ``chc`` and ``none``. + * Control Flow Graph: Print warning for non-empty functions with unnamed return parameters that are not assigned a value in all code paths. + * SMTChecker: Support ``keccak256``, ``sha256``, ``ripemd160`` and ``ecrecover`` in the CHC engine. + * SMTChecker: Support inline arrays. + * SMTChecker: Support variables ``block``, ``msg`` and ``tx`` in the CHC engine. + * Standard JSON: New option ``modelCheckerSettings.engine`` allows to choose a specific SMTChecker engine. Options are ``all`` (default), ``bmc``, ``chc`` and ``none``. + + +Bugfixes: + * Code generator: Fix ``ABIEncoderV2`` pragma from the current module affecting inherited functions and applied modifiers. + * Code generator: Fix internal compiler error when referencing members via module name but not using the reference. + * Code generator: Fix internal error on returning structs containing mappings from library function. + * Code generator: Use revert instead of invalid opcode for out-of-bounds array index access in getter. + * Contract Level Checker: Add missing check against inheriting functions with ABIEncoderV2 return types in ABIEncoderV1 contracts. + * Name Resolver: Fix shadowing/same-name warnings for later declarations. + * Type Checker: Allow arrays of contract types as type expressions and as arguments for ``abi.decode``. + * Type Checker: Disallow invalid use of library names as type name. + * Type Checker: Fix internal compiler error caused by storage parameters with nested mappings in libraries. + + +### 0.7.3 (2020-10-07) + +Important Bugfixes: + * Code Generator: Properly cleanup after copying dynamic-array to storage for packed types. + +Compiler Features: + * Code generator: Implemented events with function type as one of its indexed parameters. + * General: Option to stop compilation after parsing stage. Can be used with ``solc --stop-after parsing`` + * Optimizer: Optimize ``exp`` when base is ``-1``. + * SMTChecker: Support ``addmod`` and ``mulmod``. + * SMTChecker: Support array slices. + * SMTChecker: Support type conversions. + + +Bugfixes: + * Fixed internal compiler errors for certain contracts involving the ``new`` expression. + * JSON AST: Fix internal error when using ``--ast-json`` on a function with memory arguments in ABIEncoderV2 contracts. + * Type Checker: Add missing checks for calls using types incompatible with ABIEncoderV1 in modules where ABIEncoderV2 is not enabled. + * Type Checker: Fix internal compiler error when calling `.push()` for a storage array with a nested mapping. + + +### 0.7.2 (2020-09-28) + +Important Bugfixes: + * Type Checker: Disallow two or more free functions with identical name (potentially imported and aliased) and parameter types. + +Compiler Features: + * Export compiler-generated utility sources via standard-json or combined-json. + * Optimizer: Optimize ``exp`` when base is 0, 1 or 2. + * SMTChecker: Keep knowledge about string literals, even through assignment, and thus support the ``.length`` property properly. + * SMTChecker: Support ``address`` type conversion with literals, e.g. ``address(0)``. + * SMTChecker: Support ``revert()``. + * SMTChecker: Support ``type(T).min``, ``type(T).max``, and ``type(I).interfaceId``. + * SMTChecker: Support compound and, or, and xor operators. + * SMTChecker: Support events and low-level logs. + * SMTChecker: Support fixed bytes index access. + * SMTChecker: Support memory allocation, e.g. ``new bytes(123)``. + * SMTChecker: Support shifts. + * SMTChecker: Support structs. + * Type Checker: Explain why oversized hex string literals can not be explicitly converted to a shorter ``bytesNN`` type. + * Type Checker: More detailed error messages why implicit conversions fail. + * Type Checker: Report position of first invalid UTF-8 sequence in ``unicode""`` literals. + * Yul IR Generator: Report source locations related to unimplemented features. + * Yul Optimizer: Inline into functions further down in the call graph first. + * Yul Optimizer: Prune unused parameters in functions. + * Yul Optimizer: Try to simplify function names. + + +Bugfixes: + * Code generator: Fix internal error on stripping dynamic types from return parameters on EVM versions without ``RETURNDATACOPY``. + * Type Checker: Add missing check against nested dynamic arrays in ABI encoding functions when ABIEncoderV2 is disabled. + * Type Checker: Correct the error message for invalid named parameter in a call to refer to the right argument. + * Type Checker: Disallow ``virtual`` for modifiers in libraries. + * Name Resolver: Correct the warning for homonymous, but not shadowing declarations. + * Type system: Fix internal error on implicit conversion of contract instance to the type of its ``super``. + * Type system: Fix internal error on implicit conversion of string literal to a calldata string. + * Type system: Fix named parameters in overloaded function and event calls being matched incorrectly if the order differs from the declaration. + * ViewPureChecker: Prevent visibility check on constructors. + + +### 0.7.1 (2020-09-02) + +Language Features: + * Allow function definitions outside of contracts, behaving much like internal library functions. + * Code generator: Implementing copying structs from calldata to storage. + +Compiler Features: + * SMTChecker: Add underflow and overflow as verification conditions in the CHC engine. + * SMTChecker: Support bitwise or, xor and not operators. + * SMTChecker: Support conditional operator. + * Standard JSON Interface: Do not run EVM bytecode code generation, if only Yul IR or EWasm output is requested. + * Yul Optimizer: LoopInvariantCodeMotion can move reading operations outside for-loops as long as the affected area is not modified inside the loop. + * Yul: Report error when using non-string literals for ``datasize()``, ``dataoffset()``, ``linkersymbol()``, ``loadimmutable()``, ``setimmutable()``. + +Bugfixes: + * AST: Remove ``null`` member values also when the compiler is used in standard-json-mode. + * General: Allow `type(Contract).name` for abstract contracts and interfaces. + * Immutables: Disallow assigning immutables more than once during their declaration. + * Immutables: Properly treat complex assignment and increment/decrement as both reading and writing and thus disallow it everywhere for immutable variables. + * Optimizer: Keep side-effects of ``x`` in ``byte(a, shr(b, x))`` even if the constants ``a`` and ``b`` would make the expression zero unconditionally. This optimizer rule is very hard if not impossible to trigger in a way that it can result in invalid code, though. + * References Resolver: Fix internal bug when using constructor for library. + * Scanner: Fix bug where whitespace would be allowed within the ``->`` token (e.g. ``function f() - > x {}`` becomes invalid in inline assembly and Yul). + * SMTChecker: Fix internal error in BMC function inlining. + * SMTChecker: Fix internal error on array implicit conversion. + * SMTChecker: Fix internal error on fixed bytes index access. + * SMTChecker: Fix internal error on lvalue unary operators with tuples. + * SMTChecker: Fix internal error on tuple assignment. + * SMTChecker: Fix internal error on tuples of one element that have tuple type. + * SMTChecker: Fix internal error when using imported code. + * SMTChecker: Fix soundness of array ``pop``. + * Type Checker: Disallow ``using for`` directive inside interfaces. + * Type Checker: Disallow signed literals as exponent in exponentiation operator. + * Type Checker: Disallow structs containing nested mapping in memory as parameters for library functions. + * Yul Optimizer: Ensure that Yul keywords are not mistakenly used by the NameDispenser and VarNameCleaners. The bug would manifest as uncompilable code. + * Yul Optimizer: Make function inlining order more resilient to whether or not unrelated source files are present. + + ### 0.7.0 (2020-07-28) Breaking changes: diff --git a/README.md b/README.md index 3ffe3f4761fa..babce55fabcb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # The Solidity Contract-Oriented Programming Language -You can talk to us on [![solidity at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge). Questions, feedback and suggestions are welcome! +You can talk to us on [![solidity at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge). Questions, feedback, and suggestions are welcome! Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Tron platform. @@ -19,17 +19,17 @@ Solidity is a statically typed, contract-oriented, high-level language for imple Solidity is a statically-typed curly-braces programming language designed for developing smart contracts that run on the Tron 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, -ownership, voting and other kinds of logics. +ownership, voting, and other kinds of logic. When deploying contracts, you should use the latest released version of -Solidity. This is because breaking changes as well as new features and bug fixes are +Solidity. This is because breaking changes, as well as new features and bug fixes are introduced regularly. We currently use a 0.x version number [to indicate this fast pace of change](https://semver.org/#spec-item-4). ## Build and Install Instructions about how to build and install the Solidity compiler can be -found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source). +found in the [Solidity documentation](https://docs.soliditylang.org/en/latest/installing-solidity.html#building-from-source). ## Example @@ -46,23 +46,23 @@ contract HelloWorld { } ``` -To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is an +To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is a browser-based IDE. Here are some example contracts: -1. [Voting](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#voting) -2. [Blind Auction](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#blind-auction) -3. [Safe remote purchase](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#safe-remote-purchase) -4. [Micropayment Channel](https://solidity.readthedocs.io/en/latest/solidity-by-example.html#micropayment-channel) +1. [Voting](https://docs.soliditylang.org/en/latest/solidity-by-example.html#voting) +2. [Blind Auction](https://docs.soliditylang.org/en/latest/solidity-by-example.html#blind-auction) +3. [Safe remote purchase](https://docs.soliditylang.org/en/latest/solidity-by-example.html#safe-remote-purchase) +4. [Micropayment Channel](https://docs.soliditylang.org/en/latest/solidity-by-example.html#micropayment-channel) ## Documentation -The Solidity documentation is hosted at [Read the docs](https://solidity.readthedocs.io). +The Solidity documentation is hosted at [Read the docs](https://docs.soliditylang.org). ## Development Solidity is still under development. Contributions are always welcome! Please follow the -[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html) +[Developers Guide](https://docs.soliditylang.org/en/latest/contributing.html) if you want to help. You can find our current feature and bug priorities for forthcoming diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 8cdc00591906..a69f1a9d39e2 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -6,6 +6,9 @@ - [ ] Readthedocs account, access to the Solidity project - [ ] Write access to https://github.com/ethereum/homebrew-ethereum +### Documentation check + - [ ] Run `make linkcheck` from within `docs/` and fix any broken links it finds. Ignore false positives caused by `href` anchors and dummy links not meant to work. + ### Blog Post - [ ] Create a post on https://github.com/ethereum/solidity-blog and explain some of the new features or concepts. @@ -17,48 +20,52 @@ ### Create the Release - [ ] Create Github release page: https://github.com/ethereum/solidity/releases/new - - [ ] On the release page, select the ``release`` branch as new target and set tag to the new version (e.g. `v0.5.4`) (make sure you only `SAVE DRAFT` instead of `PUBLISH RELEASE` before the actual release) - - [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``). - - [ ] Create a pull request from ``develop`` to ``release``, wait for the tests, then merge it. + - [ ] On the release page, select the ``develop`` branch as new target and set tag to the new version (e.g. `v0.5.4`) (make sure you only `SAVE DRAFT` instead of `PUBLISH RELEASE` before the actual release) + - [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e v0.5.3..origin/develop``). - [ ] Make a final check that there are no platform-dependency issues in the ``solidity-test-bytecode`` repository. - - [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag (click the `PUBLISH RELEASE` button on the release page.) - - [ ] Wait for the CI runs on the tag itself (travis and appveyor should push artifacts onto the Github release page). - - [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. Make sure to create ``prerelease.txt`` before: (``echo -n > prerelease.txt``). 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. + - [ ] Check that all tests on the latest commit in ``develop`` are green. + - [ ] Click the `PUBLISH RELEASE` button on the release page, creating the tag. + - [ ] Wait for the CI runs on the tag itself (travis will push the source archive and the static linux binary onto the Github release page). -### Homebrew and MacOS - - [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb - - [ ] Take the binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``. +### Download Binaries + - [ ] 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``. + - [ ] If not done by travis: 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 - - [ ] Copy ``soljson.js`` from the release page to ``solc-bin/bin/soljson-v+commit..js`` - - [ ] Copy ``solc-static-linux`` from the release page to ``solc-bin/linux-amd64/solc-linux-amd64-v+commit.`` - - [ ] Copy ``solc-macos`` from the release page to ``solc-bin/macos-amd64/solc-macos-amd64-v+commit.`` - - [ ] Copy ``solc-windows.zip`` from the release page to ``solc-bin/windows-amd64/solc-windows-amd64-v+commit..zip`` - - [ ] Make the linux and the macos binaries executable. - - [ ] Run ``./update`` 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/``. +### Update [solc-bin](https://github.com/ethereum/solc-bin/) + - [ ] Copy ``soljson.js`` to ``solc-bin/bin/soljson-v$VERSION+commit.$COMMIT.js`` + - [ ] Copy ``solc-static-linux`` from the release page to ``solc-bin/linux-amd64/solc-linux-amd64-v$VERSION+commit.$COMMIT`` + - [ ] Make it executable. + - [ ] Copy ``solc-macos`` from the release page to ``solc-bin/macosx-amd64/solc-macosx-amd64-v$VERSION+commit.$COMMIT`` + - [ ] Make it executable. + - [ ] Copy ``solc-windows.exe`` from the release page to ``solc-bin/windows-amd64/solc-windows-amd64-v$VERSION+commit.$COMMIT.exe`` + - [ ] 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. -### PPA - - [ ] Change ``scripts/release_ppa.sh`` to match your key's email and key id. - - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). - - [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``. +### 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 + - [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb ### Docker - - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``). + - [ ] Run ``./scripts/docker_deploy_manual.sh v$VERSION``). + +### PPA + - [ ] Change ``scripts/release_ppa.sh`` to match your key's email and key id. + - [ ] Run ``scripts/release_ppa.sh v$VERSION`` to create the PPA release (you need the relevant openssl key). + - [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty``, ``Xenial`` and ``Bionic`` while selecting ``Copy existing binaries``. ### Documentation - - [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`) + - [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` at the bottom of the page and click `BUILD`) - [ ] In the admin panel, select `Versions` in the menu and set the default version to the released one. ### Release solc-js + - [ ] Wait until solc-bin was properly deployed. You can test this via remix - a test run through remix is advisable anyway. - [ ] Increment the version number, create a pull request for that, merge it after tests succeeded. - [ ] Run ``npm publish`` in the updated ``solc-js`` repository. - - [ ] Make sure to push the tag ``npm publish`` created with ``git push --tags``. + - [ ] Create a tag using ``git tag --annotate v$VERSION`` and push it with ``git push --tags``. ### Post-release - [ ] Publish the blog post. - [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry. - - [ ] Merge ``release`` back into ``develop``. - [ ] Announce on Twitter and Reddit. - [ ] Lean back, wait for bug reports and repeat from step 1 :) diff --git a/SECURITY.md b/SECURITY.md index dd39453e853b..d319b332f26f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -48,5 +48,5 @@ The Solidity team maintains the following JSON-formatted lists of patched securi [1]: https://bounty.ethereum.org/ [2]: https://bounty.ethereum.org/#rules -[3]: https://solidity.readthedocs.io/en/develop/bugs.html +[3]: https://docs.soliditylang.org/en/develop/bugs.html [4]: https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index d7211e14b4b1..000000000000 --- a/appveyor.yml +++ /dev/null @@ -1,110 +0,0 @@ -#------------------------------------------------------------------------------ -# Appveyor configuration file for solidity. -# -# The documentation for solidity is hosted at: -# -# http://solidity.readthedocs.org -# -# ------------------------------------------------------------------------------ -# This file is part of solidity. -# -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2016 solidity contributors. -#------------------------------------------------------------------------------ - -branches: - only: - - release - - develop -configuration: - - Release -environment: - matrix: - - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - # This is used for pushing to solidity-test-bytecodes - priv_key: - secure: yYGwg4rhCdHfwuv2mFjaNEDwAx3IKUbp0D5fMGpaKefnfk+BiMS5bqSHRiOj91PZ91P9pUk2Vu+eNuS4hTFCf1zFGfrOhlJ4Ij0xSyU5m/LQr590Mo+f7W94Xc8ubgo6j2hp9qH/szTqTzmAkmxKO5TLlWjVzVny2t/s5o5UprLS1/MdzDNLjpVNXR03oKfdWUV9a2l6+PejXCbqyUCagh6BByZqeAPbDcil6eAfxu4EPX83Fuurof+KqFzIWycBG5qK1pTipn2pxiA0QKuUrD8y8VNL0S23NTgxoxSp7nPVMd3K0qRSzPM5lrqS7Z8i3evkVwPbuhu0gSiV08jGVahH2snQ3JGYsH2D4KmVn/xiVBeJ0lRplYlfZF0GUu7iJ+DDxi6wBPhW9A25/NyD/mx7Ub2dLheyWi8AjdSCzhfRD+4We8FQQeHRo3Q0kAohFmlCXdXhrcwOOloId8r6xYwg+hWxHTt2Oe9CKwXfmiPjgl/Gd6lYgLpyyfJ8drQ6tjO/pybLEa10v74qYNdVW5LaLIsRUM9Jm/FDVTrOGYtPndi87mF+/tBJIaXXNz0EMl5xvsKW0SBfUMV49zoDDKZZgWyO9U/cfViEUi7Sdn9QLsBWLZfSgBQNkq3WGZVKPq58OxEWT9dUghQHlSVh2qWF/NUx0TRBjiJl9JM56ENTMD00y18eDcXNCeLLVYB+R1axabUPdXivrO+BrWQK94IWxKEJ+YYN8WVJWAO5T/EBDKwgiXGneePwJ75WP7XCLtuYxqjC+CeW3xBVCzCEeZB/VVBvt7fhmtcoeZZ6tAS10h0yY5WWZ/EUVorj+c/FrMm7Nlpcrd1p4hciffePSLVg+yvy9/xTuM9trYWMgj4xcDQbYsaeItHO2Z3EiUoCgNdUw6rONiNwS/XBApWhCcklWm0/g62h2gOa7/hnKG6p2omQzYw+cOzWbF9+DBzoTSXXZXqbUshVee+CD+iYJKleGYSdbMdM89HW4HyskHk6HgM1ggE8CsgD1pMhXtqLTYZBlvsZCBkHPkD9NhGD2DtrNOmJOW8xwkL2/Il6roDF4n856XNdsjvd++rvQoKr58SkyApCJeCo3sfVres0W22g+7If2b2kWC4/DphrFkeaceFzJOctBUrwstvQBXIVOcadU978A3E7jvTaMR4JL9kC/iPOUVNjNRNM/gNvTlf3CIyMMszFeftjEBGnCZaSpht2RtNapRQQb6QPkOP88nufQVZq/TP1ECmvdTUWJ7kSnAupu6u8oH2x2IIm/KKeIwSYU5rGxjRb36DwgXCHcwfRYo3VNorwTeZGj4q1TSM9PuvgzNg//gKZW6VRa+HdNm/40ZGpDsOrr55tOBqfpq9k5RmevqW/OMZS3xUuArKdYLQY75t9eWcbHSgFN2ZY1KEdyEEvVKgs6Q4lEnSSulGxroRxTU5BOoA0V4tCeCUoSPD3FB93WsO9fBPzNsqOuBtDdIkApefzc1pT38uKpmVfggKUsoWUdqMXAWqCDWr2uw9EE900RJpEY6mIEWhkcro5LAMwaqByOGpqFFUkH+UWTC102eVHEmjxKpC6c6cSzoKKU6Ckd+jVRFO7TvmVe1MKCwjXj8lcAfAM2gQ+XehtrQdIBhAmCrnzurfz2u9tKVdpiADC1ig+kMs1/HX2713LYVXzDKdk+duQ94SVtGv9F2Iv+KN5oq4UFgll6VGt7GHsJOrYYf/wrOfB09IkpmjNygvcpmmSdcXXF8ulDD6KHTGEGUlFwLOpEwKx+zX2ZvviStHhN8KsoTKSVSueDmSSI63HdTS7FxfrHJc1yAzsdqEN5g5eV/z2Fn34qy64mdFSAZMF5zsbWZYFpc9ef3llF5aRcuD90JWT2VC7rB2jeGEtiwGkDlqKzxqRvJk06wTK6+n5RncN66bDaksulOPJMAR/bRW7dinV8T6yIvybuhqDetxJQP6eyAnW4xr1YxIAG4BXGZV6XAPTgOG2oGvMdncxkcLQHXVu07x39ySqP/m2MBxn0zF3DmaqrSPIRMhS8gG3d/23Jux3YHDEOBHjdJSdwqs5F5+QBFPV2rmJnpcSoW4d3M119XI20L914c62R7wY4e6+qmi3ydQU9g6p8psZgaE3TuMsyzX3k4C30nC/3gWT+zl253NjZwfbzIdHu5LWNDY9kEHtKzLP -# NB: Appveyor cache is disabled, because it is proving very unreliable. -# We can re-enable it when we find a way to mitigate the unreliability -# issues. Have automated builds be reliable is the more important thing. -#cache: build -# -# In case we'd need a RDP detail to login into appveyor -#init: -# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) -install: - - ps: if ($env:priv_key) { - $fileContent = "-----BEGIN RSA PRIVATE KEY-----`n"; - $fileContent += $env:priv_key.Replace(' ', "`n"); - $fileContent += "`n-----END RSA PRIVATE KEY-----`n"; - Set-Content c:\users\appveyor\.ssh\id_rsa $fileContent - } - - ps: $prerelease = "nightly." - - ps: $prerelease += Get-Date -format "yyyy.M.d" - - ps: if($env:appveyor_repo_branch -eq 'release') { Set-Content prerelease.txt $null } else { Set-Content prerelease.txt $prerelease } - - scripts/install_deps.bat - - set ETHEREUM_DEPS_PATH=%APPVEYOR_BUILD_FOLDER%\deps\install -before_build: - - if not exist build mkdir build - - cd build - - cmake -G "Visual Studio 15 2017 Win64" .. -DTESTS=On -build_script: - - msbuild solidity.sln /p:Configuration=%CONFIGURATION% /m:%NUMBER_OF_PROCESSORS% /v:minimal - - cd %APPVEYOR_BUILD_FOLDER% - - scripts\release.bat %CONFIGURATION% 2017 - - ps: $bytecodedir = git show -s --format="%cd-%H" --date="format:%Y-%m-%d-%H-%M" - -test_script: - - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% - - soltest.exe --show-progress -- --testpath %APPVEYOR_BUILD_FOLDER%\test --no-smt -# Skip bytecode compare if private key is not available - - cd %APPVEYOR_BUILD_FOLDER% - - ps: if ($env:priv_key -and -not $env:APPVEYOR_PULL_REQUEST_HEAD_COMMIT) { - scripts\bytecodecompare\storebytecode.bat $Env:CONFIGURATION $bytecodedir - } - - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% - -artifacts: - - path: solidity-windows.zip - name: solidity-windows-zip - -# This is the deploy target for Windows which generates ZIPs per commit. -# We are in agreement that generating ZIPs per commit for the develop -# branch is probably just noise, so we only run this deployment target -# on 'release'. -# -# See https://www.appveyor.com/docs/deployment/github for information -# on GitHub Releases in Appveyor. -# -# You need to generate a GitHub personal access token for Appveyor -# See https://github.com/settings/tokens for more information on that. -# The token you generate there (in an encrypted form) is what is -# passed to this deployment target in the 'auth_token' parameter -# below. - -deploy: - provider: GitHub - auth_token: - secure: HPjiugbDSCsEFTphj/qwHuSw80/BV1xWoSvj95CPmtb16Ukh2VQbLVB7iFtZSans - artifact: solidity-windows-zip - on: - branch: release - appveyor_repo_tag: true - -notifications: - - provider: GitHubPullRequest - on_build_success: false - on_build_failure: false - on_build_status_changed: false diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 3bcdad47bec7..bbf631ca631b 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -48,9 +48,22 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA 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) + + eth_add_cxx_compiler_flag_if_supported( + $<$:-Wextra-semi> + ) + 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") @@ -60,12 +73,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Additional GCC-specific compiler settings. if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") - - # Check that we've got GCC 5.0 or newer. - execute_process( - COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - if (NOT (GCC_VERSION VERSION_GREATER 5.0 OR GCC_VERSION VERSION_EQUAL 5.0)) - message(FATAL_ERROR "${PROJECT_NAME} requires g++ 5.0 or greater.") + # Check that we've got GCC 8.0 or newer. + if (NOT (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)) + message(FATAL_ERROR "${PROJECT_NAME} requires g++ 8.0 or greater.") endif () # Use fancy colors in the compiler diagnostics @@ -73,6 +83,11 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Additional Clang-specific compiler settings. elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + # Check that we've got clang 7.0 or newer. + if (NOT (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0)) + message(FATAL_ERROR "${PROJECT_NAME} requires clang++ 7.0 or greater.") + endif () + if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") # Set stack size to 32MB - by default Apple's clang defines a stack size of 8MB. # Normally 16MB is enough to run all tests, but it will exceed the stack, if -DSANITIZE=address is used. @@ -157,9 +172,10 @@ elseif (DEFINED MSVC) add_compile_options(/wd4800) # disable forcing value to bool 'true' or 'false' (performance warning) (4800) add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions - add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819) + add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819) add_compile_options(-DBOOST_REGEX_NO_LIB) # disable automatic boost::regex library selection add_compile_options(-D_REGEX_MAX_STACK_COUNT=200000L) # increase std::regex recursion depth limit + add_compile_options(/permissive-) # specify standards conformance mode to the compiler # disable empty object file warning set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221") @@ -203,6 +219,9 @@ endif() # SMT Solvers integration option(USE_Z3 "Allow compiling with Z3 SMT solver integration" ON) +if(UNIX AND NOT APPLE) + option(USE_Z3_DLOPEN "Dynamically load the Z3 SMT solver instead of linking against it." OFF) +endif() option(USE_CVC4 "Allow compiling with CVC4 SMT solver integration" ON) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index d8bca8a31410..04cb876beae6 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -25,6 +25,9 @@ set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts) ## use multithreaded boost libraries, with -mt suffix set(Boost_USE_MULTITHREADED ON) option(Boost_USE_STATIC_LIBS "Link Boost statically" ON) +if(WIN32) + option(Boost_USE_STATIC_RUNTIME "Link Boost against static C++ runtime libraries" ON) +endif() set(BOOST_COMPONENTS "filesystem;unit_test_framework;program_options;system") diff --git a/cmake/EthPolicy.cmake b/cmake/EthPolicy.cmake index 4e29898cb838..cc404e794212 100644 --- a/cmake/EthPolicy.cmake +++ b/cmake/EthPolicy.cmake @@ -15,5 +15,9 @@ macro (eth_policy) # do not interpret if() arguments as variables! cmake_policy(SET CMP0054 NEW) endif() -endmacro() + if (POLICY CMP0091) + # Allow selecting MSVC runtime library using CMAKE_MSVC_RUNTIME_LIBRARY. + cmake_policy(SET CMP0091 NEW) + endif() +endmacro() diff --git a/cmake/EthToolchains.cmake b/cmake/EthToolchains.cmake index a4263b7df2b6..c2306bd75d11 100644 --- a/cmake/EthToolchains.cmake +++ b/cmake/EthToolchains.cmake @@ -1,3 +1,10 @@ +# Require C++17. +if (NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. +endif () +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_CXX_EXTENSIONS OFF) + if(NOT CMAKE_TOOLCHAIN_FILE) # Use default toolchain file if none is provided. set( diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake index 80a16f8d57c8..29b8f5f05138 100644 --- a/cmake/jsoncpp.cmake +++ b/cmake/jsoncpp.cmake @@ -34,6 +34,12 @@ if(CMAKE_VERSION VERSION_GREATER 3.1) set(byproducts BUILD_BYPRODUCTS "${JSONCPP_LIBRARY}") endif() +# Propagate CMAKE_MSVC_RUNTIME_LIBRARY on Windows builds, if set. +if (WIN32 AND POLICY CMP0091 AND CMAKE_MSVC_RUNTIME_LIBRARY) + list(APPEND JSONCPP_CMAKE_ARGS "-DCMAKE_POLICY_DEFAULT_CMP0091:STRING=NEW") + list(APPEND JSONCPP_CMAKE_ARGS "-DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY}") +endif() + ExternalProject_Add(jsoncpp-project PREFIX "${prefix}" DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/deps/downloads" @@ -51,6 +57,7 @@ ExternalProject_Add(jsoncpp-project -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF -DCMAKE_CXX_FLAGS=${JSONCPP_CXX_FLAGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + ${JSONCPP_CMAKE_ARGS} ${byproducts} ) diff --git a/cmake/templates/ewasm_polyfill.in b/cmake/templates/ewasm_polyfill.in new file mode 100644 index 000000000000..8ac364280d76 --- /dev/null +++ b/cmake/templates/ewasm_polyfill.in @@ -0,0 +1,13 @@ +// The generation of this file is defined in libyul/CMakeLists.txt. +// This file was generated by using the content of libyul/backends/wasm/polyfill/@EWASM_POLYFILL_NAME@.yul. + +#pragma once + +namespace solidity::yul::wasm::polyfill +{ + +static char const @EWASM_POLYFILL_NAME@[] = { + @EWASM_POLYFILL_CONTENT@, 0 +}; + +} // namespace solidity::yul::wasm::polyfill diff --git a/cmake/toolchains/cxx20.cmake b/cmake/toolchains/cxx20.cmake deleted file mode 100644 index ad34e57492cd..000000000000 --- a/cmake/toolchains/cxx20.cmake +++ /dev/null @@ -1,4 +0,0 @@ -# Require C++20. -set(CMAKE_CXX_STANDARD 20) # This requires at least CMake 3.12 to understand this C++20 flag -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/cmake/toolchains/default.cmake b/cmake/toolchains/default.cmake index 07fc80e8e1fd..e69de29bb2d1 100644 --- a/cmake/toolchains/default.cmake +++ b/cmake/toolchains/default.cmake @@ -1,4 +0,0 @@ -# Require C++17. -set(CMAKE_CXX_STANDARD 17) # This requires at least CMake 3.8 to accept this C++17 flag. -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index 35e8b1e2d1b7..61d738e599f0 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -311,7 +311,7 @@ This will no longer compile with Solidity v0.5.0. However, you can define a comp :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; interface OldContract { function someOldFunction(uint8 a) external; function anotherOldFunction() external returns (bool); @@ -329,7 +329,7 @@ Given the interface defined above, you can now easily use the already deployed p :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; interface OldContract { function someOldFunction(uint8 a) external; diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index 3173bca69da7..32397677c687 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -132,7 +132,7 @@ Yul Optimizer Together with the legacy bytecode optimizer, the :doc:`Yul ` optimizer is now enabled by default when you call the compiler with ``--optimize``. It can be disabled by calling the compiler with ``--no-optimize-yul``. -This mostly affects code that uses ABIEncoderV2. +This mostly affects code that uses ABI coder v2. C API Changes ~~~~~~~~~~~~~ diff --git a/docs/080-breaking-changes.rst b/docs/080-breaking-changes.rst new file mode 100644 index 000000000000..5e879a20c7b1 --- /dev/null +++ b/docs/080-breaking-changes.rst @@ -0,0 +1,173 @@ +******************************** +Solidity v0.8.0 Breaking Changes +******************************** + +This section highlights the main breaking changes introduced in Solidity +version 0.8.0. +For the full list check +`the release changelog `_. + +Silent Changes of the Semantics +=============================== + +This section lists changes where existing code changes its behaviour without +the compiler notifying you about it. + +* Arithmetic operations revert on underflow and overflow. You can use ``unchecked { ... }`` to use + the previous wrapping behaviour. + + Checks for overflow are very common, so we made them the default to increase readability of code, + even if it comes at a slight increase of gas costs. + +* ABI coder v2 is activated by default. + + You can choose to use the old behaviour using ``pragma abicoder v1;``. + The pragma ``pragma experimental ABIEncoderV2;`` is still valid, but it is deprecated and has no effect. + If you want to be explicit, please use ``pragma abicoder v2;`` instead. + + Note that ABI coder v2 supports more types than v1 and performs more sanity checks on the inputs. + ABI coder v2 makes some function calls more expensive and it can also make contract calls + revert that did not revert with ABI coder v1 when they contain data that does not conform to the + parameter types. + +* Exponentiation is right associative, i.e., the expression ``a**b**c`` is parsed as ``a**(b**c)``. + Before 0.8.0, it was parsed as ``(a**b)**c``. + + This is the common way to parse the exponentiation operator. + +* Failing assertions and other internal checks like division by zero or arithmetic overflow do + not use the invalid opcode but instead the revert opcode. + More specifically, they will use error data equal to a function call to ``Panic(uint256)`` with an error code specific + to the circumstances. + + This will save gas on errors while it still allows static analysis tools to distinguish + these situations from a revert on invalid input, like a failing ``require``. + +* If a byte array in storage is accessed whose length is encoded incorrectly, a panic is caused. + A contract cannot get into this situation unless inline assembly is used to modify the raw representation of storage byte arrays. + +* If constants are used in array length expressions, previous versions of Solidity would use arbitrary precision + in all branches of the evaluation tree. Now, if constant variables are used as intermediate expressions, + their values will be properly rounded in the same way as when they are used in run-time expressions. + +* The type ``byte`` has been removed. It was an alias of ``bytes1``. + +New Restrictions +================ + +This section lists changes that might cause existing contracts to not compile anymore. + +* There are new restrictions related to explicit conversions of literals. The previous behaviour in + the following cases was likely ambiguous: + + 1. Explicit conversions from negative literals and literals larger than ``type(uint160).max`` to + ``address`` are disallowed. + 2. Explicit conversions between literals and an integer type ``T`` are only allowed if the literal + lies between ``type(T).min`` and ``type(T).max``. In particular, replace usages of ``uint(-1)`` + with ``type(uint).max``. + 3. Explicit conversions between literals and enums are only allowed if the literal can + represent a value in the enum. + 4. Explicit conversions between literals and ``address`` type (e.g. ``address(literal)``) have the + type ``address`` instead of ``address payable``. One can get a payable address type by using an + explicit conversion, i.e., ``payable(literal)``. + +* :ref:`Address literals` have the type ``address`` instead of ``address + payable``. They can be converted to ``address payable`` by using an explicit conversion, e.g. + ``payable(0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF)``. + +* There are new restrictions on explicit type conversions. The conversion is only allowed when there + is at most one change in sign, width or type-category (``int``, ``address``, ``bytesNN``, etc.). + To perform multiple changes, use multiple conversions. + + Let us use the notation ``T(S)`` to denote the explicit conversion ``T(x)``, where, ``T`` and + ``S`` are types, and ``x`` is any arbitrary variable of type ``S``. An example of such a + disallowed conversion would be ``uint16(int8)`` since it changes both width (8 bits to 16 bits) + and sign (signed integer to unsigned integer). In order to do the conversion, one has to go + through an intermediate type. In the previous example, this would be ``uint16(uint8(int8))`` or + ``uint16(int16(int8))``. Note that the two ways to convert will produce different results e.g., + for ``-1``. The following are some examples of conversions that are disallowed by this rule. + + - ``address(uint)`` and ``uint(address)``: converting both type-category and width. Replace this by + ``address(uint160(uint))`` and ``uint(uint160(address))`` respectively. + - ``payable(uint160)``, ``payable(bytes20)`` and ``payable(integer-literal)``: converting both + type-category and state-mutability. Replace this by ``payable(address(uint160))``, + ``payable(address(bytes20))`` and ``payable(address(integer-literal))`` respectively. Note that + ``payable(0)`` is valid and is an exception to the rule. + - ``int80(bytes10)`` and ``bytes10(int80)``: converting both type-category and sign. Replace this by + ``int80(uint80(bytes10))`` and ``bytes10(uint80(int80)`` respectively. + - ``Contract(uint)``: converting both type-category and width. Replace this by + ``Contract(address(uint160(uint)))``. + + These conversions were disallowed to avoid ambiguity. For example, in the expression ``uint16 x = + uint16(int8(-1))``, the value of ``x`` would depend on whether the sign or the width conversion + was applied first. + +* Function call options can only be given once, i.e. ``c.f{gas: 10000}{value: 1}()`` is invalid and has to be changed to ``c.f{gas: 10000, value: 1}()``. + +* The global functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4`` have been removed. + + These are low-level functions that were largely unused. Their behaviour can be accessed from inline assembly. + +* ``enum`` definitions cannot contain more than 256 members. + + This will make it safe to assume that the underlying type in the ABI is always ``uint8``. + +* Declarations with the name ``this``, ``super`` and ``_`` are disallowed, with the exception of + public functions and events. The exception is to make it possible to declare interfaces of contracts + implemented in languages other than Solidity that do permit such function names. + +* Remove support for the ``\b``, ``\f``, and ``\v`` escape sequences in code. + They can still be inserted via hexadecimal escapes, e.g. ``\x08``, ``\x0c``, and ``\x0b``, respectively. + +* The global variables ``tx.origin`` and ``msg.sender`` have the type ``address`` instead of + ``address payable``. One can convert them into ``address payable`` by using an explicit + conversion, i.e., ``payable(tx.origin)`` or ``payable(msg.sender)``. + + This change was done since the compiler cannot determine whether or not these addresses + are payable or not, so it now requires an explicit conversion to make this requirement visible. + +* Explicit conversion into ``address`` type always returns a non-payable ``address`` type. In + particular, the following explicit conversions have the type ``address`` instead of ``address + payable``: + + - ``address(u)`` where ``u`` is a variable of type ``uint160``. One can convert ``u`` + into the type ``address payable`` by using two explicit conversions, i.e., + ``payable(address(u))``. + - ``address(b)`` where ``b`` is a variable of type ``bytes20``. One can convert ``b`` + into the type ``address payable`` by using two explicit conversions, i.e., + ``payable(address(b))``. + - ``address(c)`` where ``c`` is a contract. Previously, the return type of this + conversion depended on whether the contract can receive Ether (either by having a receive + function or a payable fallback function). The conversion ``payable(c)`` has the type ``address + payable`` and is only allowed when the contract ``c`` can receive Ether. In general, one can + always convert ``c`` into the type ``address payable`` by using the following explicit + conversion: ``payable(address(c))``. Note that ``address(this)`` falls under the same category + as ``address(c)`` and the same rules apply for it. + +* The ``chainid`` builtin in inline assembly is now considered ``view`` instead of ``pure``. + +Interface Changes +================= + +* The output of ``--combined-json`` has changed: JSON fields ``abi``, ``devdoc``, ``userdoc`` and + ``storage-layout`` are sub-objects now. Before 0.8.0 they used to be serialised as strings. + +* The "legacy AST" has been removed (``--ast-json`` on the commandline interface and ``legacyAST`` for standard JSON). + Use the "compact AST" (``--ast-compact--json`` resp. ``AST``) as replacement. + +* The old error reporter (``--old-reporter``) has been removed. + + +How to update your code +======================= + +- If you rely on wrapping arithmetic, surround each operation with ``unchecked { ... }``. +- Optional: If you use SafeMath or a similar library, change ``x.add(y)`` to ``x + y``, ``x.mul(y)`` to ``x * y`` etc. +- Add ``pragma abicoder v1;`` if you want to stay with the old ABI coder. +- Optionally remove ``pragma experimental ABIEncoderV2`` or ``pragma abicoder v2`` since it is redundant. +- Change ``byte`` to ``bytes1``. +- Add intermediate explicit type conversions if required. +- Combine ``c.f{gas: 10000}{value: 1}()`` to ``c.f{gas: 10000, value: 1}()``. +- Change ``msg.sender.transfer(x)`` to ``payable(msg.sender).transfer(x)`` or use a stored variable of ``address payable`` type. +- Change ``x**y**z`` to ``(x**y)**z``. +- Use inline assembly as a replacement for ``log0``, ..., ``log4``. \ No newline at end of file diff --git a/docs/Solidity.g4 b/docs/Solidity.g4 deleted file mode 100644 index 91f59d78cb37..000000000000 --- a/docs/Solidity.g4 +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright 2020 Gonçalo Sá -// Copyright 2016-2019 Federico Bond -// Licensed under the MIT license. See LICENSE file in the project root for details. - -// This grammar is much less strict than what Solidity currently parses -// to allow this to pass with older versions of Solidity. - -grammar Solidity; - -sourceUnit - : (pragmaDirective | importDirective | structDefinition | enumDefinition | contractDefinition)* EOF ; - -pragmaDirective - : 'pragma' pragmaName ( ~';' )* ';' ; - -pragmaName - : identifier ; - -importDirective - : 'import' StringLiteralFragment ('as' identifier)? ';' - | 'import' ('*' | identifier) ('as' identifier)? 'from' StringLiteralFragment ';' - | 'import' '{' importDeclaration ( ',' importDeclaration )* '}' 'from' StringLiteralFragment ';' ; - -importDeclaration - : identifier ('as' identifier)? ; - -contractDefinition - : 'abstract'? ( 'contract' | 'interface' | 'library' ) identifier - ( 'is' inheritanceSpecifier (',' inheritanceSpecifier )* )? - '{' contractPart* '}' ; - -inheritanceSpecifier - : userDefinedTypeName ( '(' expressionList? ')' )? ; - -contractPart - : stateVariableDeclaration - | usingForDeclaration - | structDefinition - | modifierDefinition - | functionDefinition - | eventDefinition - | enumDefinition ; - -stateVariableDeclaration - : typeName - ( PublicKeyword | InternalKeyword | PrivateKeyword | ConstantKeyword | ImmutableKeyword | overrideSpecifier )* - identifier ('=' expression)? ';' ; - -overrideSpecifier : 'override' ( '(' userDefinedTypeName (',' userDefinedTypeName)* ')' )? ; - -usingForDeclaration - : 'using' identifier 'for' ('*' | typeName) ';' ; - -structDefinition - : 'struct' identifier - '{' ( variableDeclaration ';' (variableDeclaration ';')* )? '}' ; - -modifierDefinition - : 'modifier' identifier parameterList? ( VirtualKeyword | overrideSpecifier )* ( ';' | block ) ; - -functionDefinition - : functionDescriptor parameterList modifierList returnParameters? ( ';' | block ) ; - -functionDescriptor - : 'function' ( identifier | ReceiveKeyword | FallbackKeyword )? - | ConstructorKeyword - | FallbackKeyword - | ReceiveKeyword ; - -returnParameters - : 'returns' parameterList ; - -modifierList - : ( modifierInvocation | stateMutability | ExternalKeyword - | PublicKeyword | InternalKeyword | PrivateKeyword | VirtualKeyword | overrideSpecifier )* ; - -modifierInvocation - : identifier ( '(' expressionList? ')' )? ; - -eventDefinition - : 'event' identifier eventParameterList AnonymousKeyword? ';' ; - -enumDefinition - : 'enum' identifier '{' enumValue? (',' enumValue)* '}' ; - -enumValue - : identifier ; - -parameterList - : '(' ( parameter (',' parameter)* )? ')' ; - -parameter - : typeName storageLocation? identifier? ; - -eventParameterList - : '(' ( eventParameter (',' eventParameter)* )? ')' ; - -eventParameter - : typeName IndexedKeyword? identifier? ; - -variableDeclaration - : typeName storageLocation? identifier ; - -typeName - : elementaryTypeName - | userDefinedTypeName - | mapping - | typeName '[' expression? ']' - | functionTypeName ; - -userDefinedTypeName - : identifier ( '.' identifier )* ; - -mapping - : 'mapping' '(' mappingKey '=>' typeName ')' ; - -mappingKey - : elementaryTypeName - | userDefinedTypeName ; - -functionTypeName - : 'function' parameterList modifierList returnParameters? ; - -storageLocation - : 'memory' | 'storage' | 'calldata'; - -stateMutability - : PureKeyword | ConstantKeyword | ViewKeyword | PayableKeyword ; - -block - : '{' statement* '}' ; - -statement - : ifStatement - | tryStatement - | whileStatement - | forStatement - | block - | inlineAssemblyStatement - | doWhileStatement - | continueStatement - | breakStatement - | returnStatement - | throwStatement - | emitStatement - | simpleStatement ; - -expressionStatement - : expression ';' ; - -ifStatement - : 'if' '(' expression ')' statement ( 'else' statement )? ; - -tryStatement : 'try' expression returnParameters? block catchClause+ ; - -// In reality catch clauses still are not processed as below -// the identifier can only be a set string: "Error". But plans -// of the Solidity team include possible expansion so we'll -// leave this as is, befitting with the Solidity docs. -catchClause : 'catch' ( identifier? parameterList )? block ; - -whileStatement - : 'while' '(' expression ')' statement ; - -forStatement - : 'for' '(' ( simpleStatement | ';' ) ( expressionStatement | ';' ) expression? ')' statement ; - -simpleStatement - : ( variableDeclarationStatement | expressionStatement ) ; - -inlineAssemblyStatement - : 'assembly' StringLiteralFragment? assemblyBlock ; - -doWhileStatement - : 'do' statement 'while' '(' expression ')' ';' ; - -continueStatement - : 'continue' ';' ; - -breakStatement - : 'break' ';' ; - -returnStatement - : 'return' expression? ';' ; - -// throw is no longer supported by latest Solidity. -throwStatement - : 'throw' ';' ; - -emitStatement - : 'emit' functionCall ';' ; - -// 'var' is no longer supported by latest Solidity. -variableDeclarationStatement - : ( 'var' identifierList | variableDeclaration | '(' variableDeclarationList ')' ) ( '=' expression )? ';'; - -variableDeclarationList - : variableDeclaration? (',' variableDeclaration? )* ; - -identifierList - : '(' ( identifier? ',' )* identifier? ')' ; - -elementaryTypeName - : 'address' PayableKeyword? | 'bool' | 'string' | 'var' | Int | Uint | 'byte' | Byte | Fixed | Ufixed ; - -Int - : 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' ; - -Uint - : 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' ; - -Byte - : 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' ; - -Fixed - : 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) ; - -Ufixed - : 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) ; - -expression - : expression ('++' | '--') - | 'new' typeName - | expression '[' expression? ']' - | expression '[' expression? ':' expression? ']' - | expression '.' identifier - | expression '{' nameValueList '}' - | expression '(' functionCallArguments ')' - | PayableKeyword '(' expression ')' - | '(' expression ')' - | ('++' | '--') expression - | ('+' | '-') expression - | ('after' | 'delete') expression - | '!' expression - | '~' expression - | expression '**' expression - | expression ('*' | '/' | '%') expression - | expression ('+' | '-') expression - | expression ('<<' | '>>') expression - | expression '&' expression - | expression '^' expression - | expression '|' expression - | expression ('<' | '>' | '<=' | '>=') expression - | expression ('==' | '!=') expression - | expression '&&' expression - | expression '||' expression - | expression '?' expression ':' expression - | expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') expression - | primaryExpression ; - -primaryExpression - : BooleanLiteral - | numberLiteral - | hexLiteral - | stringLiteral - | unicodeStringLiteral - | identifier ('[' ']')? - | TypeKeyword - | tupleExpression - | typeNameExpression ('[' ']')? ; - -expressionList - : expression (',' expression)* ; - -nameValueList - : nameValue (',' nameValue)* ','? ; - -nameValue - : identifier ':' expression ; - -functionCallArguments - : '{' nameValueList? '}' - | expressionList? ; - -functionCall - : expression '(' functionCallArguments ')' ; - -tupleExpression - : '(' ( expression? ( ',' expression? )* ) ')' - | '[' ( expression ( ',' expression )* )? ']' ; - -typeNameExpression - : elementaryTypeName - | userDefinedTypeName ; - -assemblyItem - : identifier - | assemblyBlock - | assemblyExpression - | assemblyLocalDefinition - | assemblyAssignment - | assemblyStackAssignment - | labelDefinition - | assemblySwitch - | assemblyFunctionDefinition - | assemblyFor - | assemblyIf - | BreakKeyword - | ContinueKeyword - | LeaveKeyword - | subAssembly - | numberLiteral - | stringLiteral - | hexLiteral ; - -assemblyBlock - : '{' assemblyItem* '}' ; - -assemblyExpression - : assemblyCall | assemblyLiteral | assemblyIdentifier ; - -assemblyCall - : ( 'return' | 'address' | 'byte' | identifier ) ( '(' assemblyExpression? ( ',' assemblyExpression )* ')' )? ; - -assemblyLocalDefinition - : 'let' assemblyIdentifierList ( ':=' assemblyExpression )? ; - -assemblyAssignment - : assemblyIdentifierList ':=' assemblyExpression ; - -assemblyIdentifierList - : assemblyIdentifier ( ',' assemblyIdentifier )* ; - -assemblyIdentifier - : identifier ( '.' identifier )* ; - -assemblyStackAssignment - : '=:' identifier ; - -labelDefinition - : identifier ':' ; - -assemblySwitch - : 'switch' assemblyExpression assemblyCase* ; - -assemblyCase - : 'case' assemblyLiteral assemblyType? assemblyBlock - | 'default' assemblyBlock ; - -assemblyFunctionDefinition - : 'function' identifier '(' assemblyTypedVariableList? ')' - assemblyFunctionReturns? assemblyBlock ; - -assemblyFunctionReturns - : ( '-' '>' assemblyTypedVariableList ) ; - -assemblyFor - : 'for' assemblyBlock assemblyExpression assemblyBlock assemblyBlock ; - -assemblyIf - : 'if' assemblyExpression assemblyBlock ; - -assemblyLiteral - : ( stringLiteral | DecimalNumber | HexNumber | hexLiteral | BooleanLiteral ) assemblyType? ; - -assemblyTypedVariableList - : identifier assemblyType? ( ',' assemblyTypedVariableList )? ; - -assemblyType - : ':' identifier ; - -subAssembly - : 'assembly' identifier assemblyBlock ; - -// 'finney' and 'szabo' are no longer supported as denominations by latest Solidity. -numberLiteral - : (DecimalNumber | HexNumber) (NumberUnit | Gwei | Finney | Szabo)?; - -identifier - : (Gwei | Finney | Szabo | 'from' | 'calldata' | 'address' | Identifier) ; - -BooleanLiteral - : 'true' | 'false' ; - -DecimalNumber - : ( DecimalDigits | (DecimalDigits? '.' DecimalDigits) ) ( [eE] '-'? DecimalDigits )? ; - -fragment -DecimalDigits - : [0-9] ( '_'? [0-9] )* ; - -HexNumber - : '0' [xX] HexDigits ; - -fragment -HexDigits - : HexCharacter ( '_'? HexCharacter )* ; - -NumberUnit - : 'wei' | 'ether' - | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ; - -Gwei: 'gwei' ; -Szabo: 'szabo' ; -Finney: 'finney' ; - -HexLiteralFragment - : 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ; - -hexLiteral : HexLiteralFragment+ ; - -fragment -HexPair - : HexCharacter HexCharacter ; - -fragment -HexCharacter - : [0-9A-Fa-f] ; - -ReservedKeyword - : 'after' - | 'case' - | 'default' - | 'final' - | 'in' - | 'inline' - | 'let' - | 'match' - | 'null' - | 'of' - | 'relocatable' - | 'static' - | 'switch' - | 'typeof' ; - -AnonymousKeyword : 'anonymous' ; -BreakKeyword : 'break' ; -ConstantKeyword : 'constant' ; -ImmutableKeyword : 'immutable' ; -ContinueKeyword : 'continue' ; -LeaveKeyword : 'leave' ; -ExternalKeyword : 'external' ; -IndexedKeyword : 'indexed' ; -InternalKeyword : 'internal' ; -PayableKeyword : 'payable' ; -PrivateKeyword : 'private' ; -PublicKeyword : 'public' ; -VirtualKeyword : 'virtual' ; -PureKeyword : 'pure' ; -TypeKeyword : 'type' ; -ViewKeyword : 'view' ; - -ConstructorKeyword : 'constructor' ; -FallbackKeyword : 'fallback' ; -ReceiveKeyword : 'receive' ; - -Identifier - : IdentifierStart IdentifierPart* ; - -fragment -IdentifierStart - : [a-zA-Z$_] ; - -fragment -IdentifierPart - : [a-zA-Z0-9$_] ; - -stringLiteral - : StringLiteralFragment+ ; - -StringLiteralFragment - : '"' DoubleQuotedStringCharacter* '"' - | '\'' SingleQuotedStringCharacter* '\'' ; - -unicodeStringLiteral - : UnicodeStringLiteralFragment+ ; - -UnicodeStringLiteralFragment - : 'unicode"' DoubleQuotedStringCharacter* '"' - | 'unicode\'' SingleQuotedStringCharacter* '\'' ; - -fragment -DoubleQuotedStringCharacter - : ~["\r\n\\] | ('\\' .) ; - -fragment -SingleQuotedStringCharacter - : ~['\r\n\\] | ('\\' .) ; - -WS - : [ \t\r\n\u000C]+ -> skip ; - -COMMENT - : '/*' .*? '*/' -> channel(HIDDEN) ; - -LINE_COMMENT - : '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index d5ff56a136d7..90536fa79b49 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -61,7 +61,7 @@ The following elementary types exist: - ``bool``: equivalent to ``uint8`` restricted to the values 0 and 1. For computing the function selector, ``bool`` is used. - ``fixedx``: signed fixed-point decimal number of ``M`` bits, ``8 <= M <= 256``, - ``M % 8 ==0``, and ``0 < N <= 80``, which denotes the value ``v`` as ``v / (10 ** N)``. + ``M % 8 == 0``, and ``0 < N <= 80``, which denotes the value ``v`` as ``v / (10 ** N)``. - ``ufixedx``: unsigned variant of ``fixedx``. @@ -76,6 +76,9 @@ The following (fixed-size) array type exists: - ``[M]``: a fixed-length array of ``M`` elements, ``M >= 0``, of the given type. + .. note:: + While this ABI specification can express fixed-length arrays with zero elements, they're not supported by the compiler. + The following non-fixed-size types exist: - ``bytes``: dynamic sized byte sequence. @@ -106,14 +109,15 @@ them. +-------------------------------+-----------------------------------------------------------------------------+ |:ref:`contract` |``address`` | +-------------------------------+-----------------------------------------------------------------------------+ -|:ref:`enum` |smallest ``uint`` type that is large enough to hold all values | -| | | -| |For example, an ``enum`` of 255 values or less is mapped to ``uint8`` and | -| |an ``enum`` of 256 values is mapped to ``uint16``. | +|:ref:`enum` |``uint8`` | +-------------------------------+-----------------------------------------------------------------------------+ |:ref:`struct` |``tuple`` | +-------------------------------+-----------------------------------------------------------------------------+ +.. warning:: + Before version ``0.8.0`` enums could have more than 256 members and were represented by the + smallest integer type just big enough to hold the value of any member. + Design Criteria for the Encoding ================================ @@ -233,7 +237,7 @@ Given the contract: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract Foo { function bar(bytes3[2] memory) public pure {} @@ -537,7 +541,7 @@ For example, :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.6.99 <0.9.0; contract Test { @@ -586,8 +590,8 @@ As an example, the code :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.19 <0.8.0; - pragma experimental ABIEncoderV2; + pragma solidity >0.7.4 <0.9.0; + pragma abicoder v2; contract Test { struct S { uint a; uint[] b; T[] c; } diff --git a/docs/assembly.rst b/docs/assembly.rst index b54800d46168..d5d73cfb1201 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -42,7 +42,7 @@ without a compiler change. .. code:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; library GetCode { function at(address _addr) public view returns (bytes memory o_code) { @@ -68,7 +68,7 @@ efficient code, for example: .. code:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; library VectorSum { @@ -135,12 +135,16 @@ inside that slot. To retrieve the slot pointed to by the variable ``x``, you use ``x.slot``, and to retrieve the byte-offset you use ``x.offset``. Using ``x`` itself will result in an error. +For dynamic calldata arrays, you can access +their calldata offset (in bytes) and length (number of elements) using ``x.offset`` and ``x.length``. +Both expressions can also be assigned to. + Local Solidity variables are available for assignments, for example: .. code:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract C { uint b; @@ -155,7 +159,7 @@ Local Solidity variables are available for assignments, for example: .. warning:: If you access variables of a type that spans less than 256 bits - (for example ``uint64``, ``address``, ``bytes16`` or ``byte``), + (for example ``uint64``, ``address``, or ``bytes16``), you cannot make any assumptions about bits not part of the encoding of the type. Especially, do not assume them to be zero. To be safe, always clear the data properly before you use it diff --git a/docs/brand-guide.rst b/docs/brand-guide.rst index 0e6400bede7c..fc03f35d9346 100644 --- a/docs/brand-guide.rst +++ b/docs/brand-guide.rst @@ -43,7 +43,7 @@ Solidity Logo License :alt: Creative Commons License The Solidity logo is distributed and licensed under a `Creative Commons -Attribution 4.0 International License `_. +Attribution 4.0 International License `_. This is the most permissive Creative Commons license and allows reuse and modifications for any purpose. @@ -70,6 +70,8 @@ Solidity Logo Guidelines .. image:: logo.svg :width: 256 +*(Right click on the logo to download it.)* + Please do not: - Change the ratio of the logo (do not stretch it or cut it). diff --git a/docs/bugs.json b/docs/bugs.json index 367f0843a5c4..08aa72ae51a3 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,26 @@ [ + { + "name": "EmptyByteArrayCopy", + "summary": "Copying an empty byte array (or string) from memory or calldata to storage can result in data corruption if the target array's length is increased subsequently without storing new data.", + "description": "The routine that copies byte arrays from memory or calldata to storage stores unrelated data from after the source array in the storage slot if the source array is empty. If the storage array's length is subsequently increased either by using ``.push()`` or by assigning to its ``.length`` attribute (only before 0.6.0), the newly created byte array elements will not be zero-initialized, but contain the unrelated data. You are not affected if you do not assign to ``.length`` and do not use ``.push()`` on byte arrays, or only use ``.push()`` or manually initialize the new elements.", + "fixed": "0.7.4", + "severity": "medium" + }, + { + "name": "DynamicArrayCleanup", + "summary": "When assigning a dynamically-sized array with types of size at most 16 bytes in storage causing the assigned array to shrink, some parts of deleted slots were not zeroed out.", + "description": "Consider a dynamically-sized array in storage whose base-type is small enough such that multiple values can be packed into a single slot, such as `uint128[]`. Let us define its length to be `l`. When this array gets assigned from another array with a smaller length, say `m`, the slots between elements `m` and `l` have to be cleaned by zeroing them out. However, this cleaning was not performed properly. Specifically, after the slot corresponding to `m`, only the first packed value was cleaned up. If this array gets resized to a length larger than `m`, the indices corresponding to the unclean parts of the slot contained the original value, instead of 0. The resizing here is performed by assigning to the array `length`, by a `push()` or via inline assembly. You are not affected if you are only using `.push()` or if you assign a value (even zero) to the new elements after increasing the length of the array.", + "fixed": "0.7.3", + "severity": "medium" + }, + { + "name": "FreeFunctionRedefinition", + "summary": "The compiler does not flag an error when two or more free functions with the same name and parameter types are defined in a source unit or when an imported free function alias shadows another free function with a different name but identical parameter types.", + "description": "In contrast to functions defined inside contracts, free functions with identical names and parameter types did not create an error. Both definition of free functions with identical name and parameter types and an imported free function with an alias that shadows another function with a different name but identical parameter types were permitted due to which a call to either the multiply defined free function or the imported free function alias within a contract led to the execution of that free function which was defined first within the source unit. Subsequently defined identical free function definitions were silently ignored and their code generation was skipped.", + "introduced": "0.7.1", + "fixed": "0.7.2", + "severity": "low" + }, { "name": "UsingForCalldata", "summary": "Function calls to internal library functions with calldata parameters called via ``using for`` can result in invalid data being read.", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index fc7191322ad0..d9fedc66ed8d 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1,6 +1,8 @@ { "0.1.0": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -19,6 +21,8 @@ }, "0.1.1": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -37,6 +41,8 @@ }, "0.1.2": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -55,6 +61,8 @@ }, "0.1.3": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ExpExponentCleanup", "ZeroFunctionSelector", "ECRecoverMalformedInput", @@ -73,6 +81,8 @@ }, "0.1.4": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -92,6 +102,8 @@ }, "0.1.5": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -111,6 +123,8 @@ }, "0.1.6": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -132,6 +146,8 @@ }, "0.1.7": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", @@ -153,6 +169,8 @@ }, "0.2.0": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "ExpExponentCleanup", @@ -175,6 +193,8 @@ }, "0.2.1": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "ExpExponentCleanup", @@ -197,6 +217,8 @@ }, "0.2.2": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "ExpExponentCleanup", @@ -219,6 +241,8 @@ }, "0.3.0": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -243,6 +267,8 @@ }, "0.3.1": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -266,6 +292,8 @@ }, "0.3.2": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -289,6 +317,8 @@ }, "0.3.3": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -311,6 +341,8 @@ }, "0.3.4": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -333,6 +365,8 @@ }, "0.3.5": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -355,6 +389,8 @@ }, "0.3.6": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -375,6 +411,8 @@ }, "0.4.0": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -395,6 +433,8 @@ }, "0.4.1": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -415,6 +455,8 @@ }, "0.4.10": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -434,6 +476,8 @@ }, "0.4.11": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -452,6 +496,8 @@ }, "0.4.12": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -469,6 +515,8 @@ }, "0.4.13": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -486,6 +534,8 @@ }, "0.4.14": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -502,6 +552,8 @@ }, "0.4.15": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -517,6 +569,8 @@ }, "0.4.16": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -534,6 +588,8 @@ }, "0.4.17": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -552,6 +608,8 @@ }, "0.4.18": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -569,6 +627,8 @@ }, "0.4.19": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -587,6 +647,8 @@ }, "0.4.2": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -606,6 +668,8 @@ }, "0.4.20": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -624,6 +688,8 @@ }, "0.4.21": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -642,6 +708,8 @@ }, "0.4.22": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -660,6 +728,8 @@ }, "0.4.23": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -677,6 +747,8 @@ }, "0.4.24": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -694,6 +766,8 @@ }, "0.4.25": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -709,6 +783,8 @@ }, "0.4.26": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -721,6 +797,8 @@ }, "0.4.3": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -739,6 +817,8 @@ }, "0.4.4": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", "privateCanBeOverridden", @@ -756,6 +836,8 @@ }, "0.4.5": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -776,6 +858,8 @@ }, "0.4.6": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -795,6 +879,8 @@ }, "0.4.7": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -814,6 +900,8 @@ }, "0.4.8": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -833,6 +921,8 @@ }, "0.4.9": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -852,6 +942,8 @@ }, "0.5.0": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -867,6 +959,8 @@ }, "0.5.1": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -882,6 +976,8 @@ }, "0.5.10": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -893,6 +989,8 @@ }, "0.5.11": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -903,6 +1001,8 @@ }, "0.5.12": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -913,6 +1013,8 @@ }, "0.5.13": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -923,6 +1025,8 @@ }, "0.5.14": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", @@ -935,6 +1039,8 @@ }, "0.5.15": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", @@ -946,6 +1052,8 @@ }, "0.5.16": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", @@ -956,6 +1064,8 @@ }, "0.5.17": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", @@ -965,6 +1075,8 @@ }, "0.5.2": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -980,6 +1092,8 @@ }, "0.5.3": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -995,6 +1109,8 @@ }, "0.5.4": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -1010,6 +1126,8 @@ }, "0.5.5": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -1027,6 +1145,8 @@ }, "0.5.6": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -1044,6 +1164,8 @@ }, "0.5.7": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -1059,6 +1181,8 @@ }, "0.5.8": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -1073,6 +1197,8 @@ }, "0.5.9": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "ImplicitConstructorCallvalueCheck", "TupleAssignmentMultiStackSlotComponents", "MemoryArrayCreationOverflow", @@ -1086,6 +1212,8 @@ }, "0.6.0": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck", @@ -1097,6 +1225,8 @@ }, "0.6.1": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck", @@ -1106,19 +1236,30 @@ "released": "2020-01-02" }, "0.6.10": { - "bugs": [], + "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup" + ], "released": "2020-06-11" }, "0.6.11": { - "bugs": [], + "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup" + ], "released": "2020-07-07" }, "0.6.12": { - "bugs": [], + "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup" + ], "released": "2020-07-22" }, "0.6.2": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck", @@ -1129,6 +1270,8 @@ }, "0.6.3": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck", @@ -1139,6 +1282,8 @@ }, "0.6.4": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck", @@ -1149,6 +1294,8 @@ }, "0.6.5": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck", @@ -1158,6 +1305,8 @@ }, "0.6.6": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck" @@ -1166,6 +1315,8 @@ }, "0.6.7": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "MissingEscapingInFormatting", "ArraySliceDynamicallyEncodedBaseType", "ImplicitConstructorCallvalueCheck" @@ -1173,17 +1324,62 @@ "released": "2020-05-04" }, "0.6.8": { - "bugs": [], + "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup" + ], "released": "2020-05-14" }, "0.6.9": { "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", "UsingForCalldata" ], "released": "2020-06-04" }, "0.7.0": { - "bugs": [], + "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup" + ], "released": "2020-07-28" + }, + "0.7.1": { + "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup", + "FreeFunctionRedefinition" + ], + "released": "2020-09-02" + }, + "0.7.2": { + "bugs": [ + "EmptyByteArrayCopy", + "DynamicArrayCleanup" + ], + "released": "2020-09-28" + }, + "0.7.3": { + "bugs": [ + "EmptyByteArrayCopy" + ], + "released": "2020-10-07" + }, + "0.7.4": { + "bugs": [], + "released": "2020-10-19" + }, + "0.7.5": { + "bugs": [], + "released": "2020-11-18" + }, + "0.7.6": { + "bugs": [], + "released": "2020-12-16" + }, + "0.8.0": { + "bugs": [], + "released": "2020-12-16" } } \ No newline at end of file diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 29b338e9373a..22ca5cc8eedf 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -67,7 +67,7 @@ The following is the order of precedence for operators, listed in order of evalu | *15* | Comma operator | ``,`` | +------------+-------------------------------------+--------------------------------------------+ -.. 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, send +.. 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 Global Variables ================ @@ -82,6 +82,7 @@ Global Variables the given arguments starting from the second and prepends the given four-byte selector - ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)``` +- ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -89,10 +90,10 @@ Global Variables - ``block.timestamp`` (``uint``): current block timestamp - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata -- ``msg.sender`` (``address payable``): sender of the message (current call) +- ``msg.sender`` (``address``): sender of the message (current call) - ``msg.value`` (``uint``): number of wei sent with the message - ``tx.gasprice`` (``uint``): gas price of the transaction -- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) +- ``tx.origin`` (``address``): sender of the transaction (full call chain) - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) @@ -114,6 +115,8 @@ Global Variables - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei +- ``
.code`` (``bytes memory``): code at the :ref:`address` (can be empty) +- ``
.codehash`` (``bytes32``): the codehash of the :ref:`address` - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index c4459938967e..c9130ad198d7 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -28,7 +28,7 @@ you receive the funds of the person who is now the richest. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract WithdrawalContract { address public richest; @@ -53,7 +53,7 @@ you receive the funds of the person who is now the richest. // Remember to zero the pending refund before // sending to prevent re-entrancy attacks pendingWithdrawals[msg.sender] = 0; - msg.sender.transfer(amount); + payable(msg.sender).transfer(amount); } } @@ -62,14 +62,14 @@ This is as opposed to the more intuitive sending pattern: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract SendContract { address payable public richest; uint public mostSent; constructor() payable { - richest = msg.sender; + richest = payable(msg.sender); mostSent = msg.value; } @@ -77,7 +77,7 @@ This is as opposed to the more intuitive sending pattern: require(msg.value > mostSent, "Not enough money sent."); // This line can cause problems (explained below). richest.transfer(msg.value); - richest = msg.sender; + richest = payable(msg.sender); mostSent = msg.value; } } @@ -124,7 +124,7 @@ restrictions highly readable. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract AccessRestriction { // These will be assigned at the construction @@ -192,7 +192,7 @@ restrictions highly readable. ); _; if (msg.value > _amount) - msg.sender.transfer(msg.value - _amount); + payable(msg.sender).transfer(msg.value - _amount); } function forceOwnerChange(address _newOwner) @@ -202,7 +202,7 @@ restrictions highly readable. { owner = _newOwner; // just some example condition - if (uint(owner) & 0 == 1) + if (uint160(owner) & 0 == 1) // This did not refund for Solidity // before version 0.4.0. return; @@ -277,7 +277,7 @@ function finishes. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract StateMachine { enum Stages { diff --git a/docs/conf.py b/docs/conf.py index 1ff7947224f2..ccc2eb548fec 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -39,7 +39,9 @@ def setup(sphinx): # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [] +extensions = [ 'sphinx_a4doc' ] + +a4_base_path = os.path.dirname(__file__) + '/grammar' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -83,7 +85,7 @@ def setup(sphinx): # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', 'contracts', 'types', 'examples'] +exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar', 'ir'] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/docs/contracts/abstract-contracts.rst b/docs/contracts/abstract-contracts.rst index fe31c0b328a2..8467337afc0b 100644 --- a/docs/contracts/abstract-contracts.rst +++ b/docs/contracts/abstract-contracts.rst @@ -14,7 +14,7 @@ defined as abstract, because the function ``utterance()`` was defined, but no im provided (no implementation body ``{ }`` was given).:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; abstract contract Feline { function utterance() public virtual returns (bytes32); @@ -24,7 +24,7 @@ Such abstract contracts can not be instantiated directly. This is also true, if all defined functions. The usage of an abstract contract as a base class is shown in the following example:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; abstract contract Feline { function utterance() public pure virtual returns (bytes32); diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 33204f58d7fa..d22735618f15 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -1,5 +1,7 @@ .. index:: ! constant +.. _constants: + ************************************** Constant and Immutable State Variables ************************************** @@ -9,6 +11,8 @@ In both cases, the variables cannot be modified after the contract has been cons For ``constant`` variables, the value has to be fixed at compile-time, while for ``immutable``, it can still be assigned at construction time. +It is also possible to define ``constant`` variables at the file level. + The compiler does not reserve a storage slot for these variables, and every occurrence is replaced by the respective value. @@ -21,15 +25,16 @@ is copied to all the places in the code where they are accessed. For these value can sometimes be cheaper than immutable values. Not all types for constants and immutables are implemented at this time. The only supported types are -`strings `_ (only for constants) and `value types `_. +:ref:`strings ` (only for constants) and :ref:`value types `. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.4; + + uint constant X = 32**22 + 8; contract C { - uint constant X = 32**22 + 8; string constant TEXT = "abc"; bytes32 constant MY_HASH = keccak256("abc"); uint immutable decimals; diff --git a/docs/contracts/creating-contracts.rst b/docs/contracts/creating-contracts.rst index 2679d05a5449..d93ac832c812 100644 --- a/docs/contracts/creating-contracts.rst +++ b/docs/contracts/creating-contracts.rst @@ -35,7 +35,7 @@ This means that cyclic creation dependencies are impossible. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract OwnedToken { diff --git a/docs/contracts/events.rst b/docs/contracts/events.rst index 3183d3c3f0dc..d40b34f62fdd 100644 --- a/docs/contracts/events.rst +++ b/docs/contracts/events.rst @@ -66,7 +66,7 @@ is that they are cheaper to deploy and call. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.21 <0.8.0; + pragma solidity >=0.4.21 <0.9.0; contract ClientReceipt { event Deposit( @@ -126,40 +126,9 @@ The output of the above looks like the following (trimmed): } } -.. index:: ! log - -Low-Level Interface to Logs -=========================== - -It is also possible to access the low-level interface to the logging -mechanism via the functions ``log0``, ``log1``, ``log2``, ``log3`` and ``log4``. -Each function ``logi`` takes ``i + 1`` parameter of type ``bytes32``, where the first -argument will be used for the data part of the log and the others -as topics. The event call above can be performed in the same way as - -:: - - // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.10 <0.8.0; - - contract C { - function f() public payable { - uint256 _id = 0x420042; - log3( - bytes32(msg.value), - bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20), - bytes32(uint256(msg.sender)), - bytes32(_id) - ); - } - } - -where the long hexadecimal number is equal to -``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event. - Additional Resources for Understanding Events ============================================== -- `Javascript documentation `_ -- `Example usage of events `_ -- `How to access them in js `_ +- `Javascript documentation `_ +- `Example usage of events `_ +- `How to access them in js `_ diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index 823e6e6ac388..1eb6e28a12ec 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -18,10 +18,10 @@ if they are marked ``virtual``. For details, please see :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >0.7.0 <0.9.0; contract owned { - constructor() { owner = msg.sender; } + constructor() { owner = payable(msg.sender); } address payable owner; // This contract only defines a modifier but does not use @@ -100,6 +100,11 @@ if they are marked ``virtual``. For details, please see } } +If you want to access a modifier ``m`` defined in a contract ``C``, you can use ``C.m`` to +reference it without virtual lookup. It is only possible to use modifiers defined in the current +contract or its base contracts. Modifiers can also be defined in libraries but their use is +limited to functions of the same library. + Multiple modifiers are applied to a function by specifying them in a whitespace-separated list and are evaluated in the order presented. diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 41174c9580e2..6aaa9d245f1f 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -6,6 +6,41 @@ Functions ********* +Functions can be defined inside and outside of contracts. + +Functions outside of a contract, also called "free functions", always have implicit ``internal`` +:ref:`visibility`. Their code is included in all contracts +that call them, similar to internal library functions. + +:: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.0 <0.9.0; + + function sum(uint[] memory _arr) pure returns (uint s) { + for (uint i = 0; i < _arr.length; i++) + s += _arr[i]; + } + + contract ArrayExample { + bool found; + function f(uint[] memory _arr) public { + // This calls the free function internally. + // The compiler will add its code to the contract. + uint s = sum(_arr); + require(s >= 10); + found = true; + } + } + +.. 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, + 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 + not in their scope. + .. _function-parameters-return-variables: Function Parameters and Return Variables @@ -24,7 +59,7 @@ For example, if you want your contract to accept one kind of external call with two integers, you would use something like the following:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract Simple { uint sum; @@ -39,8 +74,8 @@ Function parameters can be used as any other local variable and they can also be An :ref:`external function` cannot accept a multi-dimensional array as an input - parameter. This functionality is possible if you enable the new - ``ABIEncoderV2`` feature by adding ``pragma experimental ABIEncoderV2;`` to your source file. + 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` can accept a multi-dimensional array without enabling the feature. @@ -57,7 +92,7 @@ For example, suppose you want to return two results: the sum and the product of two integers passed as function parameters, then you use something like:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract Simple { function arithmetic(uint _a, uint _b) @@ -82,7 +117,7 @@ or you can provide return values statement:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract Simple { function arithmetic(uint _a, uint _b) @@ -100,8 +135,8 @@ 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 - new ``ABIEncoderV2`` feature by adding ``pragma experimental - ABIEncoderV2;`` to your source file then more types are available, but + 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. @@ -146,7 +181,7 @@ The following statements are considered modifying the state: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; contract C { function f(uint a, uint b) public view returns (uint) { @@ -192,7 +227,7 @@ In addition to the list of state modifying statements explained above, the follo :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; contract C { function f(uint a, uint b) public pure returns (uint) { @@ -240,7 +275,10 @@ A contract can have at most one ``receive`` function, declared using ``receive() external payable { ... }`` (without the ``function`` keyword). This function cannot have arguments, cannot return anything and must have -``external`` visibility and ``payable`` state mutability. It is executed on a +``external`` visibility and ``payable`` state mutability. +It can be virtual, can override and can have modifiers. + +The receive function is executed on a call to the contract with empty calldata. This is the function that is executed on plain Ether transfers (e.g. via ``.send()`` or ``.transfer()``). If no such function exists, but a payable :ref:`fallback function ` @@ -249,7 +287,7 @@ neither a receive Ether nor a payable fallback function is present, the contract cannot receive Ether through regular transactions and throws an exception. -In the worst case, the fallback function can only rely on 2300 gas being +In the worst case, the ``receive`` function can only rely on 2300 gas being available (for example when ``send`` or ``transfer`` is used), leaving little room to perform other operations except basic logging. The following operations will consume more gas than the 2300 gas stipend: @@ -286,7 +324,7 @@ Below you can see an example of a Sink contract that uses function ``receive``. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; // This contract keeps all Ether sent to it with no way // to get it back. @@ -304,15 +342,22 @@ Below you can see an example of a Sink contract that uses function ``receive``. Fallback Function ================= -A contract can have at most one ``fallback`` function, declared using ``fallback () external [payable]`` -(without the ``function`` keyword). -This function cannot have arguments, cannot return anything and must have ``external`` visibility. -It is executed on a call to the contract if none of the other +A contract can have at most one ``fallback`` function, declared using either ``fallback () external [payable]`` +or ``fallback (bytes calldata _input) external [payable] returns (bytes memory _output)`` +(both without the ``function`` keyword). +This function must have ``external`` visibility. A fallback function can be virtual, can override +and can have modifiers. + +The fallback function is executed on a call to the contract if none of the other functions match the given function signature, or if no data was supplied at all and there is no :ref:`receive Ether function `. The fallback function always receives data, but in order to also receive Ether it must be marked ``payable``. +If the version with parameters is used, ``_input`` will contain the full data sent to the contract +(equal to ``msg.data``) and can return data in ``_output``. The returned data will not be +ABI-encoded. Instead it will be returned without modifications (not even padding). + In the worst case, if a payable fallback function is also used in place of a receive function, it can only rely on 2300 gas being available (see :ref:`receive Ether function ` @@ -329,12 +374,11 @@ operations as long as there is enough gas passed on to it. to distinguish Ether transfers from interface confusions. .. note:: - Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve - any payload supplied with the call. - After having checked the first four bytes of ``msg.data``, + If you want to decode the input data, you can check the first four bytes + for the function selector and then you can use ``abi.decode`` together with the array slice syntax to decode ABI-encoded data: - ``(c, d) = abi.decode(msg.data[4:], (uint256, uint256));`` + ``(c, d) = abi.decode(_input[4:], (uint256, uint256));`` Note that this should only be used as a last resort and proper functions should be used instead. @@ -342,7 +386,7 @@ operations as long as there is enough gas passed on to it. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.2 <0.8.0; + pragma solidity >=0.6.2 <0.9.0; contract Test { // This function is called for all messages sent to @@ -394,8 +438,10 @@ operations as long as there is enough gas passed on to it. // results in test.x becoming == 1 and test.y becoming 1. // If someone sends Ether to that contract, the receive function in TestPayable will be called. - require(address(test).send(2 ether)); + require(payable(test).send(2 ether)); // results in test.x becoming == 2 and test.y becoming 2 ether. + + return true; } } @@ -415,7 +461,7 @@ The following example shows overloading of the function :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract A { function f(uint _in) public pure returns (uint out) { @@ -434,7 +480,7 @@ externally visible functions differ by their Solidity types but not by their ext :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; // This will not compile contract A { @@ -468,7 +514,7 @@ candidate, resolution fails. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract A { function f(uint8 _in) public pure returns (uint8 out) { diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index f628d49fbe35..c18cfd418fc0 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -39,11 +39,11 @@ Details are given in the following example. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract Owned { - constructor() { owner = msg.sender; } + constructor() { owner = payable(msg.sender); } address payable owner; } @@ -127,10 +127,10 @@ destruction request. The way this is done is problematic, as seen in the following example:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract owned { - constructor() { owner = msg.sender; } + constructor() { owner = payable(msg.sender); } address payable owner; } @@ -157,10 +157,10 @@ explicitly in the final override, but this function will bypass ``Base1.destroy``. The way around this is to use ``super``:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract owned { - constructor() { owner = msg.sender; } + constructor() { owner = payable(msg.sender); } address payable owner; } @@ -214,7 +214,7 @@ The following example demonstrates changing mutability and visibility: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract Base { @@ -238,7 +238,7 @@ bases, it has to explicitly override it: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract Base1 { @@ -265,7 +265,7 @@ that already overrides all other functions. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract A { function f() public pure{} } contract B is A {} @@ -306,7 +306,7 @@ of the variable: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract A { @@ -331,14 +331,14 @@ Modifier Overriding =================== Function modifiers can override each other. This works in the same way as -`function overriding `_ (except that there is no overloading for modifiers). The +:ref:`function overriding ` (except that there is no overloading for modifiers). The ``virtual`` keyword must be used on the overridden modifier and the ``override`` keyword must be used in the overriding modifier: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract Base { @@ -357,7 +357,7 @@ explicitly: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract Base1 { @@ -405,7 +405,7 @@ equivalent to ``constructor() {}``. For example: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; abstract contract A { uint public a; @@ -442,7 +442,7 @@ linearization rules explained below. If the base constructors have arguments, derived contracts need to specify all of them. This can be done in two ways:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract Base { uint x; @@ -502,7 +502,7 @@ error "Linearization of inheritance graph impossible". :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract X {} contract A is X {} @@ -523,7 +523,7 @@ One area where inheritance linearization is especially important and perhaps not :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract Base1 { constructor() {} diff --git a/docs/contracts/interfaces.rst b/docs/contracts/interfaces.rst index 4ed25584e14a..19a33605edaf 100644 --- a/docs/contracts/interfaces.rst +++ b/docs/contracts/interfaces.rst @@ -23,7 +23,7 @@ Interfaces are denoted by their own keyword: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.2 <0.8.0; + pragma solidity >=0.6.2 <0.9.0; interface Token { enum TokenType { Fungible, NonFungible } @@ -44,7 +44,7 @@ inheritance. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.2 <0.8.0; + pragma solidity >=0.6.2 <0.9.0; interface ParentA { function test() external returns (uint256); diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst index e28a16da2618..8a60eb3cac83 100644 --- a/docs/contracts/libraries.rst +++ b/docs/contracts/libraries.rst @@ -47,7 +47,7 @@ more advanced example to implement a set). :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; // We define a new struct datatype that will be used to @@ -126,7 +126,7 @@ custom types without the overhead of external function calls: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.8 <0.9.0; struct bigint { uint[] limbs; @@ -145,7 +145,7 @@ custom types without the overhead of external function calls: uint a = limb(_a, i); uint b = limb(_b, i); r.limbs[i] = a + b + carry; - if (a + b < a || (a + b == uint(-1) && carry > 0)) + if (a + b < a || (a + b == type(uint).max && carry > 0)) carry = 1; else carry = 0; @@ -175,7 +175,7 @@ custom types without the overhead of external function calls: function f() public pure { bigint memory x = BigInt.fromUint(7); - bigint memory y = BigInt.fromUint(uint(-1)); + bigint memory y = BigInt.fromUint(type(uint).max); bigint memory z = x.add(y); assert(z.limb(1) > 0); } @@ -184,24 +184,15 @@ custom types without the overhead of external function calls: It is possible to obtain the address of a library by converting the library type to the ``address`` type, i.e. using ``address(LibraryName)``. -As the compiler cannot know where the library will be -deployed at, these addresses have to be filled into the -final bytecode by a linker -(see :ref:`commandline-compiler` for how to use the -commandline compiler for linking). If the addresses are not -given as arguments to the compiler, the compiled hex code -will contain placeholders of the form ``__Set______`` (where -``Set`` is the name of the library). The address can be filled -manually by replacing all those 40 symbols by the hex -encoding of the address of the library contract. - -.. note:: - Manually linking libraries on the generated bytecode is discouraged, because - in this way, the library name is restricted to 36 characters. - You should ask the compiler to link the libraries at the time - a contract is compiled by either using - the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use - the standard-JSON interface to the compiler. +As the compiler does not know the address where the library will be deployed, the compiled hex code +will contain placeholders of the form ``__$30bbc0abd4d6364515865950d3e0d10953$__``. The placeholder +is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library +name, which would be for example ``libraries/bigint.sol:BigInt`` if the library was stored in a file +called ``bigint.sol`` in a ``libraries/`` directory. Such bytecode is incomplete and should not be +deployed. Placeholders need to be replaced with actual addresses. You can do that by either passing +them to the compiler when the library is being compiled or by using the linker to update an already +compiled binary. See :ref:`library-linking` for information on how to use the commandline compiler +for linking. In comparison to contracts, libraries are restricted in the following ways: @@ -229,7 +220,9 @@ The following identifiers are used for the types in the signatures: - Non-storage array types follow the same convention as in the contract ABI, i.e. ``[]`` for dynamic arrays and ``[M]`` for fixed-size arrays of ``M`` elements. - Non-storage structs are referred to by their fully qualified name, i.e. ``C.S`` for ``contract C { struct S { ... } }``. - - Storage pointer types use the type identifier of their corresponding non-storage type, but append a single space + - Storage pointer mappings use ``mapping( => ) storage`` where ```` and ```` are + the identifiers for the key and value types of the mapping, respectively. + - Other storage pointer types use the type identifier of their corresponding non-storage type, but append a single space followed by ``storage`` to it. The argument encoding is the same as for the regular contract ABI, except for storage pointers, which are encoded as a @@ -241,7 +234,7 @@ Its value can be obtained from Solidity using the ``.selector`` member as follow :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.14 <0.8.0; + pragma solidity >=0.5.14 <0.9.0; library L { function f(uint256) external {} @@ -283,4 +276,4 @@ for any non-view and non-pure function. This means that the actual code stored on chain for a library is different from the code reported by the compiler as -``deployedBytecode``. \ No newline at end of file +``deployedBytecode``. diff --git a/docs/contracts/using-for.rst b/docs/contracts/using-for.rst index dc4827e670eb..19cdf8e61f36 100644 --- a/docs/contracts/using-for.rst +++ b/docs/contracts/using-for.rst @@ -30,7 +30,7 @@ Let us rewrite the set example from the :ref:`libraries` in this way:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; // This is the same code as before, just without comments @@ -83,7 +83,7 @@ Let us rewrite the set example from the It is also possible to extend elementary types in that way:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.6.8 <0.9.0; library Search { function indexOf(uint[] storage self, uint value) @@ -93,7 +93,7 @@ It is also possible to extend elementary types in that way:: { for (uint i = 0; i < self.length; i++) if (self[i] == value) return i; - return uint(-1); + return type(uint).max; } } @@ -108,7 +108,7 @@ It is also possible to extend elementary types in that way:: function replace(uint _old, uint _new) public { // This performs the library function call uint index = data.indexOf(_old); - if (index == uint(-1)) + if (index == type(uint).max) data.push(_new); else data[index] = _new; diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst index feecbc45747f..874fb64078cc 100644 --- a/docs/contracts/visibility-and-getters.rst +++ b/docs/contracts/visibility-and-getters.rst @@ -55,7 +55,7 @@ return parameter list for functions. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract C { function f(uint a) private pure returns (uint b) { return a + 1; } @@ -70,7 +70,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract C { uint private data; @@ -115,7 +115,7 @@ when they are declared. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract C { uint public data = 42; @@ -136,7 +136,7 @@ it evaluates to a state variable. If it is accessed externally :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract C { uint public data; @@ -156,7 +156,7 @@ to write a function, for example: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract arrayExample { // public state variable @@ -183,7 +183,7 @@ The next example is more complex: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract Complex { struct Data { diff --git a/docs/contributing.rst b/docs/contributing.rst index ab5163e11c79..2062cf92aee4 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -2,22 +2,25 @@ Contributing ############ -Help is always appreciated! +Help is always welcome and there are plenty of options how you can contribute to Solidity. -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. +In particular, we appreciate support in the following areas: -In particular, we need help in the following areas: - -* Improving the documentation -* Responding to questions from other users on `StackExchange - `_ and the `Solidity Gitter - `_ +* Reporting issues. * Fixing and responding to `Solidity's GitHub issues `_, especially those tagged as `good first issue `_ which are meant as introductory issues for external contributors. +* Improving the documentation. +* Translating the documentation into more languages. +* Responding to questions from other users on `StackExchange + `_ and the `Solidity Gitter Chat + `_. +* Getting involved in the language design process by joining language design calls, proposing language changes or new features and providing feedback. + +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 `_. By participating in this project - in the issues, pull requests, or Gitter channels - you agree to abide by its terms. @@ -27,8 +30,8 @@ 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 12pm CET/CEST -- Wednesdays at 2pm CET/CEST +- Mondays at 3pm CET/CEST. +- Wednesdays at 2pm CET/CEST. Both calls take place on `Google Meet `_. @@ -39,12 +42,12 @@ To report an issue, please use the `GitHub issues tracker `_. When reporting issues, please mention the following details: -* Which version of Solidity you are using -* What was the source code (if applicable) -* Which platform are you running on -* How to reproduce the issue -* What was the result of the issue -* What the expected behaviour is +* Which version of Solidity you are using. +* What was the source code (if applicable). +* Which platform are you running on. +* How to reproduce the issue. +* What was the result of the issue. +* What the expected behaviour is. Reducing the source code that caused the issue to a bare minimum is always very helpful and sometimes even clarifies a misunderstanding. @@ -66,7 +69,7 @@ test cases under ``test/`` (see below). However, if you are making a larger change, please consult with the `Solidity Development Gitter channel `_ (different from the one mentioned above, this one is -focused on compiler and language development instead of language use) first. +focused on compiler and language development instead of language usage) first. New features and bugfixes should be added to the ``Changelog.md`` file: please follow the style of previous entries, when applicable. @@ -78,7 +81,7 @@ ensure that it builds locally before submitting a pull request. Thank you for your help! -Running the compiler tests +Running the Compiler Tests ========================== Prerequisites @@ -91,7 +94,10 @@ in the current directory, installed on the system level, or the ``deps`` folder in the project top level. The required file is called ``libevmone.so`` on Linux systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. -Running the tests +On macOS some of the testing scripts expect GNU coreutils to be installed. +This can be easiest accomplished using Homebrew: ``brew install coreutils``. + +Running the Tests ----------------- Solidity includes different types of tests, most of them bundled into the @@ -102,7 +108,7 @@ The ``./scripts/tests.sh`` script executes most Solidity tests automatically, including those bundled into the `Boost C++ Test Framework `_ application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as command line tests and compilation tests. -The test system automatically tries try to discover the location of the ``evmone`` library +The test system automatically tries to discover the location of the ``evmone`` library starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``, @@ -155,7 +161,7 @@ you have access to functions and variables in which you can break or print with. The CI runs additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. -Writing and running syntax tests +Writing and Running Syntax Tests -------------------------------- Syntax tests check that the compiler generates the correct error messages for invalid code @@ -254,7 +260,7 @@ inside the input. We have a specialized binary called ``solfuzzer`` which takes and fails whenever it encounters an internal compiler error, segmentation fault or similar, but does not fail if e.g., the code contains an error. This way, fuzzing tools can find internal problems in the compiler. -We mainly use `AFL `_ for fuzzing. You need to download and +We mainly use `AFL `_ for fuzzing. You need to download and install the AFL packages from your repositories (afl, afl-clang) or build them manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler: @@ -366,7 +372,7 @@ the string parameter ``name`` is non-empty. Documentation Style Guide ========================= -The following are style recommendations specifically for documentation +In the following section you find style recommendations specifically focusing on documentation contributions to Solidity. English Language @@ -388,16 +394,16 @@ local slang and references, making your language as clear to all readers as poss Title Case for Headings ----------------------- -Use `title case `_ for headings. This means capitalise all principal words in +Use `title case `_ for headings. This means capitalise all principal words in titles, but not articles, conjunctions, and prepositions unless they start the title. For example, the following are all correct: -* Title Case for Headings -* For Headings Use Title Case -* Local and State Variable Names -* Order of Layout +* Title Case for Headings. +* For Headings Use Title Case. +* Local and State Variable Names. +* Order of Layout. Expand Contractions ------------------- @@ -443,10 +449,28 @@ or ``interface`` using the ``./test/cmdlineTests.sh`` script when you create a P ensure they work and pass tests before creating the PR. Ensure that all code examples begin with a ``pragma`` version that spans the largest where the contract code is valid. -For example ``pragma solidity >=0.4.0 <0.8.0;``. +For example ``pragma solidity >=0.4.0 <0.9.0;``. Running Documentation Tests --------------------------- Make sure your contributions pass our documentation tests by running ``./scripts/docs.sh`` that installs dependencies needed for documentation and checks for any problems such as broken links or syntax issues. + +Solidity Language Design +======================== + +If you want to get involved in the language design process and share your ideas, please join the `solidity-users forum `_, +where existing properties of the language and proposals for new features can be discussed. + +We regularly host language design discussion calls, in which selected topics, issues or feature implementations are debated in detail. The invitation +to those calls is shared via the aforementioned forum. We are also sharing feedback surveys and other language design relevant content in this forum. + +For ad-hoc cases and questions you can reach out to us via the `Solidity-dev Gitter channel `_, a +dedicated chatroom for conversations around the Solidity compiler and language development. + +You can follow the implementation status of new features in the `Solidity Github project `_. +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 `_. + +We are happy to hear your thoughts on how we can improve the language design process to be even more collaborative and transparent. \ No newline at end of file diff --git a/docs/control-structures.rst b/docs/control-structures.rst index eba075fc8f9a..b832582573de 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -19,7 +19,7 @@ Solidity also supports exception handling in the form of ``try``/``catch``-state but only for :ref:`external function calls ` and contract creation calls. -Parentheses can *not* be omitted for conditionals, but curly brances can be omitted +Parentheses can *not* be omitted for conditionals, but curly braces can be omitted around single-statement bodies. Note that there is no type conversion from non-boolean to boolean types as @@ -42,7 +42,7 @@ Functions of the current contract can be called directly ("internally"), also re this nonsensical example:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract C { function g(uint a) public pure returns (uint ret) { return a + f(); } @@ -84,7 +84,7 @@ to the total balance of that contract: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.2 <0.8.0; + pragma solidity >=0.6.2 <0.9.0; contract InfoFeed { function info() public payable returns (uint ret) { return 42; } @@ -105,8 +105,12 @@ otherwise, the ``value`` option would not be available. parentheses at the end perform the actual call. So in this case, the function is not called and the ``value`` and ``gas`` settings are lost. -Function calls cause exceptions if the called contract does not exist (in the -sense that the account does not contain code) or if the called contract itself +Due to the fact that the EVM considers a call to a non-existing contract to +always succeed, Solidity uses the ``extcodesize`` opcode to check that +the contract that is about to be called actually exists (it contains code) +and causes an exception if it does not. + +Function calls also cause exceptions if the called contract itself throws an exception or goes out of gas. .. warning:: @@ -140,7 +144,7 @@ parameters from the function declaration, but can be in arbitrary order. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract C { mapping(uint => uint) data; @@ -164,7 +168,7 @@ Those parameters will still be present on the stack, but they are inaccessible. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract C { // omitted name for parameter @@ -188,8 +192,7 @@ is compiled so recursive creation-dependencies are not possible. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract D { uint public x; constructor(uint a) payable { @@ -244,8 +247,7 @@ which only need to be created if there is a dispute. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract D { uint public x; constructor(uint a) { @@ -258,15 +260,15 @@ which only need to be created if there is a dispute. // This complicated expression just tells you how the address // can be pre-computed. It is just there for illustration. // You actually only need ``new D{salt: salt}(arg)``. - address predictedAddress = address(uint(keccak256(abi.encodePacked( - byte(0xff), + address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked( + bytes1(0xff), address(this), salt, keccak256(abi.encodePacked( type(D).creationCode, arg )) - )))); + ))))); D d = new D{salt: salt}(arg); require(address(d) == predictedAddress); @@ -314,7 +316,7 @@ groupings of expressions. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; contract C { uint index; @@ -360,7 +362,7 @@ because only a reference and not a copy is passed. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract C { uint[20] x; @@ -419,7 +421,7 @@ the two variables have the same name but disjoint scopes. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; contract C { function minimalScoping() pure public { { @@ -441,7 +443,7 @@ In any case, you will get a warning about the outer variable being shadowed. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; // This will report a warning contract C { function f() pure public returns (uint) { @@ -463,7 +465,7 @@ In any case, you will get a warning about the outer variable being shadowed. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; // This will not compile contract C { function f() pure public returns (uint) { @@ -473,6 +475,69 @@ In any case, you will get a warning about the outer variable being shadowed. } } + +.. _unchecked: + +Checked or Unchecked Arithmetic +=============================== + +An overflow or underflow is the situation where the resulting value of an arithmetic operation, +when executed on an unrestricted integer, falls outside the range of the result type. + +Prior to Solidity 0.8.0, arithmetic operations would always wrap in case of +under- or overflow leading to widespread use of libraries that introduce +additional checks. + +Since Solidity 0.8.0, all arithmetic operations revert on over- and underflow by default, +thus making the use of these libraries unnecessary. + +To obtain the previous behaviour, an ``unchecked`` block can be used: + +:: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.99; + contract C { + function f(uint a, uint b) pure public returns (uint) { + // This addition will wrap on underflow. + unchecked { return a - b; } + } + function g(uint a, uint b) pure public returns (uint) { + // This addition will revert on underflow. + return a - b; + } + } + +The call to ``f(2, 3)`` will return ``2**256-1``, while ``g(2, 3)`` will cause +a failing assertion. + +The ``unchecked`` block can be used everywhere inside a block, but not as a replacement +for a block. It also cannot be nested. + +The setting only affects the statements that are syntactically inside the block. +Functions called from within an ``unchecked`` block do not inherit the property. + +.. note:: + To avoid ambiguity, you cannot use ``_;`` inside an ``unchecked`` block. + +The following operators will cause a failing assertion on overflow or underflow +and will wrap without an error if used inside an unchecked block: + +``++``, ``--``, ``+``, binary ``-``, unary ``-``, ``*``, ``/``, ``%``, ``**`` + +``+=``, ``-=``, ``*=``, ``/=``, ``%=`` + +.. warning:: + It is not possible to disable the check for division by zero + or modulo by zero using the ``unchecked`` block. + +.. note:: + The second statement in ``int x = type(int).min; -x;`` will result in an overflow + because the negative range can hold one more value than the positive range. + +Explicit type conversions will always truncate and never cause a failing assertion +with the exception of a conversion from an integer to an enum type. + .. index:: ! exception, ! throw, ! assert, ! require, ! revert, ! errors .. _assert-and-require: @@ -497,39 +562,69 @@ of an exception instead of "bubbling up". if the account called is non-existent, as part of the design of the EVM. Account existence must be checked prior to calling if needed. -Exceptions can be caught with the ``try``/``catch`` statement. +Exceptions in external calls can be caught with the ``try``/``catch`` statement. -``assert`` and ``require`` --------------------------- +Exceptions can contain data that is passed back to the caller. +This data consists of a 4-byte selector and subsequent :ref:`ABI-encoded` data. +The selector is computed in the same way as a function selector, i.e., +the first four bytes of the keccak256-hash of a function +signature - in this case an error signature. + +Currently, Solidity supports two error signatures: ``Error(string)`` +and ``Panic(uint256)``. The first ("error") is used for "regular" error conditions +while the second ("panic") is used for errors that should not be present in bug-free code. + +Panic via ``assert`` and Error via ``require`` +---------------------------------------------- The convenience functions ``assert`` and ``require`` can be used to check for conditions and throw an exception if the condition is not met. -The ``assert`` function should only be used to test for internal +The ``assert`` function creates an error of type ``Panic(uint256)``. +The same error is created by the compiler in certain situations as listed below. + +Assert should only be used to test for internal errors, and to check invariants. Properly functioning code should -never reach a failing ``assert`` statement; if this happens there +never create a Panic, not even on invalid external input. +If this happens, then there is a bug in your contract which you should fix. Language analysis tools can evaluate your contract to identify the conditions and -function calls which will reach a failing ``assert``. - -An ``assert``-style exception is generated in the following situations: - -#. If you access an array or an array slice at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). -#. If you access a fixed-length ``bytesN`` at a too large or negative index. -#. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). -#. If you shift by a negative amount. -#. If you convert a value too big or negative into an enum type. -#. If you call a zero-initialized variable of internal function type. -#. If you call ``assert`` with an argument that evaluates to false. - -The ``require`` function should be used to ensure valid conditions +function calls which will cause a Panic. + +A Panic exception is generated in the following situations. +The error code supplied with the error data indicates the kind of panic. + +#. 0x01: If you call ``assert`` with an argument that evaluates to false. +#. 0x11: If an arithmetic operation results in underflow or overflow outside of an ``unchecked { ... }`` block. +#. 0x12; If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). +#. 0x21: If you convert a value that is too big or negative into an enum type. +#. 0x22: If you access a storage byte array that is incorrectly encoded. +#. 0x31: If you call ``.pop()`` on an empty array. +#. 0x32: If you access an array, ``bytesN`` or an array slice at an out-of-bounds or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``). +#. 0x41: If you allocate too much memory or create an array that is too large. +#. 0x51: If you call a zero-initialized variable of internal function type. + +The ``require`` function either creates an error of type ``Error(string)`` +or an error without any error data and it +should be used to ensure valid conditions that cannot be detected until execution time. This includes conditions on inputs or return values from calls to external contracts. -A ``require``-style exception is generated in the following situations: +A ``Error(string)`` exception (or an exception without data) is generated +in the following situations: #. Calling ``require`` with an argument that evaluates to ``false``. +#. If you perform an external function call targeting a contract that contains no code. +#. If your contract receives Ether via a public function without + ``payable`` modifier (including the constructor and the fallback function). +#. If your contract receives Ether via a public getter function. + +For the following cases, the error data from the external call +(if provided) is forwarded. This mean that it can either cause +an `Error` or a `Panic` (or whatever else was given): + +#. If a ``.transfer()`` fails. #. If you call a function via a message call but it does not finish properly (i.e., it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation @@ -538,21 +633,21 @@ A ``require``-style exception is generated in the following situations: indicate failures by returning ``false``. #. If you create a contract using the ``new`` keyword but the contract creation :ref:`does not finish properly`. -#. If you perform an external function call targeting a contract that contains no code. -#. If your contract receives Ether via a public function without - ``payable`` modifier (including the constructor and the fallback function). -#. If your contract receives Ether via a public getter function. -#. If a ``.transfer()`` fails. You can optionally provide a message string for ``require``, but not for ``assert``. +.. note:: + If you do not provide a string argument to ``require``, it will revert + with empty error data, not even including the error selector. + + The following example shows how you can use ``require`` to check conditions on inputs and ``assert`` for internal error checking. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; contract Sharer { function sendHalf(address payable addr) public payable returns (uint balance) { @@ -568,36 +663,36 @@ and ``assert`` for internal error checking. } Internally, Solidity performs a revert operation (instruction -``0xfd``) for a ``require``-style exception and executes an invalid operation -(instruction ``0xfe``) to throw an ``assert``-style exception. In both cases, this causes +``0xfd``). This causes the EVM to revert all changes made to the state. The reason for reverting is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to keep the atomicity of transactions, the safest action is to revert all changes and make the whole transaction (or at least call) without effect. -In both cases, the caller can react on such failures using ``try``/``catch`` -(in the failing ``assert``-style exception only if enough gas is left), but +In both cases, the caller can react on such failures using ``try``/``catch``, but the changes in the caller will always be reverted. .. note:: - ``assert``-style exceptions consume all gas available to the call, - while ``require``-style exceptions do not consume any gas starting from the Metropolis release. + Panic exceptions used to use the ``invalid`` opcode before Solidity 0.8.0, + which consumed all gas available to the call. + Exceptions that use ``require`` used to consume all gas until before the Metropolis release. ``revert`` ---------- The ``revert`` function is another way to trigger exceptions from within other code blocks to flag an error and revert the current call. The function takes an optional string -message containing details about the error that is passed back to the caller. +message containing details about the error that is passed back to the caller +and it will create an ``Error(string)`` exception. The following example shows how to use an error string together with ``revert`` and the equivalent ``require``: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; contract VendingMachine { function buy(uint amount) public payable { @@ -612,7 +707,13 @@ The following example shows how to use an error string together with ``revert`` } } -The two syntax options are equivalent, it's developer preference which to use. +If you provide the reason string directly, then the two syntax options are equivalent, it is the developer's preference which one to use. + +.. note:: + The ``require`` function is evaluated just as any other function. + This means that all arguments are evaluated before the function itself is executed. + In particular, in ``require(condition, f())`` the function ``f`` is executed even if + ``condition`` is true. The provided string is :ref:`abi-encoded ` as if it were a call to a function ``Error(string)``. In the above example, ``revert("Not enough Ether provided.");`` returns the following hexadecimal as error return data: @@ -641,7 +742,7 @@ A failure in an external call can be caught using a try/catch statement, as foll :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; interface DataFeed { function getData(address token) external returns (uint value); } @@ -661,9 +762,7 @@ A failure in an external call can be caught using a try/catch statement, as foll errorCount++; return (0, false); } catch (bytes memory /*lowLevelData*/) { - // This is executed in case revert() was used - // or there was a failing assertion, division - // by zero, etc. inside getData. + // This is executed in case revert() was used. errorCount++; return (0, false); } @@ -689,9 +788,8 @@ It is planned to support other types of error data in the future. The string ``Error`` is currently parsed as is and is not treated as an identifier. The clause ``catch (bytes memory lowLevelData)`` is executed if the error signature -does not match any other clause, there was an error during decoding of the error -message, if there was a failing assertion in the external -call (for example due to a division by zero or a failing ``assert()``) or +does not match any other clause, if there was an error while decoding the error +message, or if no error data was provided with the exception. The declared variable provides access to the low-level error data in that case. diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index 3034b69150f8..fec9c279b088 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -25,8 +25,7 @@ to receive their money - contracts cannot activate themselves. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract SimpleAuction { // Parameters of the auction. Times are either // absolute unix timestamps (seconds since 1970-01-01) @@ -115,7 +114,7 @@ to receive their money - contracts cannot activate themselves. // before `send` returns. pendingReturns[msg.sender] = 0; - if (!msg.sender.send(amount)) { + if (!payable(msg.sender).send(amount)) { // No need to call throw here, just reset the amount owing pendingReturns[msg.sender] = amount; return false; @@ -186,8 +185,7 @@ invalid bids. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract BlindAuction { struct Bid { bytes32 blindedBid; @@ -282,7 +280,7 @@ invalid bids. // the same deposit. bidToCheck.blindedBid = bytes32(0); } - msg.sender.transfer(refund); + payable(msg.sender).transfer(refund); } /// Withdraw a bid that was overbid. @@ -295,7 +293,7 @@ invalid bids. // conditions -> effects -> interaction). pendingReturns[msg.sender] = 0; - msg.sender.transfer(amount); + payable(msg.sender).transfer(amount); } } diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index b68de3cadcbe..8bfa764f8d7e 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -61,12 +61,11 @@ For a contract that fulfils payments, the signed message must include: 2. The amount to be transferred. 3. Protection against replay attacks. -A replay attack is when a signed message is reused to claim authorization for -a second action. -To avoid replay attacks we use the same as in Ethereum transactions -themselves, a so-called nonce, which is the number of transactions sent by an -account. -The smart contract checks if a nonce is used multiple times. +A replay attack is when a signed message is reused to claim +authorization for a second action. To avoid replay attacks +we use the same technique as in Ethereum transactions themselves, +a so-called nonce, which is the number of transactions sent by +an account. The smart contract checks if a nonce is used multiple times. Another type of replay attack can occur when the owner deploys a ``ReceiverPays`` smart contract, makes some @@ -114,7 +113,7 @@ In general, ECDSA signatures consist of two parameters, parameter called ``v``, that you can use to verify which account's private key was used to sign the message, and the transaction's sender. Solidity provides a built-in -function `ecrecover `_ that +function :ref:`ecrecover ` that accepts a message along with the ``r``, ``s`` and ``v`` parameters and returns the address that was used to sign the message. @@ -127,7 +126,7 @@ apart. You can do this on the client-side, but doing it inside the smart contract means you only need to send one signature parameter rather than three. Splitting apart a byte array into its constituent parts is a mess, so we use -`inline assembly `_ to do the job in the ``splitSignature`` +:doc:`inline assembly ` to do the job in the ``splitSignature`` function (the third function in the full contract at the end of this section). Computing the Message Hash @@ -143,8 +142,7 @@ The full contract :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract ReceiverPays { address owner = msg.sender; @@ -161,13 +159,13 @@ The full contract require(recoverSigner(message, signature) == owner); - msg.sender.transfer(amount); + payable(msg.sender).transfer(amount); } /// destroy the contract and reclaim the leftover funds. function shutdown() public { require(msg.sender == owner); - selfdestruct(msg.sender); + selfdestruct(payable(msg.sender)); } /// signature methods. @@ -340,8 +338,7 @@ The full contract :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract SimplePaymentChannel { address payable public sender; // The account sending payments. address payable public recipient; // The account receiving the payments. @@ -350,7 +347,7 @@ The full contract constructor (address payable _recipient, uint256 duration) payable { - sender = msg.sender; + sender = payable(msg.sender); recipient = _recipient; expiration = block.timestamp + duration; } diff --git a/docs/examples/modular.rst b/docs/examples/modular.rst index 78986ece5732..5ad6a691d454 100644 --- a/docs/examples/modular.rst +++ b/docs/examples/modular.rst @@ -20,7 +20,7 @@ and the sum of all balances is an invariant across the lifetime of the contract. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; library Balances { function move(mapping(address => uint256) storage balances, address from, address to, uint amount) internal { diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst index bae6a3a50a71..6b46007fd602 100644 --- a/docs/examples/safe-remote.rst +++ b/docs/examples/safe-remote.rst @@ -26,8 +26,7 @@ you can use state machine-like constructs inside a contract. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract Purchase { uint public value; address payable public seller; @@ -75,7 +74,7 @@ you can use state machine-like constructs inside a contract. // Division will truncate if it is an odd number. // Check via multiplication that it wasn't an odd number. constructor() payable { - seller = msg.sender; + seller = payable(msg.sender); value = msg.value / 2; require((2 * value) == msg.value, "Value has to be even."); } @@ -108,7 +107,7 @@ you can use state machine-like constructs inside a contract. payable { emit PurchaseConfirmed(); - buyer = msg.sender; + buyer = payable(msg.sender); state = State.Locked; } @@ -143,4 +142,4 @@ you can use state machine-like constructs inside a contract. seller.transfer(3 * value); } - } \ No newline at end of file + } diff --git a/docs/examples/voting.rst b/docs/examples/voting.rst index 56a880edf6d9..299816fd97f2 100644 --- a/docs/examples/voting.rst +++ b/docs/examples/voting.rst @@ -33,8 +33,7 @@ of votes. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; /// @title Voting with delegation. contract Ballot { // This declares a new complex type which will diff --git a/docs/grammar.rst b/docs/grammar.rst index c7a22dd7e92c..d69f86b089ac 100644 --- a/docs/grammar.rst +++ b/docs/grammar.rst @@ -2,5 +2,12 @@ Language Grammar **************** -.. literalinclude:: Solidity.g4 - :language: antlr +.. a4:autogrammar:: Solidity + :only-reachable-from: Solidity.sourceUnit + :undocumented: + :cc-to-dash: + +.. a4:autogrammar:: SolidityLexer + :only-reachable-from: Solidity.sourceUnit + :fragments: + :cc-to-dash: \ No newline at end of file diff --git a/docs/grammar/Solidity.g4 b/docs/grammar/Solidity.g4 new file mode 100644 index 000000000000..3d66df9b6470 --- /dev/null +++ b/docs/grammar/Solidity.g4 @@ -0,0 +1,549 @@ +/** + * Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. + */ +grammar Solidity; + +options { tokenVocab=SolidityLexer; } + +/** + * On top level, Solidity allows pragmas, import directives, and + * definitions of contracts, interfaces, libraries, structs, enums and constants. + */ +sourceUnit: ( + pragmaDirective + | importDirective + | contractDefinition + | interfaceDefinition + | libraryDefinition + | functionDefinition + | constantVariableDeclaration + | structDefinition + | enumDefinition +)* EOF; + +//@doc: inline +pragmaDirective: Pragma PragmaToken+ PragmaSemicolon; + +/** + * Import directives import identifiers from different files. + */ +importDirective: + Import ( + (path (As unitAlias=identifier)?) + | (symbolAliases From path) + | (Mul As unitAlias=identifier From path) + ) Semicolon; +//@doc: inline +//@doc:name aliases +importAliases: symbol=identifier (As alias=identifier)?; +/** + * Path of a file to be imported. + */ +path: NonEmptyStringLiteral; +/** + * List of aliases for symbols to be imported. + */ +symbolAliases: LBrace aliases+=importAliases (Comma aliases+=importAliases)* RBrace; + +/** + * Top-level definition of a contract. + */ +contractDefinition: + Abstract? Contract name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of an interface. + */ +interfaceDefinition: + Interface name=identifier + inheritanceSpecifierList? + LBrace contractBodyElement* RBrace; +/** + * Top-level definition of a library. + */ +libraryDefinition: Library name=identifier LBrace contractBodyElement* RBrace; + +//@doc:inline +inheritanceSpecifierList: + Is inheritanceSpecifiers+=inheritanceSpecifier + (Comma inheritanceSpecifiers+=inheritanceSpecifier)*?; +/** + * Inheritance specifier for contracts and interfaces. + * Can optionally supply base constructor arguments. + */ +inheritanceSpecifier: name=identifierPath arguments=callArgumentList?; + +/** + * Declarations that can be used in contracts, interfaces and libraries. + * + * Note that interfaces and libraries may not contain constructors, interfaces may not contain state variables + * and libraries may not contain fallback, receive functions nor non-constant state variables. + */ +contractBodyElement: + constructorDefinition + | functionDefinition + | modifierDefinition + | fallbackFunctionDefinition + | receiveFunctionDefinition + | structDefinition + | enumDefinition + | stateVariableDeclaration + | eventDefinition + | usingDirective; +//@doc:inline +namedArgument: name=identifier Colon value=expression; +/** + * Arguments when calling a function or a similar callable object. + * The arguments are either given as comma separated list or as map of named arguments. + */ +callArgumentList: LParen ((expression (Comma expression)*)? | LBrace (namedArgument (Comma namedArgument)*)? RBrace) RParen; +/** + * Qualified name. + */ +identifierPath: identifier (Period identifier)*; + +/** + * Call to a modifier. If the modifier takes no arguments, the argument list can be skipped entirely + * (including opening and closing parentheses). + */ +modifierInvocation: identifierPath callArgumentList?; +/** + * Visibility for functions and function types. + */ +visibility: Internal | External | Private | Public; +/** + * A list of parameters, such as function arguments or return values. + */ +parameterList: parameters+=parameterDeclaration (Comma parameters+=parameterDeclaration)*; +//@doc:inline +parameterDeclaration: type=typeName location=dataLocation? name=identifier?; +/** + * Definition of a constructor. + * Must always supply an implementation. + * Note that specifying internal or public visibility is deprecated. + */ +constructorDefinition +locals[boolean payableSet = false, boolean visibilitySet = false] +: + Constructor LParen (arguments=parameterList)? RParen + ( + modifierInvocation + | {!$payableSet}? Payable {$payableSet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$visibilitySet}? Public {$visibilitySet = true;} + )* + body=block; + +/** + * State mutability for function types. + * The default mutability 'non-payable' is assumed if no mutability is specified. + */ +stateMutability: Pure | View | Payable; +/** + * An override specifier used for functions, modifiers or state variables. + * In cases where there are ambiguous declarations in several base contracts being overridden, + * a complete list of base contracts has to be given. + */ +overrideSpecifier: Override (LParen overrides+=identifierPath (Comma overrides+=identifierPath)* RParen)?; +/** + * The definition of contract, library and interface functions. + * Depending on the context in which the function is defined, further restrictions may apply, + * e.g. functions in interfaces have to be unimplemented, i.e. may not contain a body block. + */ +functionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Function (identifier | Fallback | Receive) + LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)? + (Semicolon | body=block); +/** + * The definition of a modifier. + * Note that within the body block of a modifier, the underscore cannot be used as identifier, + * but is used as placeholder statement for the body of a function to which the modifier is applied. + */ +modifierDefinition +locals[ + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + Modifier name=identifier + (LParen (arguments=parameterList)? RParen)? + ( + {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definition of the special fallback function. + */ +fallbackFunctionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false, + boolean hasParameters = false +] +: + kind=Fallback LParen (parameterList { $hasParameters = true; } )? RParen + ( + {!$visibilitySet}? External {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + ( {$hasParameters}? Returns LParen returnParameters=parameterList RParen | {!$hasParameters}? ) + (Semicolon | body=block); + +/** + * Definition of the special receive function. + */ +receiveFunctionDefinition +locals[ + boolean visibilitySet = false, + boolean mutabilitySet = false, + boolean virtualSet = false, + boolean overrideSpecifierSet = false +] +: + kind=Receive LParen RParen + ( + {!$visibilitySet}? External {$visibilitySet = true;} + | {!$mutabilitySet}? Payable {$mutabilitySet = true;} + | modifierInvocation + | {!$virtualSet}? Virtual {$virtualSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + )* + (Semicolon | body=block); + +/** + * Definition of a struct. Can occur at top-level within a source unit or within a contract, library or interface. + */ +structDefinition: Struct name=identifier LBrace members=structMember+ RBrace; +/** + * The declaration of a named struct member. + */ +structMember: type=typeName name=identifier Semicolon; +/** + * Definition of an enum. Can occur at top-level within a source unit or within a contract, library or interface. + */ +enumDefinition: Enum name=identifier LBrace enumValues+=identifier (Comma enumValues+=identifier)* RBrace; + +/** + * The declaration of a state variable. + */ +stateVariableDeclaration +locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean overrideSpecifierSet = false] +: + type=typeName + ( + {!$visibilitySet}? Public {$visibilitySet = true;} + | {!$visibilitySet}? Private {$visibilitySet = true;} + | {!$visibilitySet}? Internal {$visibilitySet = true;} + | {!$constantnessSet}? Constant {$constantnessSet = true;} + | {!$overrideSpecifierSet}? overrideSpecifier {$overrideSpecifierSet = true;} + | {!$constantnessSet}? Immutable {$constantnessSet = true;} + )* + name=identifier + (Assign initialValue=expression)? + Semicolon; + +/** + * The declaration of a constant variable. + */ +constantVariableDeclaration +: + type=typeName + Constant + name=identifier + Assign initialValue=expression + Semicolon; + +/** + * Parameter of an event. + */ +eventParameter: type=typeName Indexed? name=identifier?; +/** + * Definition of an event. Can occur in contracts, libraries or interfaces. + */ +eventDefinition: + Event name=identifier + LParen (parameters+=eventParameter (Comma parameters+=eventParameter)*)? RParen + Anonymous? + Semicolon; + +/** + * Using directive to bind library functions to types. + * Can occur within contracts and libraries. + */ +usingDirective: Using identifierPath For (Mul | typeName) Semicolon; +/** + * A type name can be an elementary type, a function type, a mapping type, a user-defined type + * (e.g. a contract or struct) or an array type. + */ +typeName: elementaryTypeName[true] | functionTypeName | mappingType | identifierPath | typeName LBrack expression? RBrack; +elementaryTypeName[boolean allowAddressPayable]: Address | {$allowAddressPayable}? Address Payable | Bool | String | Bytes | SignedIntegerType | UnsignedIntegerType | FixedBytes | Fixed | Ufixed; +functionTypeName +locals [boolean visibilitySet = false, boolean mutabilitySet = false] +: + Function LParen (arguments=parameterList)? RParen + ( + {!$visibilitySet}? visibility {$visibilitySet = true;} + | {!$mutabilitySet}? stateMutability {$mutabilitySet = true;} + )* + (Returns LParen returnParameters=parameterList RParen)?; + +/** + * The declaration of a single variable. + */ +variableDeclaration: type=typeName location=dataLocation? name=identifier; +dataLocation: Memory | Storage | Calldata; + +/** + * Complex expression. + * Can be an index access, an index range access, a member access, a function call (with optional function call options), + * a type conversion, an unary or binary expression, a comparison or assignment, a ternary expression, + * a new-expression (i.e. a contract creation or the allocation of a dynamic memory array), + * a tuple, an inline array or a primary expression (i.e. an identifier, literal or type name). + */ +expression: + expression LBrack index=expression? RBrack # IndexAccess + | expression LBrack start=expression? Colon end=expression? RBrack # IndexRangeAccess + | expression Period (identifier | Address) # MemberAccess + | expression LBrace (namedArgument (Comma namedArgument)*)? RBrace # FunctionCallOptions + | expression callArgumentList # FunctionCall + | Payable callArgumentList # PayableConversion + | Type LParen typeName RParen # MetaType + | (Inc | Dec | Not | BitNot | Delete | Sub) expression # UnaryPrefixOperation + | expression (Inc | Dec) # UnarySuffixOperation + | expression Exp expression # ExpOperation + | expression (Mul | Div | Mod) expression # MulDivModOperation + | expression (Add | Sub) expression # AddSubOperation + | expression (Shl | Sar | Shr) expression # ShiftOperation + | expression BitAnd expression # BitAndOperation + | expression BitXor expression # BitXorOperation + | expression BitOr expression # BitOrOperation + | expression (LessThan | GreaterThan | LessThanOrEqual | GreaterThanOrEqual) expression # OrderComparison + | expression (Equal | NotEqual) expression # EqualityComparison + | expression And expression # AndOperation + | expression Or expression # OrOperation + | expression Conditional expression Colon expression # Conditional + | expression assignOp expression # Assignment + | New typeName # NewExpression + | tupleExpression # Tuple + | inlineArrayExpression # InlineArray + | ( + identifier + | literal + | elementaryTypeName[false] + ) # PrimaryExpression +; + +//@doc:inline +assignOp: Assign | AssignBitOr | AssignBitXor | AssignBitAnd | AssignShl | AssignSar | AssignShr | AssignAdd | AssignSub | AssignMul | AssignDiv | AssignMod; +tupleExpression: LParen (expression? ( Comma expression?)* ) RParen; +/** + * An inline array expression denotes a statically sized array of the common type of the contained expressions. + */ +inlineArrayExpression: LBrack (expression ( Comma expression)* ) RBrack; + +/** + * Besides regular non-keyword Identifiers, the 'from' keyword can also occur as identifier outside of import statements. + */ +identifier: Identifier | From; + +literal: stringLiteral | numberLiteral | booleanLiteral | hexStringLiteral | unicodeStringLiteral; +booleanLiteral: True | False; +/** + * A full string literal consists of either one or several consecutive quoted strings. + */ +stringLiteral: StringLiteral+; +/** + * A full hex string literal that consists of either one or several consecutive hex strings. + */ +hexStringLiteral: HexString+; +/** + * A full unicode string literal that consists of either one or several consecutive unicode strings. + */ +unicodeStringLiteral: UnicodeStringLiteral+; + +/** + * Number literals can be decimal or hexadecimal numbers with an optional unit. + */ +numberLiteral: (DecimalNumber | HexNumber) NumberUnit?; +/** + * A curly-braced block of statements. Opens its own scope. + */ +block: + LBrace ( statement | uncheckedBlock )* RBrace; + +uncheckedBlock: Unchecked block; + +statement: + block + | simpleStatement + | ifStatement + | forStatement + | whileStatement + | doWhileStatement + | continueStatement + | breakStatement + | tryStatement + | returnStatement + | emitStatement + | assemblyStatement +; + +//@doc:inline +simpleStatement: variableDeclarationStatement | expressionStatement; +/** + * If statement with optional else part. + */ +ifStatement: If LParen expression RParen statement (Else statement)?; +/** + * For statement with optional init, condition and post-loop part. + */ +forStatement: For LParen (simpleStatement | Semicolon) (expressionStatement | Semicolon) expression? RParen statement; +whileStatement: While LParen expression RParen statement; +doWhileStatement: Do statement While LParen expression RParen Semicolon; +/** + * A continue statement. Only allowed inside for, while or do-while loops. + */ +continueStatement: Continue Semicolon; +/** + * A break statement. Only allowed inside for, while or do-while loops. + */ +breakStatement: Break Semicolon; +/** + * A try statement. The contained expression needs to be an external function call or a contract creation. + */ +tryStatement: Try expression (Returns LParen returnParameters=parameterList RParen)? block catchClause+; +/** + * The catch clause of a try statement. + */ +catchClause: Catch (identifier? LParen (arguments=parameterList) RParen)? block; + +returnStatement: Return expression? Semicolon; +/** + * An emit statement. The contained expression needs to refer to an event. + */ +emitStatement: Emit expression callArgumentList Semicolon; +/** + * An inline assembly block. + * The contents of an inline assembly block use a separate scanner/lexer, i.e. the set of keywords and + * allowed identifiers is different inside an inline assembly block. + */ +assemblyStatement: Assembly AssemblyDialect? AssemblyLBrace yulStatement* YulRBrace; + +//@doc:inline +variableDeclarationList: variableDeclarations+=variableDeclaration (Comma variableDeclarations+=variableDeclaration)*; +/** + * A tuple of variable names to be used in variable declarations. + * May contain empty fields. + */ +variableDeclarationTuple: + LParen + (Comma* variableDeclarations+=variableDeclaration) + (Comma (variableDeclarations+=variableDeclaration)?)* + RParen; +/** + * A variable declaration statement. + * A single variable may be declared without initial value, whereas a tuple of variables can only be + * declared with initial value. + */ +variableDeclarationStatement: ((variableDeclaration (Assign expression)?) | (variableDeclarationTuple Assign expression)) Semicolon; +expressionStatement: expression Semicolon; + +mappingType: Mapping LParen key=mappingKeyType DoubleArrow value=typeName RParen; +/** + * Only elementary types or user defined types are viable as mapping keys. + */ +mappingKeyType: elementaryTypeName[false] | identifierPath; + +/** + * A Yul statement within an inline assembly block. + * continue and break statements are only valid within for loops. + * leave statements are only valid within function bodies. + */ +yulStatement: + yulBlock + | yulVariableDeclaration + | yulAssignment + | yulFunctionCall + | yulIfStatement + | yulForStatement + | yulSwitchStatement + | YulLeave + | YulBreak + | YulContinue + | yulFunctionDefinition; + +yulBlock: YulLBrace yulStatement* YulRBrace; + +/** + * The declaration of one or more Yul variables with optional initial value. + * If multiple variables are declared, only a function call is a valid initial value. + */ +yulVariableDeclaration: + (YulLet variables+=YulIdentifier (YulAssign yulExpression)?) + | (YulLet variables+=YulIdentifier (YulComma variables+=YulIdentifier)* (YulAssign yulFunctionCall)?); + +/** + * Any expression can be assigned to a single Yul variable, whereas + * multi-assignments require a function call on the right-hand side. + */ +yulAssignment: yulPath YulAssign yulExpression | (yulPath (YulComma yulPath)+) YulAssign yulFunctionCall; + +yulIfStatement: YulIf cond=yulExpression body=yulBlock; + +yulForStatement: YulFor init=yulBlock cond=yulExpression post=yulBlock body=yulBlock; + +//@doc:inline +yulSwitchCase: YulCase yulLiteral yulBlock; +/** + * A Yul switch statement can consist of only a default-case (deprecated) or + * one or more non-default cases optionally followed by a default-case. + */ +yulSwitchStatement: + YulSwitch yulExpression + ( + (yulSwitchCase+ (YulDefault yulBlock)?) + | (YulDefault yulBlock) + ); + +yulFunctionDefinition: + YulFunction YulIdentifier + YulLParen (arguments+=YulIdentifier (YulComma arguments+=YulIdentifier)*)? YulRParen + (YulArrow returnParameters+=YulIdentifier (YulComma returnParameters+=YulIdentifier)*)? + body=yulBlock; + +/** + * While only identifiers without dots can be declared within inline assembly, + * paths containing dots can refer to declarations outside the inline assembly block. + */ +yulPath: YulIdentifier (YulPeriod YulIdentifier)*; +/** + * A call to a function with return values can only occur as right-hand side of an assignment or + * a variable declaration. + */ +yulFunctionCall: (YulIdentifier | YulEVMBuiltin) YulLParen (yulExpression (YulComma yulExpression)*)? YulRParen; +yulBoolean: YulTrue | YulFalse; +yulLiteral: YulDecimalNumber | YulStringLiteral | YulHexNumber | yulBoolean; +yulExpression: yulPath | yulFunctionCall | yulLiteral; diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 new file mode 100644 index 000000000000..d8e89b27800d --- /dev/null +++ b/docs/grammar/SolidityLexer.g4 @@ -0,0 +1,334 @@ +lexer grammar SolidityLexer; + +/** + * Keywords reserved for future use in Solidity. + */ +ReservedKeywords: + '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'; + +Pragma: 'pragma' -> pushMode(PragmaMode); +Abstract: 'abstract'; +Anonymous: 'anonymous'; +Address: 'address'; +As: 'as'; +Assembly: 'assembly' -> pushMode(AssemblyBlockMode); +Bool: 'bool'; +Break: 'break'; +Bytes: 'bytes'; +Calldata: 'calldata'; +Catch: 'catch'; +Constant: 'constant'; +Constructor: 'constructor'; +Continue: 'continue'; +Contract: 'contract'; +Delete: 'delete'; +Do: 'do'; +Else: 'else'; +Emit: 'emit'; +Enum: 'enum'; +Event: 'event'; +External: 'external'; +Fallback: 'fallback'; +False: 'false'; +Fixed: 'fixed' | ('fixed' [1-9][0-9]* 'x' [1-9][0-9]*); +From: 'from'; +/** + * Bytes types of fixed length. + */ +FixedBytes: + 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | + 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | + 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | + 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32'; +For: 'for'; +Function: 'function'; +Hex: 'hex'; +If: 'if'; +Immutable: 'immutable'; +Import: 'import'; +Indexed: 'indexed'; +Interface: 'interface'; +Internal: 'internal'; +Is: 'is'; +Library: 'library'; +Mapping: 'mapping'; +Memory: 'memory'; +Modifier: 'modifier'; +New: 'new'; +/** + * Unit denomination for numbers. + */ +NumberUnit: 'wei' | 'gwei' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'; +Override: 'override'; +Payable: 'payable'; +Private: 'private'; +Public: 'public'; +Pure: 'pure'; +Receive: 'receive'; +Return: 'return'; +Returns: 'returns'; +/** + * Sized signed integer types. + * int is an alias of int256. + */ +SignedIntegerType: + 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | + 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | + 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | + 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256'; +Storage: 'storage'; +String: 'string'; +Struct: 'struct'; +True: 'true'; +Try: 'try'; +Type: 'type'; +Ufixed: 'ufixed' | ('ufixed' [1-9][0-9]+ 'x' [1-9][0-9]+); +Unchecked: 'unchecked'; +/** + * Sized unsigned integer types. + * uint is an alias of uint256. + */ +UnsignedIntegerType: + 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | + 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | + 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | + 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256'; +Using: 'using'; +View: 'view'; +Virtual: 'virtual'; +While: 'while'; + +LParen: '('; +RParen: ')'; +LBrack: '['; +RBrack: ']'; +LBrace: '{'; +RBrace: '}'; +Colon: ':'; +Semicolon: ';'; +Period: '.'; +Conditional: '?'; +DoubleArrow: '=>'; +RightArrow: '->'; + +Assign: '='; +AssignBitOr: '|='; +AssignBitXor: '^='; +AssignBitAnd: '&='; +AssignShl: '<<='; +AssignSar: '>>='; +AssignShr: '>>>='; +AssignAdd: '+='; +AssignSub: '-='; +AssignMul: '*='; +AssignDiv: '/='; +AssignMod: '%='; + +Comma: ','; +Or: '||'; +And: '&&'; +BitOr: '|'; +BitXor: '^'; +BitAnd: '&'; +Shl: '<<'; +Sar: '>>'; +Shr: '>>>'; +Add: '+'; +Sub: '-'; +Mul: '*'; +Div: '/'; +Mod: '%'; +Exp: '**'; + +Equal: '=='; +NotEqual: '!='; +LessThan: '<'; +GreaterThan: '>'; +LessThanOrEqual: '<='; +GreaterThanOrEqual: '>='; +Not: '!'; +BitNot: '~'; +Inc: '++'; +Dec: '--'; + +/** + * A single quoted string literal restricted to printable characters. + */ +StringLiteral: '"' DoubleQuotedStringCharacter* '"' | '\'' SingleQuotedStringCharacter* '\''; +/** + * A single non-empty quoted string literal. + */ +NonEmptyStringLiteral: '"' DoubleQuotedStringCharacter+ '"' | '\'' SingleQuotedStringCharacter+ '\''; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment DoubleQuotedStringCharacter: DoubleQuotedPrintable | EscapeSequence; +// Note that this will also be used for Yul string literals. +//@doc:inline +fragment SingleQuotedStringCharacter: SingleQuotedPrintable | EscapeSequence; +/** + * Any printable character except single quote or back slash. + */ +fragment SingleQuotedPrintable: [\u0020-\u0026\u0028-\u005B\u005D-\u007E]; +/** + * Any printable character except double quote or back slash. + */ +fragment DoubleQuotedPrintable: [\u0020-\u0021\u0023-\u005B\u005D-\u007E]; +/** + * Escape sequence. + * Apart from common single character escape sequences, line breaks can be escaped + * as well as four hex digit unicode escapes \\uXXXX and two digit hex escape sequences \\xXX are allowed. + */ +fragment EscapeSequence: + '\\' ( + ['"\\nrt\n\r] + | 'u' HexCharacter HexCharacter HexCharacter HexCharacter + | 'x' HexCharacter HexCharacter + ); +/** + * A single quoted string literal allowing arbitrary unicode characters. + */ +UnicodeStringLiteral: + 'unicode"' DoubleQuotedUnicodeStringCharacter* '"' + | 'unicode\'' SingleQuotedUnicodeStringCharacter* '\''; +//@doc:inline +fragment DoubleQuotedUnicodeStringCharacter: ~["\r\n\\] | EscapeSequence; +//@doc:inline +fragment SingleQuotedUnicodeStringCharacter: ~['\r\n\\] | EscapeSequence; + +/** + * Hex strings need to consist of an even number of hex digits that may be grouped using underscores. + */ +HexString: 'hex' (('"' EvenHexDigits? '"') | ('\'' EvenHexDigits? '\'')); +/** + * Hex numbers consist of a prefix and an arbitrary number of hex digits that may be delimited by underscores. + */ +HexNumber: '0' 'x' HexDigits; +//@doc:inline +fragment HexDigits: HexCharacter ('_'? HexCharacter)*; +//@doc:inline +fragment EvenHexDigits: HexCharacter HexCharacter ('_'? HexCharacter HexCharacter)*; +//@doc:inline +fragment HexCharacter: [0-9A-Fa-f]; + +/** + * A decimal number literal consists of decimal digits that may be delimited by underscores and + * an optional positive or negative exponent. + * If the digits contain a decimal point, the literal has fixed point type. + */ +DecimalNumber: (DecimalDigits | (DecimalDigits? '.' DecimalDigits)) ([eE] '-'? DecimalDigits)?; +//@doc:inline +fragment DecimalDigits: [0-9] ('_'? [0-9])* ; + + +/** + * An identifier in solidity has to start with a letter, a dollar-sign or an underscore and + * may additionally contain numbers after the first symbol. + */ +Identifier: IdentifierStart IdentifierPart*; +//@doc:inline +fragment IdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment IdentifierPart: [a-zA-Z0-9$_]; + +WS: [ \t\r\n\u000C]+ -> skip ; +COMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN); + +mode AssemblyBlockMode; + +//@doc:inline +AssemblyDialect: '"evmasm"'; +AssemblyLBrace: '{' -> popMode, pushMode(YulMode); + +AssemblyBlockWS: [ \t\r\n\u000C]+ -> skip ; +AssemblyBlockCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +AssemblyBlockLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode YulMode; + +YulBreak: 'break'; +YulCase: 'case'; +YulContinue: 'continue'; +YulDefault: 'default'; +YulFalse: 'false'; +YulFor: 'for'; +YulFunction: 'function'; +YulIf: 'if'; +YulLeave: 'leave'; +YulLet: 'let'; +YulSwitch: 'switch'; +YulTrue: 'true'; + +/** + * Builtin functions in the EVM Yul dialect. + */ +YulEVMBuiltin: + 'stop' | 'add' | 'sub' | 'mul' | 'div' | 'sdiv' | 'mod' | 'smod' | 'exp' | 'not' + | 'lt' | 'gt' | 'slt' | 'sgt' | 'eq' | 'iszero' | 'and' | 'or' | 'xor' | 'byte' + | 'shl' | 'shr' | 'sar' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' + | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' + | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' + | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' + | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' + | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' + | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' + | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'gaslimit'; + +YulLBrace: '{' -> pushMode(YulMode); +YulRBrace: '}' -> popMode; +YulLParen: '('; +YulRParen: ')'; +YulAssign: ':='; +YulPeriod: '.'; +YulComma: ','; +YulArrow: '->'; + +/** + * Yul identifiers consist of letters, dollar signs, underscores and numbers, but may not start with a number. + * In inline assembly there cannot be dots in user-defined identifiers. Instead see yulPath for expressions + * consisting of identifiers with dots. + */ +YulIdentifier: YulIdentifierStart YulIdentifierPart*; +//@doc:inline +fragment YulIdentifierStart: [a-zA-Z$_]; +//@doc:inline +fragment YulIdentifierPart: [a-zA-Z0-9$_]; +/** + * Hex literals in Yul consist of a prefix and one or more hexadecimal digits. + */ +YulHexNumber: '0' 'x' [0-9a-fA-F]+; +/** + * Decimal literals in Yul may be zero or any sequence of decimal digits without leading zeroes. + */ +YulDecimalNumber: '0' | ([1-9] [0-9]*); +/** + * String literals in Yul consist of one or more double-quoted or single-quoted strings + * that may contain escape sequences and printable characters except unescaped line breaks or + * unescaped double-quotes or single-quotes, respectively. + */ +YulStringLiteral: + '"' DoubleQuotedStringCharacter* '"' + | '\'' SingleQuotedStringCharacter* '\''; + + +YulWS: [ \t\r\n\u000C]+ -> skip ; +YulCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +YulLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; + +mode PragmaMode; + +/** + * Pragma token. Can contain any kind of symbol except a semicolon. + * Note that currently the solidity parser only allows a subset of this. + */ +//@doc:name pragma-token +//@doc:no-diagram +PragmaToken: ~[;]+; +PragmaSemicolon: ';' -> popMode; + +PragmaWS: [ \t\r\n\u000C]+ -> skip ; +PragmaCOMMENT: '/*' .*? '*/' -> channel(HIDDEN) ; +PragmaLINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN) ; diff --git a/docs/index.rst b/docs/index.rst index 470fc56fe465..26bf84b457e9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,42 +26,59 @@ a 0.x version number `to indicate this fast pace of change `. + Solidity recently released the 0.8.x version that introduced a lot of breaking + changes. Make sure you read :doc:`the full list <080-breaking-changes>`. -Language Documentation ----------------------- +Ideas for improving Solidity or this documentation are always welcome, +read our :doc:`contributors guide ` for more details. + +Getting Started +--------------- + +**1. Understand the Smart Contract Basics** + +If you are new to the concept of smart contracts we recommend you to get started by digging +into the "Introduction to Smart Contracts" section, which covers: + +* :ref:`A simple example smart contract ` written in Solidity. +* :ref:`Blockchain Basics `. +* :ref:`The Ethereum Virtual Machine `. + +**2. Get to Know Solidity** -If you are new to the concept of smart contracts we recommend you start with -:ref:`an example smart contract ` written -in Solidity. When you are ready for more detail, we recommend you read the -:doc:`"Solidity by Example" ` and -"Language Description" sections to learn the core concepts of the language. +Once you are accustomed to the basics, we recommend you read the :doc:`"Solidity by Example" ` +and “Language Description†sections to understand the core concepts of the language. -For further reading, try :ref:`the basics of blockchains ` -and details of the :ref:`Ethereum Virtual Machine `. +**3. Install the Solidity Compiler** + +There are various ways to install the Solidity compiler, +simply choose your preferred option and follow the steps outlined on the :ref:`installation page `. .. hint:: - You can always try out code examples in your browser with the + You can try out code examples directly in your browser with the `Remix IDE `_. Remix is a web browser based IDE - that allows you to write Solidity smart contracts, then deploy and run the - smart contracts. It can take a while to load, so please be patient. + that allows you to write, deploy and administer Solidity smart contracts, without + the need to install Solidity locally. .. warning:: As humans write software, it can have bugs. You should follow established - software development best-practices when writing your smart contracts, this + software development best-practices when writing your smart contracts. This includes code review, testing, audits, and correctness proofs. Smart contract users are sometimes more confident with code than their authors, and blockchains and smart contracts have their own unique issues to watch out for, so before working on production code, make sure you read the :ref:`security_considerations` section. -If you have any questions, you can try searching for answers or asking on the -`Ethereum Stackexchange `_, or -our `gitter channel `_. +**4. Learn More** -Ideas for improving Solidity or this documentation are always welcome, -read our :doc:`contributors guide ` for more details. +If you want to learn more about building decentralized applications on Ethereum, the +`Ethereum Developer Resources `_ +can help you with further general documentation around Ethereum, and a wide selection of tutorials, +tools and development frameworks. + +If you have any questions, you can try searching for answers or asking on the +`Ethereum StackExchange `_, or +our `Gitter channel `_. .. _translations: @@ -72,10 +89,10 @@ Community volunteers help translate this documentation into several languages. They have varying degrees of completeness and up-to-dateness. The English version stands as a reference. -* `French `_ (in progress) +* `French `_ (in progress) * `Italian `_ (in progress) * `Japanese `_ -* `Korean `_ (in progress) +* `Korean `_ (in progress) * `Russian `_ (rather outdated) * `Simplified Chinese `_ (in progress) * `Spanish `_ @@ -128,6 +145,7 @@ Contents 050-breaking-changes.rst 060-breaking-changes.rst 070-breaking-changes.rst + 080-breaking-changes.rst natspec-format.rst security-considerations.rst resources.rst diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 4f03a6b76be5..25d82ec93904 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -55,7 +55,7 @@ Please refer to the solc-js repository for instructions. The commandline executable is named ``solcjs``. - The comandline options of ``solcjs`` are not compatible with ``solc`` and tools (such as ``geth``) + The commandline options of ``solcjs`` are not compatible with ``solc`` and tools (such as ``geth``) expecting the behaviour of ``solc`` will not work with ``solcjs``. Docker @@ -92,8 +92,8 @@ When using this interface it is not necessary to mount any directories. docker run ethereum/solc:stable --standard-json < input.json > output.json -Binary Packages -=============== +Linux Packages +============== Binary packages of Solidity are available at `solidity/releases `_. @@ -143,6 +143,16 @@ Arch Linux also has packages, albeit limited to the latest development version: pacman -S solidity +Gentoo Linux has an `Ethereum overlay `_ that contains a Solidity package. +After the overlay is setup, ``solc`` can be installed in x86_64 architectures by: + +.. code-block:: bash + + emerge dev-lang/solidity + +macOS Packages +============== + We distribute the Solidity compiler through Homebrew as a build-from-source version. Pre-built bottles are currently not supported. @@ -163,8 +173,13 @@ Homebrew formula directly from Github. View `solidity.rb commits on Github `_. -Follow the history links until you have a raw file link of a -specific commit of ``solidity.rb``. +Copy the commit hash of the version you want and check it out on your machine. + +.. code-block:: bash + + git clone https://github.com/ethereum/homebrew-ethereum.git + cd homebrew-ethereum + git checkout Install it using ``brew``: @@ -172,14 +187,111 @@ Install it using ``brew``: brew unlink solidity # eg. Install 0.4.8 - brew install https://raw.githubusercontent.com/ethereum/homebrew-ethereum/77cce03da9f289e5a3ffe579840d3c5dc0a62717/solidity.rb + brew install solidity.rb -Gentoo Linux has an `Ethereum overlay `_ that contains a solidity package. -After the overlay is setup, ``solc`` can be installed in x86_64 architectures by: +Static Binaries +=============== -.. code-block:: bash +We maintain a repository containing static builds of past and current compiler versions for all +supported platforms at `solc-bin`_. This is also the location where you can find the nightly builds. + +The repository is not only a quick and easy way for end users to get binaries ready to be used +out-of-the-box but it is also meant to be friendly to third-party tools: + +- The content is mirrored to https://binaries.soliditylang.org where it can be easily downloaded over + HTTPS without any authentication, rate limiting or the need to use git. +- Content is served with correct `Content-Type` headers and lenient CORS configuration so that it + can be directly loaded by tools running in the browser. +- Binaries do not require installation or unpacking (with the exception of older Windows builds + bundled with necessary DLLs). +- We strive for a high level of backwards-compatibility. Files, once added, are not removed or moved + without providing a symlink/redirect at the old location. They are also never modified + in place and should always match the original checksum. The only exception would be broken or + unusable files with a potential to cause more harm than good if left as is. +- Files are served over both HTTP and HTTPS. As long as you obtain the file list in a secure way + (via git, HTTPS, IPFS or just have it cached locally) and verify hashes of the binaries + after downloading them, you do not have to use HTTPS for the binaries themselves. + +The same binaries are in most cases available on the `Solidity release page on Github`_. The +difference is that we do not generally update old releases on the Github release page. This means +that we do not rename them if the naming convention changes and we do not add builds for platforms +that were not supported at the time of release. This only happens in ``solc-bin``. + +The ``solc-bin`` repository contains several top-level directories, each representing a single platform. +Each one contains a ``list.json`` file listing the available binaries. For example in +``emscripten-wasm32/list.json`` you will find the following information about version 0.7.4: + +.. code-block:: json + + { + "path": "solc-emscripten-wasm32-v0.7.4+commit.3f05b770.js", + "version": "0.7.4", + "build": "commit.3f05b770", + "longVersion": "0.7.4+commit.3f05b770", + "keccak256": "0x300330ecd127756b824aa13e843cb1f43c473cb22eaf3750d5fb9c99279af8c3", + "urls": [ + "bzzr://16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1", + "dweb:/ipfs/QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS" + ] + } + +This means that: + +- You can find the binary in the same directory under the name + `solc-emscripten-wasm32-v0.7.4+commit.3f05b770.js `_. + Note that the file might be a symlink, and you will need to resolve it yourself if you are not using + git to download it or your file system does not support symlinks. +- The binary is also mirrored at https://binaries.soliditylang.org/emscripten-wasm32/solc-emscripten-wasm32-v0.7.4+commit.3f05b770.js. + In this case git is not necessary and symlinks are resolved transparently, either by serving a copy + of the file or returning a HTTP redirect. +- The file is also available on IPFS at `QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS`_. +- The file might in future be available on Swarm at `16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1`_. +- You can verify the integrity of the binary by comparing its keccak256 hash to + ``0x300330ecd127756b824aa13e843cb1f43c473cb22eaf3750d5fb9c99279af8c3``. The hash can be computed + on the command line using ``keccak256sum`` utility provided by `sha3sum`_ or `keccak256() function + from ethereumjs-util`_ in JavaScript. - emerge dev-lang/solidity +.. warning:: + + Due to the strong backwards compatibility requirement the repository contains some legacy elements + but you should avoid using them when writing new tools: + + - Use ``emscripten-wasm32/`` (with a fallback to ``emscripten-asmjs/``) instead of ``bin/`` if + you want the best performance. Until version 0.6.1 we only provided asm.js binaries. + Starting with 0.6.2 we switched to `WebAssembly builds`_ with much better performance. We have + rebuilt the older versions for wasm but the original asm.js files remain in ``bin/``. + The new ones had to be placed in a separate directory to avoid name clashes. + - Use ``emscripten-asmjs/`` and ``emscripten-wasm32/`` instead of ``bin/`` and ``wasm/`` directories + if you want to be sure whether you are downloading a wasm or an asm.js binary. + - Use ``list.json`` instead of ``list.js`` and ``list.txt``. The JSON list format contains all + the information from the old ones and more. + - Use https://binaries.soliditylang.org instead of https://solc-bin.ethereum.org. To keep things + simple we moved almost everything related to the compiler under the new ``soliditylang.org`` + domain and this applies to ``solc-bin`` too. While the new domain is recommended, the old one + is still fully supported and guaranteed to point at the same location. + +.. warning:: + + The binaries are also available at https://ethereum.github.io/solc-bin/ but this page + stopped being updated just after the release of version 0.7.2, will not receive any new releases + or nightly builds for any platform and does not serve the new directory structure, including + non-emscripten builds. + + If you are using it, please switch to https://binaries.soliditylang.org, which is a drop-in + replacement. This allows us to make changes to the underlying hosting in a transparent way and + minimize disruption. Unlike the ``ethereum.github.io`` domain, which we do not have any control + over, ``binaries.soliditylang.org`` is guaranteed to work and maintain the same URL structure + in the long-term. + +.. _IPFS: https://ipfs.io +.. _Swarm: https://swarm-gateways.net/bzz:/swarm.eth +.. _solc-bin: https://github.com/ethereum/solc-bin/ +.. _Solidity release page on github: https://github.com/ethereum/solidity/releases +.. _sha3sum: https://github.com/maandree/sha3sum +.. _keccak256() function from ethereumjs-util: https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/modules/_hash_.md#const-keccak256 +.. _WebAssembly builds: https://emscripten.org/docs/compiling/WebAssembly.html +.. _QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS: https://gateway.ipfs.io/ipfs/QmTLs5MuLEWXQkths41HiACoXDiH8zxyqBHGFDRSzVE5CS +.. _16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1: https://swarm-gateways.net/bzz:/16c5f09109c793db99fe35f037c6092b061bd39260ee7a677c8a97f18c955ab1/ .. _building-from-source: @@ -194,18 +306,18 @@ The following are dependencies for all builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | Software | Notes | +===================================+=======================================================+ -| `CMake`_ (version 3.9+) | Cross-platform build file generator. | +| `CMake`_ (version 3.13+) | Cross-platform build file generator. | +-----------------------------------+-------------------------------------------------------+ | `Boost`_ (version 1.65+) | C++ libraries. | +-----------------------------------+-------------------------------------------------------+ | `Git`_ | Command-line tool for retrieving source code. | +-----------------------------------+-------------------------------------------------------+ -| `z3`_ (version 4.6+, Optional) | For use with SMT checker. | +| `z3`_ (version 4.8+, Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ | `cvc4`_ (Optional) | For use with SMT checker. | +-----------------------------------+-------------------------------------------------------+ -.. _cvc4: http://cvc4.cs.stanford.edu/web/ +.. _cvc4: https://cvc4.cs.stanford.edu/web/ .. _Git: https://git-scm.com/download .. _Boost: https://www.boost.org .. _CMake: https://cmake.org/download/ @@ -223,9 +335,9 @@ Minimum compiler versions The following C++ compilers and their minimum versions can build the Solidity codebase: -- `GCC `_, version 5+ -- `Clang `_, version 3.4+ -- `MSVC `_, version 2017+ +- `GCC `_, version 8+ +- `Clang `_, version 7+ +- `MSVC `_, version 2019+ Prerequisites - macOS --------------------- @@ -234,7 +346,7 @@ For macOS builds, ensure that you have the latest version of `Xcode installed `_. This contains the `Clang C++ compiler `_, the `Xcode IDE `_ and other Apple development -tools which are required for building C++ applications on OS X. +tools that are required for building C++ applications on OS X. If you are installing Xcode for the first time, or have just installed a new version then you will need to agree to the license before you can do command-line builds: @@ -243,10 +355,10 @@ command-line builds: sudo xcodebuild -license accept -Our OS X build script uses `the Homebrew `_ +Our OS X build script uses `the Homebrew `_ package manager for installing external dependencies. Here's how to `uninstall Homebrew -`_, +`_, if you ever want to start again from scratch. Prerequisites - Windows @@ -257,29 +369,29 @@ You need to install the following dependencies for Windows builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | Software | Notes | +===================================+=======================================================+ -| `Visual Studio 2017 Build Tools`_ | C++ compiler | +| `Visual Studio 2019 Build Tools`_ | C++ compiler | +-----------------------------------+-------------------------------------------------------+ -| `Visual Studio 2017`_ (Optional) | C++ compiler and dev environment. | +| `Visual Studio 2019`_ (Optional) | C++ compiler and dev environment. | +-----------------------------------+-------------------------------------------------------+ If you already have one IDE and only need the compiler and libraries, -you could install Visual Studio 2017 Build Tools. +you could install Visual Studio 2019 Build Tools. -Visual Studio 2017 provides both IDE and necessary compiler and libraries. -So if you have not got an IDE and prefer to develop solidity, Visual Studio 2017 +Visual Studio 2019 provides both IDE and necessary compiler and libraries. +So if you have not got an IDE and prefer to develop Solidity, Visual Studio 2019 may be a choice for you to get everything setup easily. Here is the list of components that should be installed -in Visual Studio 2017 Build Tools or Visual Studio 2017: +in Visual Studio 2019 Build Tools or Visual Studio 2019: * Visual Studio C++ core features -* VC++ 2017 v141 toolset (x86,x64) +* VC++ 2019 v141 toolset (x86,x64) * Windows Universal CRT SDK * Windows 8.1 SDK * C++/CLI support -.. _Visual Studio 2017: https://www.visualstudio.com/vs/ -.. _Visual Studio 2017 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017 +.. _Visual Studio 2019: https://www.visualstudio.com/vs/ +.. _Visual Studio 2019 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019 Dependencies Helper Script -------------------------- @@ -295,7 +407,10 @@ Or, on Windows: .. code-block:: bat - scripts\install_deps.bat + scripts\install_deps.ps1 + +Note that the latter command will install ``boost`` and ``cmake`` to the ``deps`` subdirectory, while the former command +will attempt to install the dependencies globally. Clone the Repository -------------------- @@ -330,10 +445,12 @@ Command-Line Build **Be sure to install External Dependencies (see above) before build.** Solidity project uses CMake to configure the build. -You might want to install ccache to speed up repeated builds. +You might want to install `ccache`_ to speed up repeated builds. CMake will pick it up automatically. Building Solidity is quite similar on Linux, macOS and other Unices: +.. _ccache: https://ccache.dev/ + .. code-block:: bash mkdir build @@ -357,11 +474,14 @@ And for Windows: mkdir build cd build - cmake -G "Visual Studio 15 2017 Win64" .. + cmake -G "Visual Studio 16 2019 Win64" .. + +In case you want to use the version of boost installed by ``./scripts/install_deps.ps1``, you will +additionally need to pass ``-DBoost_DIR="..\deps\boost\lib\cmake\Boost-*"`` and ``-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded`` +as arguments to the call to ``cmake``. -This latter set of instructions should result in the creation of -**solidity.sln** in that build directory. Double-clicking on that file -should result in Visual Studio firing up. We suggest building +This should result in the creation of **solidity.sln** in that build directory. +Double-clicking on that file should result in Visual Studio firing up. We suggest building **Release** configuration, but all others work. Alternatively, you can build for Windows on the command-line, like so: @@ -370,7 +490,7 @@ Alternatively, you can build for Windows on the command-line, like so: cmake --build . --config Release -CMake options +CMake Options ============= If you are interested what CMake options are available run ``cmake .. -LH``. @@ -398,7 +518,7 @@ Inside the build folder you can disable them, since they are enabled by default: # disables both Z3 and CVC4 cmake .. -DUSE_CVC4=OFF -DUSE_Z3=OFF -The version string in detail +The Version String in Detail ============================ The Solidity version string contains four parts: @@ -417,7 +537,7 @@ A release example: ``0.4.8+commit.60cc1668.Emscripten.clang``. A pre-release example: ``0.4.9-nightly.2017.1.17+commit.6ecb4aa3.Emscripten.clang`` -Important information about versioning +Important Information About Versioning ====================================== After a release is made, the patch version level is bumped, because we assume that only diff --git a/docs/internals/layout_in_calldata.rst b/docs/internals/layout_in_calldata.rst index cfabaf99fa0d..aef27b1531c3 100644 --- a/docs/internals/layout_in_calldata.rst +++ b/docs/internals/layout_in_calldata.rst @@ -1,3 +1,6 @@ + +.. index: calldata layout + ******************* Layout of Call Data ******************* diff --git a/docs/internals/layout_in_memory.rst b/docs/internals/layout_in_memory.rst index 34c3035eb393..bad8c1654693 100644 --- a/docs/internals/layout_in_memory.rst +++ b/docs/internals/layout_in_memory.rst @@ -36,4 +36,37 @@ elements. definitely zeroed out memory area, using such a pointer non-temporarily without updating the free memory pointer can have unexpected results. -.. index: calldata layout \ No newline at end of file + +Differences to Layout in Storage +================================ + +As described above the layout in memory is different from the layout in +:ref:`storage`. Below there are some examples. + +Example for Difference in Arrays +-------------------------------- + +The following array occupies 32 bytes (1 slot) in storage, but 128 +bytes (4 items with 32 bytes each) in memory. + +:: + + uint8[4] a; + + + +Example for Difference in Struct Layout +--------------------------------------- + +The following struct occupies 96 bytes (3 slots of 32 bytes) in storage, +but 128 bytes (4 items with 32 bytes each) in memory. + + +:: + + struct S { + uint a; + uint b; + uint8 c; + uint8 d; + } diff --git a/docs/internals/layout_in_storage.rst b/docs/internals/layout_in_storage.rst index d43858f55af8..ad0530e69049 100644 --- a/docs/internals/layout_in_storage.rst +++ b/docs/internals/layout_in_storage.rst @@ -72,7 +72,7 @@ the position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25 // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract C { @@ -173,7 +173,7 @@ value and reference types, types that are encoded packed, and nested types. .. code:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract A { struct S { uint128 a; diff --git a/docs/internals/source_mappings.rst b/docs/internals/source_mappings.rst index d650031c712c..0ee21553372e 100644 --- a/docs/internals/source_mappings.rst +++ b/docs/internals/source_mappings.rst @@ -21,6 +21,10 @@ Both kinds of source mappings use integer identifiers to refer to source files. The identifier of a source file is stored in ``output['sources'][sourceName]['id']`` where ``output`` is the output of the standard-json compiler interface parsed as JSON. +For some utility routines, the compiler generates "internal" source files +that are not part of the original input but are referenced from the source +mappings. These source files together with their identifiers can be +obtained via ``output['contracts'][sourceName][contractName]['evm']['bytecode']['generatedSources']``. .. note :: In the case of instructions that are not associated with any particular source file, diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 1edb881693bf..a0335e5d0705 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -18,7 +18,7 @@ Storage Example :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract SimpleStorage { uint storedData; @@ -37,7 +37,7 @@ GPL version 3.0. Machine-readable license specifiers are important in a setting where publishing the source code is the default. The next line specifies that the source code is written for -Solidity version 0.4.16, or a newer version of the language up to, but not including version 0.7.0. +Solidity version 0.4.16, or a newer version of the language up to, but not including version 0.8.0. This is to ensure that the contract is not compilable with a new (breaking) compiler version, where it could behave differently. :ref:`Pragmas` are common instructions for compilers about how to treat the source code (e.g. `pragma once `_). @@ -83,7 +83,7 @@ registering with a username and password, all you need is an Ethereum keypair. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.5.99 <0.8.0; + pragma solidity >=0.7.0 <0.9.0; contract Coin { // The keyword "public" makes variables @@ -285,7 +285,7 @@ likely it will be. since it is not up to the submitter of a transaction, but up to the miners to determine in which block the transaction is included. If you want to schedule future calls of your contract, you can use - the `alarm clock `_ or a similar oracle service. + the `alarm clock `_ or a similar oracle service. .. _the-ethereum-virtual-machine: diff --git a/docs/ir/ir-breaking-changes.rst b/docs/ir/ir-breaking-changes.rst new file mode 100644 index 000000000000..13a3e0759bdb --- /dev/null +++ b/docs/ir/ir-breaking-changes.rst @@ -0,0 +1,71 @@ +******************************** +Solidity IR-based Codegen Changes +******************************** + +This section highlights the main differences between the old and the IR-based codegen, +along with the reasoning behind the changes and how to update affected code. + +Semantic Only Changes +===================== + +This section lists the changes that are semantic-only, thus potentially +hiding new and different behavior in existing code. + + * When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched. +Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past). + +:: + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.0; + + contract C { + struct S { + uint64 y; + uint64 z; + } + S s; + function f() public { + // ... + delete s; + // s occupies only first 16 bytes of the 32 bytes slot + // delete will write zero to the full slot + } + } + +We have the same behavior for implicit delete, for example when array of structs is shortened. + + * The order of contract initialization has changed in case of inheritance. + +The order used to be: + - All state variables are zero-initialized at the beginning. + - Evaluate base constructor arguments from most derived to most base contract. + - Initialize all state variables in the whole inheritance hierarchy from most base to most derived. + - Run the constructor, if present, for all contracts in the linearized hierarchy from most base to most derived. + +New order: + - All state variables are zero-initialized at the beginning. + - Evaluate base constructor arguments from most derived to most base contract. + - For every contract in order from most base to most derived in the linearized hierarchy execute: + 1. If present at declaration, initial values are assigned to state variables. + 2. Constructor, if present. + +This causes differences in some contracts, for example: +:: + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >0.7.0; + + contract A { + uint x; + constructor() { + x = 42; + } + function f() public view returns(uint256) { + return x; + } + } + contract B is A { + uint public y = f(); + } + +Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well. +With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42. diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 54cb8c59a887..ea39e8a794d0 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -5,7 +5,8 @@ Layout of a Solidity Source File Source files can contain an arbitrary number of :ref:`contract definitions`, import_ directives, :ref:`pragma directives` and -:ref:`struct` and :ref:`enum` definitions. +:ref:`struct`, :ref:`enum`, :ref:`function` +and :ref:`constant variable` definitions. .. index:: ! license, spdx @@ -14,7 +15,7 @@ SPDX License Identifier Trust in smart contract can be better established if their source code is available. Since making source code available always touches on legal problems -with regards to copyright, the Solidity compiler encouranges the use +with regards to copyright, the Solidity compiler encourages the use of machine-readable `SPDX license identifiers `_. Every source file should start with a comment indicating its license: @@ -22,7 +23,7 @@ Every source file should start with a comment indicating its license: The compiler does not validate that the license is part of the `list allowed by SPDX `_, but -it does include the supplied string in the `bytecode metadata `_. +it does include the supplied string in the :ref:`bytecode metadata `. If you do not want to specify a license or if the source code is not open-source, please use the special value ``UNLICENSED``. @@ -87,6 +88,41 @@ these follow the same syntax used by `npm `_ required by the pragma. If it does not match, the compiler issues an error. +ABI Coder Pragma +---------------- + +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;``. + +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 +that do not without limitations. The reverse is possible only as long as the +non-``abicoder v2`` contract does not try to make calls that would require +decoding types only supported by the new encoder. The compiler can detect this +and will issue an error. Simply enabling ``abicoder v2`` for your contract is +enough to make the error go away. + +.. note:: + This pragma applies to all the code defined in the file where it is activated, + regardless of where that code ends up eventually. This means that a contract + whose source file is selected to compile with ABI coder v1 + can still contain code that uses the new encoder + by inheriting it from another contract. This is allowed if the new types are only + used internally and not in external function signatures. + +.. note:: + Up to Solidity 0.7.4, it was possible to select the ABI coder v2 + by using ``pragma experimental ABIEncoderV2``, but it was not possible + to explicitly select coder v1 because it was the default. + .. index:: ! pragma, experimental .. _experimental_pragma: @@ -102,13 +138,9 @@ The following experimental pragmas are currently supported: ABIEncoderV2 ~~~~~~~~~~~~ -The new ABI encoder 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 experimental ABIEncoderV2;`` - we kept -the same pragma, even though it is not considered experimental -anymore. +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. .. _smt_checker: @@ -317,7 +349,7 @@ for the two function parameters and two return variables. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.21 <0.8.0; + pragma solidity >=0.4.21 <0.9.0; /** @title Shape calculator. */ contract ShapeCalculator { diff --git a/docs/metadata.rst b/docs/metadata.rst index 240814adca53..bd827a0d8b96 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -202,6 +202,6 @@ This automatically verifies the metadata since its hash is part of the bytecode. Excess data corresponds to the constructor input data, which should be decoded according to the interface and presented to the user. -In the repository `source-verify `_ +In the repository `sourcify `_ (`npm package `_) you can see example code that shows how to use this feature. diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index d4777df3132e..0d00711b1590 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -36,7 +36,7 @@ for the purposes of NatSpec. - For Vyper, use ``"""`` indented to the inner contents with bare comments. See `Vyper - documentation `__. + documentation `__. The following example shows a contract and a function using all available tags. @@ -46,10 +46,10 @@ The following example shows a contract and a function using all available tags. public. You are welcome to use similar comments for your internal and private functions, but those will not be parsed. -.. code:: solidity +.. code:: Solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.10 <0.8.0; + pragma solidity >=0.6.12 <0.9.0; /// @title A simulator for trees /// @author Larry A. Gardner @@ -126,7 +126,7 @@ JSON output, for example the end-user client software, may present this to the e For example, some client software will render: -.. code:: solidity +.. code:: Solidity /// @notice This function will multiply `a` by 7 @@ -195,8 +195,8 @@ JSON file as output: } Note that the key by which to find the methods is the function's -canonical signature as defined in the `Contract -ABI `__ and not simply the function's +canonical signature as defined in the :ref:`Contract +ABI ` and not simply the function's name. .. _header-developer-doc: diff --git a/docs/requirements.txt b/docs/requirements.txt index 8f67f9594fa0..65cc192fdb01 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ sphinx_rtd_theme>=0.3.1 pygments-lexer-solidity>=0.5.1 +sphinx-a4doc>=1.2.1 diff --git a/docs/resources.rst b/docs/resources.rst index 02c1930aace1..01c4715a18fc 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -75,25 +75,14 @@ Solidity Integrations * `Vim Solidity `_ Plugin for the Vim editor providing syntax highlighting. - * `Vim Syntastic `_ + * `Vim Syntastic `_ Plugin for the Vim editor providing compile checking. * Visual Studio Code: - * `Visual Studio Code extension `_ + * `Visual Studio Code extension `_ Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. -Discontinued: - -* `Mix IDE `_ - Qt based IDE for designing, debugging and testing solidity smart contracts. - -* `Ethereum Studio `_ - Specialized web IDE that also provides shell access to a complete Ethereum environment. - -* `Visual Studio Extension `_ - Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. - Solidity Tools ~~~~~~~~~~~~~~ @@ -142,8 +131,5 @@ Solidity Tools Third-Party Solidity Parsers and Grammars ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* `solidity-parser `_ - Solidity parser for JavaScript - -* `Solidity Grammar for ANTLR 4 `_ - Solidity grammar for the ANTLR 4 parser generator +* `Solidity Parser for JavaScript `_ + A Solidity parser for JS built on top of a robust ANTLR4 grammar. diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 9a1cbc970221..4fcd42804ef2 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -59,7 +59,7 @@ complete contract): :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -67,7 +67,7 @@ complete contract): mapping(address => uint) shares; /// Withdraw your share. function withdraw() public { - if (msg.sender.send(shares[msg.sender])) + if (payable(msg.sender).send(shares[msg.sender])) shares[msg.sender] = 0; } } @@ -83,7 +83,7 @@ as it uses ``call`` which forwards all remaining gas by default: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.2 <0.8.0; + pragma solidity >=0.6.2 <0.9.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -103,7 +103,7 @@ outlined further below: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.11 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract Fund { /// @dev Mapping of trx shares of the contract. @@ -112,7 +112,7 @@ outlined further below: function withdraw() public { uint share = shares[msg.sender]; shares[msg.sender] = 0; - msg.sender.transfer(share); + payable(msg.sender).transfer(share); } } @@ -201,8 +201,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract TxUserWallet { address owner; @@ -222,8 +221,7 @@ Now someone tricks you into sending Trx to the address of this attack wallet: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; interface TxUserWallet { function transferTo(address payable dest, uint amount) external; } @@ -232,7 +230,7 @@ Now someone tricks you into sending Trx to the address of this attack wallet: address payable owner; constructor() { - owner = msg.sender; + owner = payable(msg.sender); } receive() external payable { @@ -248,21 +246,32 @@ Two's Complement / Underflows / Overflows ========================================= As in many programming languages, Solidity's integer types are not actually integers. -They resemble integers when the values are small, but behave differently if the numbers are larger. -For example, the following is true: ``uint8(255) + uint8(1) == 0``. This situation is called -an *overflow*. It occurs when an operation is performed that requires a fixed size variable -to store a number (or piece of data) that is outside the range of the variable's data type. -An *underflow* is the converse situation: ``uint8(0) - uint8(1) == 255``. +They resemble integers when the values are small, but cannot represent arbitrarily large numbers. + +The following code causes an overflow because the result of the addition is too large +to be stored in the type ``uint8``: + +:: + + uint8 x = 255; + uint8 y = 1; + return x + y; + +Solidity has two modes in which it deals with these overflows: Checked and Unchecked or "wrapping" mode. + +The default checked mode will detect overflows and cause a failing assertion. You can disable this check +using ``unchecked { ... }``, causing the overflow to be silently ignored. The above code would return +``0`` if wrapped in ``unchecked { ... }``. + +Even in checked mode, do not assume you are protected from overflow bugs. +In this mode, overflows will always revert. If it is not possible to avoid the +overflow, this can lead to a smart contract being stuck in a certain state. In general, read about the limits of two's complement representation, which even has some more special edge cases for signed numbers. Try to use ``require`` to limit the size of inputs to a reasonable range and use the -:ref:`SMT checker` to find potential overflows, or use a library like -`SafeMath `_ -if you want all overflows to cause a revert. - -Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect. +:ref:`SMT checker` to find potential overflows. .. _clearing-mappings: @@ -283,7 +292,7 @@ field of a ``struct`` that is the base type of a dynamic storage array. The :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract Map { mapping (uint => uint)[] array; @@ -448,19 +457,21 @@ The SMT encoding tries to be as precise as possible, mapping Solidity types and expressions to their closest `SMT-LIB `_ representation, as shown in the table below. -+-----------------------+--------------+-----------------------------+ -|Solidity type |SMT sort |Theories (quantifier-free) | -+=======================+==============+=============================+ -|Boolean |Bool |Bool | -+-----------------------+--------------+-----------------------------+ -|intN, uintN, address, |Integer |LIA, NIA | -|bytesN, enum | | | -+-----------------------+--------------+-----------------------------+ -|array, mapping, bytes, |Array |Arrays | -|string | | | -+-----------------------+--------------+-----------------------------+ -|other types |Integer |LIA | -+-----------------------+--------------+-----------------------------+ ++-----------------------+--------------------------------+-----------------------------+ +|Solidity type |SMT sort |Theories (quantifier-free) | ++=======================+================================+=============================+ +|Boolean |Bool |Bool | ++-----------------------+--------------------------------+-----------------------------+ +|intN, uintN, address, |Integer |LIA, NIA | +|bytesN, enum | | | ++-----------------------+--------------------------------+-----------------------------+ +|array, mapping, bytes, |Tuple |Datatypes, Arrays, LIA | +|string |(Array elements, Integer length)| | ++-----------------------+--------------------------------+-----------------------------+ +|struct |Tuple |Datatypes | ++-----------------------+--------------------------------+-----------------------------+ +|other types |Integer |LIA | ++-----------------------+--------------------------------+-----------------------------+ Types that are not yet supported are abstracted by a single 256-bit unsigned integer, where their unsupported operations are ignored. @@ -618,26 +629,32 @@ types. // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.5.0; + pragma experimental ABIEncoderV2; pragma experimental SMTChecker; // This will report a warning contract Aliasing { - uint[] array; + uint[] array1; + uint[][] array2; function f( uint[] memory a, uint[] memory b, uint[][] memory c, uint[] storage d - ) internal view { - require(array[0] == 42); - require(a[0] == 2); - require(c[0][0] == 2); - require(d[0] == 2); + ) internal { + array1[0] = 42; + a[0] = 2; + c[0][0] = 2; b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. - assert(array[0] == 42); + assert(array1[0] == 42); + // However, an assignment to a storage reference will erase + // storage knowledge accordingly. + d[0] = 2; + // Fails as false positive because of the assignment above. + assert(array1[0] == 42); // Fails because `a == b` is possible. assert(a[0] == 2); // Fails because `c[i] == b` is possible. @@ -645,6 +662,14 @@ types. assert(d[0] == 2); assert(b[0] == 1); } + function g( + uint[] memory a, + uint[] memory b, + uint[][] memory c, + uint x + ) public { + f(a, b, c, array2[x]); + } } After the assignment to ``b[0]``, we need to clear knowledge about ``a`` since diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 01613073054e..e7af9a811f55 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -27,7 +27,7 @@ storage. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract SimpleStorage { uint storedData; // State variable @@ -43,12 +43,14 @@ visibility. Functions ========= -Functions are the executable units of code within a contract. +Functions are the executable units of code. Functions are usually +defined inside a contract, but they can also be defined outside of +contracts. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >0.7.0 <0.9.0; contract SimpleAuction { function bid() public payable { // Function @@ -56,6 +58,11 @@ Functions are the executable units of code within a contract. } } + // Helper function defined outside of a contract + function helper(uint x) pure returns (uint) { + return x * 2; + } + :ref:`function-calls` can happen internally or externally and have different levels of :ref:`visibility` towards other contracts. :ref:`Functions` accept :ref:`parameters and return variables` to pass parameters @@ -77,7 +84,7 @@ Like functions, modifiers can be :ref:`overridden `. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract Purchase { address public seller; @@ -105,7 +112,7 @@ Events are convenience interfaces with the EVM logging facilities. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.21 <0.8.0; + pragma solidity >=0.4.21 <0.9.0; contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event @@ -130,7 +137,7 @@ Structs are custom defined types that can group several variables (see :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract Ballot { struct Voter { // Struct @@ -152,7 +159,7 @@ Enums can be used to create custom types with a finite set of 'constant values' :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract Purchase { enum State { Created, Locked, Inactive } // Enum diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 30ceec3a98a2..61837cea55ef 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -56,7 +56,7 @@ Surround top level declarations in solidity source with two blank lines. Yes:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract A { // ... @@ -75,7 +75,7 @@ Yes:: No:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract A { // ... @@ -95,7 +95,7 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu Yes:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; abstract contract A { function spam() public virtual pure; @@ -116,7 +116,7 @@ Yes:: No:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; abstract contract A { function spam() virtual pure public; @@ -251,7 +251,7 @@ Import statements should always be placed at the top of the file. Yes:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; import "./Owned.sol"; @@ -266,7 +266,7 @@ Yes:: No:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract A { // ... @@ -300,8 +300,7 @@ Within a grouping, place the ``view`` and ``pure`` functions last. Yes:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract A { constructor() { // ... @@ -337,8 +336,7 @@ Yes:: No:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract A { // External functions @@ -445,7 +443,7 @@ should: Yes:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract Coin { struct Bank { @@ -457,7 +455,7 @@ Yes:: No:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract Coin { @@ -560,7 +558,7 @@ Yes:: return x + 1; } - function increment(uint x) public pure onlyowner returns (uint) { + function increment(uint x) public pure onlyOwner returns (uint) { return x + 1; } @@ -596,7 +594,7 @@ Yes:: return balanceOf[from]; } - function shutdown() public onlyowner { + function shutdown() public onlyOwner { selfdestruct(owner); } @@ -606,7 +604,7 @@ No:: return balanceOf[from]; } - function shutdown() onlyowner public { + function shutdown() onlyOwner public { selfdestruct(owner); } @@ -663,7 +661,7 @@ Yes:: function thisFunctionNameIsReallyLong(address x, address y, address z) public - onlyowner + onlyOwner priced returns (address) { @@ -676,7 +674,7 @@ Yes:: address z, ) public - onlyowner + onlyOwner priced returns (address) { @@ -687,21 +685,21 @@ No:: function thisFunctionNameIsReallyLong(address x, address y, address z) public - onlyowner + onlyOwner priced returns (address) { doSomething(); } function thisFunctionNameIsReallyLong(address x, address y, address z) - public onlyowner priced returns (address) + public onlyOwner priced returns (address) { doSomething(); } function thisFunctionNameIsReallyLong(address x, address y, address z) public - onlyowner + onlyOwner priced returns (address) { doSomething(); @@ -758,8 +756,7 @@ manner as modifiers if the function declaration is long or hard to read. Yes:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; // Base contracts just to make this compile contract B { constructor(uint) { @@ -790,8 +787,7 @@ Yes:: No:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; // Base contracts just to make this compile contract B { @@ -1012,8 +1008,7 @@ As shown in the example below, if the contract name is ``Congress`` and the libr Yes:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; // Owned.sol contract Owned { @@ -1036,7 +1031,7 @@ Yes:: and in ``Congress.sol``:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; import "./Owned.sol"; @@ -1048,8 +1043,7 @@ and in ``Congress.sol``:: No:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; // owned.sol contract owned { @@ -1071,6 +1065,10 @@ No:: and in ``Congress.sol``:: + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.7.0; + + import "./owned.sol"; @@ -1145,15 +1143,15 @@ NatSpec Solidity contracts can have a form of comments that are the basis of the Ethereum Natural Language Specification Format. -Add comments above functions or contracts following `doxygen `_ notation +Add comments above functions or contracts following `doxygen `_ notation of one or multiple lines starting with ``///`` or a multiline comment starting with ``/**`` and ending with ``*/``. -For example, the contract from `a simple smart contract `_ with the comments +For example, the contract from :ref:`a simple smart contract ` with the comments added looks like the one below:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; /// @author The Solidity Team @@ -1176,6 +1174,6 @@ added looks like the one below:: } } -It is recommended that Solidity contracts are fully annotated using `NatSpec `_ for all public interfaces (everything in the ABI). +It is recommended that Solidity contracts are fully annotated using :ref:`NatSpec ` for all public interfaces (everything in the ABI). -Please see the section about `NatSpec `_ for a detailed explanation. +Please see the section about :ref:`NatSpec ` for a detailed explanation. diff --git a/docs/types/conversion.rst b/docs/types/conversion.rst index 7442a17b57f8..050abb9e6ebd 100644 --- a/docs/types/conversion.rst +++ b/docs/types/conversion.rst @@ -114,6 +114,11 @@ that is large enough to represent it without truncation:: uint32 b = 1234; // fine uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456 +.. note:: + Prior to version 0.8.0, any decimal or hexadecimal number literals could be explicitly + converted to an integer type. From 0.8.0, such explicit conversions are as strict as implicit + conversions, i.e., they are only allowed if the literal fits in the resulting range. + Fixed-Size Byte Arrays ---------------------- diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst index ed27d0508306..edf04aed22ca 100644 --- a/docs/types/mapping-types.rst +++ b/docs/types/mapping-types.rst @@ -43,7 +43,7 @@ contract that returns the value at the specified address. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract MappingExample { mapping(address => uint) public balances; @@ -69,7 +69,7 @@ The example below uses ``_allowances`` to record the amount someone else is allo :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract MappingExample { @@ -124,7 +124,7 @@ the ``sum`` function iterates over to sum all the values. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.8 <0.9.0; struct IndexValue { uint keyIndex; uint value; } struct KeyFlag { uint key; bool deleted; } @@ -165,7 +165,7 @@ the ``sum`` function iterates over to sum all the values. } function iterate_start(itmap storage self) internal view returns (uint keyIndex) { - return iterate_next(self, uint(-1)); + return iterate_next(self, type(uint).max); } function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) { diff --git a/docs/types/operators.rst b/docs/types/operators.rst index bbde72e26454..883f8d4b7635 100644 --- a/docs/types/operators.rst +++ b/docs/types/operators.rst @@ -43,7 +43,7 @@ value it referred to previously. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; contract DeleteExample { uint data; diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 336cf5b2edb3..897539468d1b 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -63,7 +63,7 @@ Data locations are not only relevant for persistency of data, but also for the s :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.8.0; + pragma solidity >=0.5.0 <0.9.0; contract C { // The data location of x is storage. @@ -174,7 +174,7 @@ or create a new memory array and copy every element. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract C { function f(uint len) public pure { @@ -206,7 +206,7 @@ the first element to ``uint``. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract C { function f() public pure { @@ -223,7 +223,7 @@ memory arrays, i.e. the following is not possible: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.0 <0.9.0; // This will not compile. contract C { @@ -243,7 +243,7 @@ individual elements: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.0 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract C { function f() public pure { @@ -290,7 +290,7 @@ Array Members .. note:: To use arrays of arrays in external (instead of public) functions, you need to - activate ABIEncoderV2. + activate ABI coder v2. .. note:: In EVM versions before Byzantium, it was not possible to access @@ -301,7 +301,7 @@ Array Members :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; contract ArrayContract { uint[2**20] m_aLotOfIntegers; @@ -391,7 +391,7 @@ Array Members // Create a dynamic byte array: bytes memory b = new bytes(200); for (uint i = 0; i < b.length; i++) - b[i] = byte(uint8(i)); + b[i] = bytes1(uint8(i)); return b; } } @@ -434,8 +434,7 @@ Array slices are useful to ABI-decode secondary data passed in function paramete :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >0.6.99 <0.8.0; - + pragma solidity >=0.7.0 <0.9.0; contract Proxy { /// @dev Address of the client contract managed by proxy i.e., this contract address client; @@ -478,7 +477,7 @@ shown in the following example: :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.8.0; + pragma solidity >=0.6.0 <0.9.0; // Defines a new type with two fields. // Declaring a struct outside of a contract allows @@ -550,3 +549,8 @@ members of the local variable actually write to the state. Of course, you can also directly access the members of the struct without assigning it to a local variable, as in ``campaigns[campaignID].amount = 0``. + +.. note:: + Until Solidity 0.7.0, memory-structs containing members of storage-only types (e.g. mappings) + were allowed and assignments like ``campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)`` + in the example above would work and just silently skip those members. \ No newline at end of file diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 7c149784e645..5e88058c3256 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -38,7 +38,7 @@ Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) * Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) * Shift operators: ``<<`` (left shift), ``>>`` (right shift) -* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) +* Arithmetic operators: ``+``, ``-``, unary ``-`` (only for signed integers), ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) For an integer type ``X``, you can use ``type(X).min`` and ``type(X).max`` to access the minimum and maximum value representable by the type. @@ -46,8 +46,10 @@ access the minimum and maximum value representable by the type. .. warning:: Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``. - If the result of some operation on those numbers does not fit inside this range, it is truncated. These truncations can have - serious consequences that you should :ref:`be aware of and mitigate against`. + There are two modes in which arithmetic is performed on these types: The "wrapping" or "unchecked" mode and the "checked" mode. + By default, arithmetic is always "checked", which mean that if the result of an operation falls outside the value range + of the type, the call is reverted through a :ref:`failing assertion`. You can switch to "unchecked" mode + using ``unchecked { ... }``. More details can be found in the section about :ref:`unchecked `. Comparisons ^^^^^^^^^^^ @@ -77,23 +79,22 @@ Right operand must be unsigned type. Trying to shift by signed type will produce Addition, Subtraction and Multiplication ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Addition, subtraction and multiplication have the usual semantics. -They wrap in two's complement representation, meaning that -for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these overflows -into account when designing safe smart contracts. +Addition, subtraction and multiplication have the usual semantics, with two different +modes in regard to over- and underflow: + +By default, all arithmetic is checked for under- or overflow, but this can be disabled +using the :ref:`unchecked block`, resulting in wrapping arithmetic. More details +can be found in that section. The expression ``-x`` is equivalent to ``(T(0) - x)`` where -``T`` is the type of ``x``. This means that ``-x`` will not be negative -if the type of ``x`` is an unsigned integer type. Also, ``-x`` can be +``T`` is the type of ``x``. It can only be applied to signed types. +The value of ``-x`` can be positive if ``x`` is negative. There is another caveat also resulting -from two's complement representation:: - - int x = -2**255; - assert(-x == x); - -This means that even if a number is negative, you cannot assume that -its negation will be positive. +from two's complement representation: +If you have ``int x = type(int).min;``, then ``-x`` does not fit the positive range. +This means that ``unchecked { assert(-x == x); }`` works, and the expression ``-x`` +when used in checked mode will result in a failing assertion. Division ^^^^^^^^ @@ -106,7 +107,12 @@ Note that in contrast, division on :ref:`literals` results in of arbitrary precision. .. note:: - Division by zero causes a failing assert. + Division by zero causes a :ref:`Panic error`. This check can **not** be disabled through ``unchecked { ... }``. + +.. note:: + The expression ``type(int).min / (-1)`` is the only case where division causes an overflow. + In checked arithmetic mode, this will cause a failing assertion, while in wrapping + mode, the value will be ``type(int).min``. Modulo ^^^^^^ @@ -121,14 +127,19 @@ results in the same sign as its left operand (or zero) and ``a % n == -(-a % n)` * ``int256(-5) % int256(-2) == int256(-1)`` .. note:: - Modulo with zero causes a failing assert. + Modulo with zero causes a :ref:`Panic error`. This check can **not** be disabled through ``unchecked { ... }``. Exponentiation ^^^^^^^^^^^^^^ Exponentiation is only available for unsigned types in the exponent. The resulting type of an exponentiation is always equal to the type of the base. Please take care that it is -large enough to hold the result and prepare for potential wrapping behaviour. +large enough to hold the result and prepare for potential assertion failures or wrapping behaviour. + +.. note:: + In checked mode, exponentiation only uses the comparatively cheap ``exp`` opcode for small bases. + For the cases of ``x**3``, the expression ``x*x*x`` might be cheaper. + In any case, gas cost tests and the use of the optimizer are advisable. .. note:: Note that ``0**0`` is defined by the EVM as ``1``. @@ -177,25 +188,19 @@ Type conversions: Implicit conversions from ``address payable`` to ``address`` are allowed, whereas conversions from ``address`` to ``address payable`` must be explicit via ``payable(
)``. -:ref:`Address literals` can be implicitly converted to ``address payable``. - -Explicit conversions to and from ``address`` are allowed for integers, integer literals, ``bytes20`` and contract types with the following -caveat: -The result of a conversion of the form ``address(x)`` -has the type ``address payable``, if ``x`` is of integer or fixed bytes type, -a literal or a contract with a receive or payable fallback function. -If ``x`` is a contract without a receive or payable fallback function, -then ``address(x)`` will be of type ``address``. -In external function signatures ``address`` is used for both the ``address`` and the ``address payable`` type. +Explicit conversions to and from ``address`` are allowed for ``uint160``, integer literals, +``bytes20`` and contract types. -Only expressions of type ``address`` can be converted to type ``address payable`` via ``payable(
)``. +Only expressions of type ``address`` and contract-type can be converted to the type ``address +payable`` via the explicit conversion ``payable(...)``. For contract-type, this conversion is only +allowed if the contract can receive Ether, i.e., the contract either has a :ref:`receive +` or a payable fallback function. Note that ``payable(0)`` is valid and is +an exception to this rule. .. note:: - It might very well be that you do not need to care about the distinction between ``address`` - and ``address payable`` and just use ``address`` everywhere. For example, - if you are using the :ref:`withdrawal pattern`, you can (and should) store the - address itself as ``address``, because you invoke the ``transfer`` function on - ``msg.sender``, which is an ``address payable``. + If you need a variable of type ``address`` and plan to send Ether to it, then + declare its type as ``address payable`` to make this requirement visible. Also, + try to make this distinction or conversion as early as possible. Operators: @@ -361,7 +366,6 @@ Fixed-size byte arrays The value types ``bytes1``, ``bytes2``, ``bytes3``, ..., ``bytes32`` hold a sequence of bytes from one to up to 32. -``byte`` is an alias for ``bytes1``. Operators: @@ -383,6 +387,9 @@ Members: 31 bytes of space for each element (except in storage). It is better to use the ``bytes`` type instead. +.. note:: + Prior to version 0.8.0, ``byte`` used to be an alias for ``bytes1``. + Dynamically-sized byte array ---------------------------- @@ -399,7 +406,7 @@ Address Literals ---------------- Hexadecimal literals that pass the address checksum test, for example -``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address payable`` type. +``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address`` type. Hexadecimal literals that are between 39 and 41 digits long and do not pass the checksum test produce an error. You can prepend (for integer types) or append (for bytesNN types) zeros to remove the error. @@ -551,8 +558,10 @@ Enums Enums are one way to create a user-defined type in Solidity. They are explicitly convertible to and from all integer types but implicit conversion is not allowed. The explicit conversion -from integer checks at runtime that the value lies inside the range of the enum and causes a failing assert otherwise. +from integer checks at runtime that the value lies inside the range of the enum and causes a +:ref:`Panic error` otherwise. Enums require at least one member, and its default value when declared is the first member. +Enums cannot have more than 256 members. The data representation is the same as for enums in C: The options are represented by subsequent unsigned integer values starting from ``0``. @@ -561,7 +570,7 @@ subsequent unsigned integer values starting from ``0``. :: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } @@ -574,9 +583,7 @@ subsequent unsigned integer values starting from ``0``. // Since enum types are not part of the ABI, the signature of "getChoice" // will automatically be changed to "getChoice() returns (uint8)" - // for all matters external to Solidity. The integer type used is just - // large enough to hold all enum values, i.e. if you have more than 256 values, - // `uint16` will be used and so on. + // for all matters external to Solidity. function getChoice() public view returns (ActionChoices) { return choice; } @@ -645,7 +652,7 @@ On the other hand, a ``non-payable`` function will reject Ether sent to it, so ``non-payable`` functions cannot be converted to ``payable`` functions. If a function type variable is not initialised, calling it results -in a failed assertion. The same happens if you call a function after using ``delete`` +in a :ref:`Panic error`. The same happens if you call a function after using ``delete`` on it. If external function types are used outside of the context of Solidity, @@ -674,7 +681,7 @@ External (or public) functions have the following members: Example that shows how to use the members:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.4 <0.8.0; + pragma solidity >=0.6.4 <0.9.0; contract Example { function f() public payable returns (bytes4) { @@ -690,7 +697,7 @@ Example that shows how to use the members:: Example that shows how to use internal function types:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.8.0; + pragma solidity >=0.4.16 <0.9.0; library ArrayUtils { // internal functions can be used in internal library functions because @@ -748,7 +755,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.22 <0.8.0; + pragma solidity >=0.4.22 <0.9.0; contract Oracle { @@ -773,7 +780,7 @@ Another example that uses external function types:: contract OracleUser { - Oracle constant private ORACLE_CONST = Oracle(0x1234567); // known contract + Oracle constant private ORACLE_CONST = Oracle(address(0x00000000219ab540356cBB839Cbe05303d7705Fa)); // known contract uint private exchangeRate; function buySomething() public { diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 37df176a04ad..425711f229e4 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -71,6 +71,7 @@ Block and Transaction Properties -------------------------------- - ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks +- ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -146,7 +147,7 @@ See the dedicated section on :ref:`assert and require` for more details on error handling and when to use which function. ``assert(bool condition)`` - causes an invalid opcode and thus state change reversion if the condition is not met - to be used for internal errors. + causes a Panic error and thus state change reversion if the condition is not met - to be used for internal errors. ``require(bool condition)`` reverts if the condition is not met - to be used for errors in inputs or external components. @@ -162,6 +163,8 @@ more details on error handling and when to use which function. .. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, +.. _mathematical-and-cryptographic-functions: + Mathematical and Cryptographic Functions ---------------------------------------- @@ -195,23 +198,23 @@ Mathematical and Cryptographic Functions ``ecrecover`` returns an ``address``, and not an ``address payable``. See :ref:`address payable
` for conversion, in case you need to transfer funds to the recovered address. - For further details, read `example usage `_. + For further details, read `example usage `_. .. warning:: If you use ``ecrecover``, be aware that a valid signature can be turned into a different valid signature without requiring knowledge of the corresponding private key. In the Homestead hard fork, this issue was fixed - for _transaction_ signatures (see `EIP-2 `_), but + for _transaction_ signatures (see `EIP-2 `_), but the ecrecover function remained unchanged. This is usually not a problem unless you require signatures to be unique or - use them to identify items. OpenZeppelin have a `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. + use them to identify items. OpenZeppelin have a `ECDSA helper library `_ that you can use as a wrapper for ``ecrecover`` without this issue. .. note:: When running ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*, you might encounter Out-of-Gas. This is because these functions are implemented as "precompiled contracts" and only really exist after they receive the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution might run into an Out-of-Gas error. A workaround for this problem is to first send Wei (1 for example) to each of the contracts before you use them in your actual contracts. This is not an issue on the main or test net. -.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall +.. index:: balance, codehash, send, transfer, call, callcode, delegatecall, staticcall .. _address_related: @@ -221,6 +224,12 @@ Members of Address Types ``
.balance`` (``uint256``) balance of the :ref:`address` in sun +``
.code`` (``bytes memory``) + code at the :ref:`address` (can be empty) + +``
.codehash`` (``bytes32``) + the codehash of the :ref:`address` + ``
.transfer(uint256 amount)`` send given amount of sun to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 9ef1758c1219..89435da3560e 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -12,10 +12,16 @@ Using the Commandline Compiler .. note:: This section does not apply to :ref:`solcjs `, not even if it is used in commandline mode. +Basic usage +----------- + One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast-json --asm sourceFile.sol``. +Optimizer options +----------------- + Before you deploy your contract, activate the optimizer when compiling using ``solc --optimize --bin sourceFile.sol``. By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime (more specifically, it assumes each opcode is executed around 200 times). @@ -27,6 +33,9 @@ This parameter has effects on the following (this might change in the future): - the size of the binary search in the function dispatch routine - the way constants like large numbers or strings are stored +Path remapping +-------------- + The commandline compiler will automatically read imported files from the filesystem, but it is also possible to provide path redirects using ``prefix=path`` in the following way: @@ -53,6 +62,11 @@ For security reasons the compiler has restrictions what directories it can acces Everything inside the path specified via ``--base-path`` is always allowed. +.. _library-linking: + +Library linking +--------------- + If your contracts use :ref:`libraries `, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__``. These are placeholders for the actual library addresses. The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name. The bytecode file will also contain lines of the form ``// -> `` at the end to help @@ -60,13 +74,23 @@ identify which libraries the placeholders represent. Note that the fully qualifi is the path of its source file and the library name separated by ``:``. You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points: -Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. - -If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. +Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library (use commas or spaces as separators) or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. The process will always terminate in a "success" state and report any errors via the JSON output. The option ``--base-path`` is also processed in standard-json mode. +If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. + +.. warning:: + Manually linking libraries on the generated bytecode is discouraged because it does not update + contract metadata. Since metadata contains a list of libraries specified at the time of + compilation and bytecode contains a metadata hash, you will get different binaries, depending + on when linking is performed. + + You should ask the compiler to link the libraries at the time a contract is compiled by either + using the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use the + standard-JSON interface to the compiler. + .. note:: The library placeholder used to be the fully qualified name of the library itself instead of the hash of it. This format is still supported by ``solc --link`` but @@ -200,6 +224,8 @@ Input Description // Optional "settings": { + // Optional: Stop compilation after the given stage. Currently only "parsing" is valid here + "stopAfter": "parsing", // Optional: Sorted list of remappings "remappings": [ ":g=/dir" ], // Optional: Optimizer settings @@ -229,7 +255,7 @@ Input Description "cse": false, // Optimize representation of literal numbers and strings in code. "constantOptimizer": false, - // The new Yul optimizer. Mostly operates on the code of ABIEncoderV2 + // The new Yul optimizer. Mostly operates on the code of ABI coder v2 // and inline assembly. // It is activated together with the global optimizer setting // and can be deactivated here. @@ -250,6 +276,9 @@ Input Description // Affects type checking and code generation. Can be homestead, // tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul or berlin "evmVersion": "byzantium", + // Optional: Change compilation pipeline to go through the Yul intermediate representation. + // This is a highly EXPERIMENTAL feature, not to be used for production. This is false by default. + "viaIR": true, // Optional: Debugging settings "debug": { // How to treat revert (and require) reason strings. Settings are @@ -298,7 +327,6 @@ Input Description // // File level (needs empty string as contract name): // ast - AST of all source files - // legacyAST - legacy AST of all source files // // Contract level (needs the contract name or "*"): // abi - ABI @@ -314,12 +342,13 @@ Input Description // evm.bytecode.opcodes - Opcodes list // evm.bytecode.sourceMap - Source mapping (useful for debugging) // evm.bytecode.linkReferences - Link references (if unlinked object) + // evm.bytecode.generatedSources - Sources generated by the compiler // evm.deployedBytecode* - Deployed bytecode (has all the options that evm.bytecode has) // evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables // evm.methodIdentifiers - The list of function hashes // evm.gasEstimates - Function gas estimates - // ewasm.wast - eWASM S-expressions format (not supported at the moment) - // ewasm.wasm - eWASM binary format (not supported at the moment) + // ewasm.wast - Ewasm in WebAssembly S-expressions format + // ewasm.wasm - Ewasm in WebAssembly binary format // // Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every // target part of that output. Additionally, `*` can be used as a wildcard to request everything. @@ -338,6 +367,16 @@ Input Description "def": { "MyContract": [ "abi", "evm.bytecode.opcodes" ] } + }, + "modelChecker": + { + // Choose which model checker engine to use: all (default), bmc, chc, none. + "engine": "chc", + // Timeout for each SMT query in milliseconds. + // If this option is not given, the SMTChecker will use a deterministic + // resource limit by default. + // A given timeout of 0 means no resource/time restrictions for any query. + "timeout": 20000 } } } @@ -390,8 +429,6 @@ Output Description "id": 1, // The AST object "ast": {}, - // The legacy AST object - "legacyAST": {} } }, // This contains the contract-level outputs. @@ -401,7 +438,7 @@ Output Description // If the language used has no contract names, this field should equal to an empty string. "ContractName": { // The Ethereum Contract ABI. If empty, it is represented as an empty array. - // See https://solidity.readthedocs.io/en/develop/abi-spec.html + // See https://docs.soliditylang.org/en/develop/abi-spec.html "abi": [], // See the Metadata Output documentation (serialised JSON string) "metadata": "{...}", @@ -427,6 +464,18 @@ Output Description "opcodes": "", // The source mapping as a string. See the source mapping definition. "sourceMap": "", + // Array of sources generated by the compiler. Currently only + // contains a single Yul file. + "generatedSources": [{ + // Yul AST + "ast": { ... } + // Source file in its text form (may contain comments) + "contents":"{ function abi_decode(start, end) -> data { data := calldataload(start) } }", + // Source file ID, used for source references, same "namespace" as the Solidity source files + "id": 2, + "language": "Yul", + "name": "#utility.yul" + }] // If given, this is an unlinked object. "linkReferences": { "libraryFile.sol": { @@ -441,11 +490,11 @@ Output Description }, "deployedBytecode": { ..., // The same layout as above. - "immutableReferences": [ + "immutableReferences": { // There are two references to the immutable with AST ID 3, both 32 bytes long. One is // at bytecode offset 42, the other at bytecode offset 80. "3": [{ "start": 42, "length": 32 }, { "start": 80, "length": 32 }] - ] + } }, // The list of function hashes "methodIdentifiers": { @@ -466,7 +515,7 @@ Output Description } } }, - // eWASM related outputs + // Ewasm related outputs "ewasm": { // S-expressions format "wast": "", @@ -598,8 +647,8 @@ Available upgrade modules +----------------------------+---------+--------------------------------------------------+ Please read :doc:`0.5.0 release notes <050-breaking-changes>`, -:doc:`0.6.0 release notes <060-breaking-changes>` and -:doc:`0.7.0 release notes <070-breaking-changes>` for further details. +:doc:`0.6.0 release notes <060-breaking-changes>`, +:doc:`0.7.0 release notes <070-breaking-changes>` and :doc:`0.8.0 release notes <080-breaking-changes>` for further details. Synopsis ~~~~~~~~ @@ -637,7 +686,7 @@ Example Assume that you have the following contract in ``Source.sol``: -.. code-block:: solidity +.. code-block:: Solidity pragma solidity >=0.6.0 <0.6.4; // This will not compile after 0.7.0 @@ -691,10 +740,10 @@ It is recommended to explicitly specify the upgrade modules by using ``--modules The command above applies all changes as shown below. Please review them carefully (the pragmas will have to be updated manually.) -.. code-block:: solidity +.. code-block:: Solidity - pragma solidity >0.6.99 <0.8.0; // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.7.0 <0.9.0; abstract contract C { // FIXME: remove constructor visibility and make the contract abstract constructor() {} diff --git a/docs/yul.rst b/docs/yul.rst index 6929bb75ca02..bc53998152c6 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -9,7 +9,7 @@ Yul Yul (previously also called JULIA or IULIA) is an intermediate language that can be compiled to bytecode for different backends. -Support for EVM 1.0, EVM 1.5 and eWASM is planned, and it is designed to +Support for EVM 1.0, EVM 1.5 and Ewasm is planned, and it is designed to be a usable common denominator of all three platforms. It can already be used in stand-alone mode and for "inline assembly" inside Solidity @@ -54,7 +54,7 @@ be omitted to help readability. To keep the language simple and flexible, Yul does not have any built-in operations, functions or types in its pure form. These are added together with their semantics when specifying a dialect of Yul, -which allows to specialize Yul to the requirements of different +which allows specializing Yul to the requirements of different target platforms and feature sets. Currently, there is only one specified dialect of Yul. This dialect uses @@ -243,7 +243,7 @@ Since variables are stored on the stack, they do not directly influence memory or storage, but they can be used as pointers to memory or storage locations in the built-in functions ``mstore``, ``mload``, ``sstore`` and ``sload``. -Future dialects migh introduce specific types for such pointers. +Future dialects might introduce specific types for such pointers. When a variable is referenced, its current value is copied. For the EVM, this translates to a ``DUP`` instruction. @@ -476,9 +476,8 @@ which are explained in their own chapter. TypeName = Identifier TypedIdentifierList = Identifier ( ':' TypeName )? ( ',' Identifier ( ':' TypeName )? )* Literal = - (NumberLiteral | StringLiteral | HexLiteral | TrueLiteral | FalseLiteral) ( ':' TypeName )? + (NumberLiteral | StringLiteral | TrueLiteral | FalseLiteral) ( ':' TypeName )? NumberLiteral = HexNumber | DecimalNumber - HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' TrueLiteral = 'true' FalseLiteral = 'false' @@ -527,7 +526,7 @@ The ``leave`` statement can only be used inside a function. Functions cannot be defined anywhere inside for loop init blocks. -Literals cannot be larger than the their type. The largest type defined is 256-bit wide. +Literals cannot be larger than their type. The largest type defined is 256-bit wide. During assignments and function calls, the types of the respective values have to match. There is no implicit type conversion. Type conversion in general can only be achieved @@ -688,8 +687,6 @@ We will use a destructuring notation for the AST nodes. L'[$parami] = vi and L'[$reti] = 0 for all i. Let G'', L'', mode = E(Gn, L', block) G'', Ln, L''[$ret1], ..., L''[$retm] - E(G, L, l: HexLiteral) = G, L, hexString(l), - where hexString decodes l from hex and left-aligns it into 32 bytes E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l), where utf8EncodeLeftAligned performs a utf8 encoding of l and aligns it left into 32 bytes @@ -899,12 +896,11 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a .. note:: The ``call*`` instructions use the ``out`` and ``outsize`` parameters to define an area in memory where - the return data is placed. This area is written to depending on how many bytes the called contract returns. + the return or failure data is placed. This area is written to depending on how many bytes the called contract returns. If it returns more data, only the first ``outsize`` bytes are written. You can access the rest of the data using the ``returndatacopy`` opcode. If it returns less data, then the remaining bytes are not touched at all. You need to use the ``returndatasize`` opcode to check which part of this memory area contains the return data. - The remaining bytes will retain their values as of before the call. If the call fails (it returns ``0``), - nothing is written to that area, but you can still retrieve the failure data using ``returndatacopy``. + The remaining bytes will retain their values as of before the call. In some internal dialects, there are additional functions: @@ -923,12 +919,12 @@ For the EVM, the ``datacopy`` function is equivalent to ``codecopy``. setimmutable, loadimmutable ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The functions ``setimmutable("name", value)`` and ``loadimmutable("name")`` are +The functions ``setimmutable(offset, "name", value)`` and ``loadimmutable("name")`` are used for the immutable mechanism in Solidity and do not nicely map to pure Yul. -The function ``setimmutable`` assumes that the runtime code of a contract -is currently copied to memory at offset zero. The call to ``setimmutable("name", value)`` -will store ``value`` at all points in memory that contain a call to -``loadimmutable("name")``. +The call to ``setimmutable(offset, "name", value)`` assumes that the runtime code of the contract +containing the given named immutable was copied to memory at offset ``offset`` and will write ``value`` to all +positions in memory (relative to ``offset``) that contain the placeholder that was generated for calls +to ``loadimmutable("name")`` in the runtime code. linkersymbol @@ -955,6 +951,26 @@ option. See :ref:`Using the Commandline Compiler ` for details about the Solidity linker. +memoryguard +^^^^^^^^^^^ + +This function is available in the EVM dialect with objects. The caller of +``let ptr := memoryguard(size)`` (where ``size`` has to be a literal number) +promises that they only use memory in either the range ``[0, size)`` or the +unbounded range starting at ``ptr``. + +Since the presence of a ``memoryguard`` call indicates that all memory access +adheres to this restriction, it allows the optimizer to perform additional +optimization steps, for example the stack limit evader, which attempts to move +stack variables that would otherwise be unreachable to memory. + +The Yul optimizer promises to only use the memory range ``[size, ptr)`` for its purposes. +If the optimizer does not need to reserve any memory, it holds that ``ptr == size``. + +``memoryguard`` can be called multiple times, but needs to have the same literal as argument +within one Yul subobject. If at least one ``memoryguard`` call is found in a subobject, +the additional optimiser steps will be run on it. + .. _yul-object: @@ -1011,7 +1027,7 @@ An example Yul Object is shown below: // executing code is the constructor code) size := datasize("runtime") offset := allocate(size) - // This will turn into a memory->memory copy for eWASM and + // This will turn into a memory->memory copy for Ewasm and // a codecopy for EVM datacopy(offset, dataoffset("runtime"), size) return(offset, size) @@ -1114,6 +1130,7 @@ Abbreviation Full name ``L`` ``LoadResolver`` ``M`` ``LoopInvariantCodeMotion`` ``r`` ``RedundantAssignEliminator`` +``R`` ``ReasoningBasedSimplifier`` - highly experimental ``m`` ``Rematerialiser`` ``V`` ``SSAReverser`` ``a`` ``SSATransform`` @@ -1125,6 +1142,10 @@ Abbreviation Full name Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. For this reason the Yul optimizer always applies them before applying any steps supplied by the user. +The ReasoningBasedSimplifier is an optimizer step that is currently not enabled +in the default set of steps. It uses an SMT solver to simplify arithmetic expressions +and boolean conditions. It has not received thorough testing or validation yet and can produce +non-reproducible results, so please use with care! .. _erc20yul: diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index c729a4eb4004..cba12e217548 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -56,14 +56,14 @@ unsigned Assembly::bytesRequired(unsigned subTagSize) const { for (unsigned tagSize = subTagSize; true; ++tagSize) { - unsigned ret = 1; + size_t ret = 1; for (auto const& i: m_data) ret += i.second.size(); for (AssemblyItem const& i: m_items) ret += i.bytesRequired(tagSize); if (util::bytesRequired(ret) <= tagSize) - return ret; + return static_cast(ret); } } @@ -257,7 +257,7 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) break; case PushString: collection.append( - createJsonValue("PUSH tag", sourceIndex, i.location().start, i.location().end, m_strings.at((h256)i.data()))); + createJsonValue("PUSH tag", sourceIndex, i.location().start, i.location().end, m_strings.at(h256(i.data())))); break; case PushTag: if (i.data() == 0) @@ -573,21 +573,21 @@ LinkerObject const& Assembly::assemble() const "Cannot push and assign immutables in the same assembly subroutine." ); - size_t bytesRequiredForCode = bytesRequired(subTagSize); + unsigned bytesRequiredForCode = bytesRequired(static_cast(subTagSize)); m_tagPositionsInBytecode = vector(m_usedTags, numeric_limits::max()); map> tagRef; multimap dataRef; multimap subRef; vector sizeRef; ///< Pointers to code locations where the size of the program is inserted unsigned bytesPerTag = util::bytesRequired(bytesRequiredForCode); - uint8_t tagPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerTag; + uint8_t tagPush = static_cast(pushInstruction(bytesPerTag)); - unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size(); + unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + static_cast(m_auxiliaryData.size()); for (auto const& sub: m_subs) - bytesRequiredIncludingData += sub->assemble().bytecode.size(); + bytesRequiredIncludingData += static_cast(sub->assemble().bytecode.size()); unsigned bytesPerDataRef = util::bytesRequired(bytesRequiredIncludingData); - uint8_t dataRefPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerDataRef; + uint8_t dataRefPush = static_cast(pushInstruction(bytesPerDataRef)); ret.bytecode.reserve(bytesRequiredIncludingData); for (AssemblyItem const& i: m_items) @@ -599,25 +599,25 @@ LinkerObject const& Assembly::assemble() const switch (i.type()) { case Operation: - ret.bytecode.push_back((uint8_t)i.instruction()); + ret.bytecode.push_back(static_cast(i.instruction())); break; case PushString: { - ret.bytecode.push_back((uint8_t)Instruction::PUSH32); + ret.bytecode.push_back(static_cast(Instruction::PUSH32)); unsigned ii = 0; - for (auto j: m_strings.at((h256)i.data())) + for (auto j: m_strings.at(h256(i.data()))) if (++ii > 32) break; else - ret.bytecode.push_back((uint8_t)j); + ret.bytecode.push_back(uint8_t(j)); while (ii++ < 32) ret.bytecode.push_back(0); break; } case Push: { - uint8_t b = max(1, util::bytesRequired(i.data())); - ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b); + unsigned b = max(1, util::bytesRequired(i.data())); + ret.bytecode.push_back(static_cast(pushInstruction(b))); ret.bytecode.resize(ret.bytecode.size() + b); bytesRef byr(&ret.bytecode.back() + 1 - b, b); toBigEndian(i.data(), byr); @@ -632,7 +632,7 @@ LinkerObject const& Assembly::assemble() const } case PushData: ret.bytecode.push_back(dataRefPush); - dataRef.insert(make_pair((h256)i.data(), ret.bytecode.size())); + dataRef.insert(make_pair(h256(i.data()), ret.bytecode.size())); ret.bytecode.resize(ret.bytecode.size() + bytesPerDataRef); break; case PushSub: @@ -646,8 +646,8 @@ LinkerObject const& Assembly::assemble() const assertThrow(i.data() <= numeric_limits::max(), AssemblyException, ""); auto s = subAssemblyById(static_cast(i.data()))->assemble().bytecode.size(); i.setPushedValue(u256(s)); - uint8_t b = max(1, util::bytesRequired(s)); - ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b); + unsigned b = max(1, util::bytesRequired(s)); + ret.bytecode.push_back(static_cast(pushInstruction(b))); ret.bytecode.resize(ret.bytecode.size() + b); bytesRef byr(&ret.bytecode.back() + 1 - b, b); toBigEndian(s, byr); @@ -656,36 +656,48 @@ LinkerObject const& Assembly::assemble() const case PushProgramSize: { ret.bytecode.push_back(dataRefPush); - sizeRef.push_back(ret.bytecode.size()); + sizeRef.push_back(static_cast(ret.bytecode.size())); ret.bytecode.resize(ret.bytecode.size() + bytesPerDataRef); break; } case PushLibraryAddress: - ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); + ret.bytecode.push_back(static_cast(Instruction::PUSH20)); ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data()); ret.bytecode.resize(ret.bytecode.size() + 20); break; case PushImmutable: - ret.bytecode.push_back(uint8_t(Instruction::PUSH32)); + ret.bytecode.push_back(static_cast(Instruction::PUSH32)); ret.immutableReferences[i.data()].first = m_immutables.at(i.data()); ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size()); ret.bytecode.resize(ret.bytecode.size() + 32); break; case AssignImmutable: - for (auto const& offset: immutableReferencesBySub[i.data()].second) + { + auto const& offsets = immutableReferencesBySub[i.data()].second; + for (size_t i = 0; i < offsets.size(); ++i) { - ret.bytecode.push_back(uint8_t(Instruction::DUP1)); + if (i != offsets.size() - 1) + { + ret.bytecode.push_back(uint8_t(Instruction::DUP2)); + ret.bytecode.push_back(uint8_t(Instruction::DUP2)); + } // TODO: should we make use of the constant optimizer methods for pushing the offsets? - bytes offsetBytes = toCompactBigEndian(u256(offset)); - ret.bytecode.push_back(uint8_t(Instruction::PUSH1) - 1 + offsetBytes.size()); + bytes offsetBytes = toCompactBigEndian(u256(offsets[i])); + ret.bytecode.push_back(static_cast(pushInstruction(static_cast(offsetBytes.size())))); ret.bytecode += offsetBytes; + ret.bytecode.push_back(uint8_t(Instruction::ADD)); ret.bytecode.push_back(uint8_t(Instruction::MSTORE)); } + if (offsets.empty()) + { + ret.bytecode.push_back(uint8_t(Instruction::POP)); + ret.bytecode.push_back(uint8_t(Instruction::POP)); + } immutableReferencesBySub.erase(i.data()); - ret.bytecode.push_back(uint8_t(Instruction::POP)); break; + } case PushDeployTimeAddress: - ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); + ret.bytecode.push_back(static_cast(Instruction::PUSH20)); ret.bytecode.resize(ret.bytecode.size() + 20); break; case Tag: @@ -694,7 +706,7 @@ LinkerObject const& Assembly::assemble() const assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large."); assertThrow(m_tagPositionsInBytecode[static_cast(i.data())] == numeric_limits::max(), AssemblyException, "Duplicate tag position."); m_tagPositionsInBytecode[static_cast(i.data())] = ret.bytecode.size(); - ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST); + ret.bytecode.push_back(static_cast(Instruction::JUMPDEST)); break; default: assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling."); @@ -708,7 +720,7 @@ LinkerObject const& Assembly::assemble() const if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()) // Append an INVALID here to help tests find miscompilation. - ret.bytecode.push_back(uint8_t(Instruction::INVALID)); + ret.bytecode.push_back(static_cast(Instruction::INVALID)); for (auto const& [subIdPath, bytecodeOffset]: subRef) { diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index e70e844d6d63..8ff96328cc15 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -102,7 +102,7 @@ size_t AssemblyItem::arguments() const if (type() == Operation) return static_cast(instructionInfo(instruction()).args); else if (type() == AssignImmutable) - return 1; + return 2; else return 0; } diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 87ef57089126..a6bb342bca44 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -23,7 +23,6 @@ #include #include #include -#include using namespace std; using namespace solidity; diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index aad0fd169bef..d094bff73e8b 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -27,8 +27,6 @@ #include #include -#include -#include #include diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp index 8431fb51c0af..d6d8adf4a38e 100644 --- a/libevmasm/ControlFlowGraph.cpp +++ b/libevmasm/ControlFlowGraph.cpp @@ -79,19 +79,19 @@ void ControlFlowGraph::splitBlocks() if (item.type() == Tag) { if (id) - m_blocks[id].end = index; + m_blocks[id].end = static_cast(index); id = BlockId::invalid(); } if (!id) { id = item.type() == Tag ? BlockId(item.data()) : generateNewId(); - m_blocks[id].begin = index; + m_blocks[id].begin = static_cast(index); } if (item.type() == PushTag) m_blocks[id].pushedTags.emplace_back(item.data()); if (SemanticInformation::altersControlFlow(item)) { - m_blocks[id].end = index + 1; + m_blocks[id].end = static_cast(index + 1); if (item == Instruction::JUMP) m_blocks[id].endType = BasicBlock::EndType::JUMP; else if (item == Instruction::JUMPI) @@ -103,7 +103,7 @@ void ControlFlowGraph::splitBlocks() } if (id) { - m_blocks[id].end = m_items.size(); + m_blocks[id].end = static_cast(m_items.size()); if (m_blocks[id].endType == BasicBlock::EndType::HANDOVER) m_blocks[id].endType = BasicBlock::EndType::STOP; } diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index 55a3a04c79c1..e2b39a7be2b4 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -86,7 +86,7 @@ ExpressionClasses::Id ExpressionClasses::find( exp.id = id; else { - exp.id = m_representatives.size(); + exp.id = static_cast(m_representatives.size()); m_representatives.push_back(exp); } m_expressions.insert(exp); @@ -117,7 +117,7 @@ void ExpressionClasses::forceEqual( ExpressionClasses::Id ExpressionClasses::newClass(SourceLocation const& _location) { Expression exp; - exp.id = m_representatives.size(); + exp.id = static_cast(m_representatives.size()); exp.item = storeItem(AssemblyItem(UndefinedItem, (u256(1) << 255) + exp.id, _location)); m_representatives.push_back(exp); m_expressions.insert(exp); diff --git a/libevmasm/ExpressionClasses.h b/libevmasm/ExpressionClasses.h index bfab317dcb80..7df907517e25 100644 --- a/libevmasm/ExpressionClasses.h +++ b/libevmasm/ExpressionClasses.h @@ -78,7 +78,7 @@ class ExpressionClasses /// @returns the canonical representative of an expression class. Expression const& representative(Id _id) const { return m_representatives.at(_id); } /// @returns the number of classes. - Id size() const { return m_representatives.size(); } + size_t size() const { return m_representatives.size(); } /// Forces the given @a _item with @a _arguments to the class @a _id. This can be used to /// add prior knowledge e.g. about CALLDATA, but has to be used with caution. Will not work as diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index ec6c50702014..89114b01de4a 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -93,9 +93,13 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool // can be ignored } else if (_item.type() == AssignImmutable) + { // Since AssignImmutable breaks blocks, it should be fine to only consider its changes to the stack, which - // is the same as POP. + // is the same as two POPs. + // Note that the StoreOperation for POP is generic and _copyItem is ignored. + feedItem(AssemblyItem(Instruction::POP), _copyItem); return feedItem(AssemblyItem(Instruction::POP), _copyItem); + } else if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); @@ -155,8 +159,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool ); break; default: - bool invMem = SemanticInformation::invalidatesMemory(_item.instruction()); - bool invStor = SemanticInformation::invalidatesStorage(_item.instruction()); + bool invMem = + SemanticInformation::memory(_item.instruction()) == SemanticInformation::Write; + bool invStor = + SemanticInformation::storage(_item.instruction()) == SemanticInformation::Write; // We could be a bit more fine-grained here (CALL only invalidates part of // memory, etc), but we do not for now. if (invMem) @@ -420,4 +426,3 @@ KnownState::Id KnownState::tagUnion(set _tags) return id; } } - diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 93ccdcaf96e0..8943c662b518 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -71,51 +71,51 @@ std::vector> simplificationRuleListPart1( { using Word = typename Pattern::Word; using Builtins = typename Pattern::Builtins; - return std::vector> { + return std::vector>{ // arithmetic on constants - {Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false}, - {Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false}, - {Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false}, - {Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, - {Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, - {Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, - {Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, - {Builtins::NOT(A), [=]{ return ~A.d(); }, false}, - {Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, - {Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, - {Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, - {Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, - {Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, - {Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, - {Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false}, - {Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false}, - {Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }, false}, + {Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }}, + {Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }}, + {Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }}, + {Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }}, + {Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }}, + {Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }}, + {Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }}, + {Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }}, + {Builtins::NOT(A), [=]{ return ~A.d(); }}, + {Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }}, + {Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }}, + {Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }}, + {Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }}, + {Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }}, + {Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }}, + {Builtins::AND(A, B), [=]{ return A.d() & B.d(); }}, + {Builtins::OR(A, B), [=]{ return A.d() | B.d(); }}, + {Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }}, {Builtins::BYTE(A, B), [=]{ return A.d() >= Pattern::WordSize / 8 ? 0 : (B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff; - }, false}, - {Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, - {Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, + }}, + {Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }}, + {Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }}, {Builtins::SIGNEXTEND(A, B), [=]() -> Word { if (A.d() >= Pattern::WordSize / 8 - 1) return B.d(); unsigned testBit = unsigned(A.d()) * 8 + 7; Word mask = (Word(1) << testBit) - 1; return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; - }, false}, + }}, {Builtins::SHL(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return shlWorkaround(B.d(), unsigned(A.d())); - }, false}, + }}, {Builtins::SHR(A, B), [=]{ if (A.d() >= Pattern::WordSize) return Word(0); return B.d() >> unsigned(A.d()); - }, false} + }} }; } @@ -133,48 +133,48 @@ std::vector> simplificationRuleListPart2( using Builtins = typename Pattern::Builtins; return std::vector> { // invariants involving known constants - {Builtins::ADD(X, 0), [=]{ return X; }, false}, - {Builtins::ADD(0, X), [=]{ return X; }, false}, - {Builtins::SUB(X, 0), [=]{ return X; }, false}, - {Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false}, - {Builtins::MUL(X, 0), [=]{ return Word(0); }, true}, - {Builtins::MUL(0, X), [=]{ return Word(0); }, true}, - {Builtins::MUL(X, 1), [=]{ return X; }, false}, - {Builtins::MUL(1, X), [=]{ return X; }, false}, - {Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, - {Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, - {Builtins::DIV(X, 0), [=]{ return Word(0); }, true}, - {Builtins::DIV(0, X), [=]{ return Word(0); }, true}, - {Builtins::DIV(X, 1), [=]{ return X; }, false}, - {Builtins::SDIV(X, 0), [=]{ return Word(0); }, true}, - {Builtins::SDIV(0, X), [=]{ return Word(0); }, true}, - {Builtins::SDIV(X, 1), [=]{ return X; }, false}, - {Builtins::AND(X, ~Word(0)), [=]{ return X; }, false}, - {Builtins::AND(~Word(0), X), [=]{ return X; }, false}, - {Builtins::AND(X, 0), [=]{ return Word(0); }, true}, - {Builtins::AND(0, X), [=]{ return Word(0); }, true}, - {Builtins::OR(X, 0), [=]{ return X; }, false}, - {Builtins::OR(0, X), [=]{ return X; }, false}, - {Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true}, - {Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true}, - {Builtins::XOR(X, 0), [=]{ return X; }, false}, - {Builtins::XOR(0, X), [=]{ return X; }, false}, - {Builtins::MOD(X, 0), [=]{ return Word(0); }, true}, - {Builtins::MOD(0, X), [=]{ return Word(0); }, true}, - {Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, - {Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, - {Builtins::SHL(0, X), [=]{ return X; }, false}, - {Builtins::SHR(0, X), [=]{ return X; }, false}, - {Builtins::SHL(X, 0), [=]{ return Word(0); }, true}, - {Builtins::SHR(X, 0), [=]{ return Word(0); }, true}, - {Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, - {Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, - {Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true}, - {Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true}, - {Builtins::GT(0, X), [=]{ return Word(0); }, true}, - {Builtins::LT(X, 0), [=]{ return Word(0); }, true}, - {Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false}, - {Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false} + {Builtins::ADD(X, 0), [=]{ return X; }}, + {Builtins::ADD(0, X), [=]{ return X; }}, + {Builtins::SUB(X, 0), [=]{ return X; }}, + {Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }}, + {Builtins::MUL(X, 0), [=]{ return Word(0); }}, + {Builtins::MUL(0, X), [=]{ return Word(0); }}, + {Builtins::MUL(X, 1), [=]{ return X; }}, + {Builtins::MUL(1, X), [=]{ return X; }}, + {Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }}, + {Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }}, + {Builtins::DIV(X, 0), [=]{ return Word(0); }}, + {Builtins::DIV(0, X), [=]{ return Word(0); }}, + {Builtins::DIV(X, 1), [=]{ return X; }}, + {Builtins::SDIV(X, 0), [=]{ return Word(0); }}, + {Builtins::SDIV(0, X), [=]{ return Word(0); }}, + {Builtins::SDIV(X, 1), [=]{ return X; }}, + {Builtins::AND(X, ~Word(0)), [=]{ return X; }}, + {Builtins::AND(~Word(0), X), [=]{ return X; }}, + {Builtins::AND(X, 0), [=]{ return Word(0); }}, + {Builtins::AND(0, X), [=]{ return Word(0); }}, + {Builtins::OR(X, 0), [=]{ return X; }}, + {Builtins::OR(0, X), [=]{ return X; }}, + {Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }}, + {Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }}, + {Builtins::XOR(X, 0), [=]{ return X; }}, + {Builtins::XOR(0, X), [=]{ return X; }}, + {Builtins::MOD(X, 0), [=]{ return Word(0); }}, + {Builtins::MOD(0, X), [=]{ return Word(0); }}, + {Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); },}, + {Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); },}, + {Builtins::SHL(0, X), [=]{ return X; }}, + {Builtins::SHR(0, X), [=]{ return X; }}, + {Builtins::SHL(X, 0), [=]{ return Word(0); }}, + {Builtins::SHR(X, 0), [=]{ return Word(0); }}, + {Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }}, + {Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }}, + {Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }}, + {Builtins::LT(~Word(0), X), [=]{ return Word(0); }}, + {Builtins::GT(0, X), [=]{ return Word(0); }}, + {Builtins::LT(X, 0), [=]{ return Word(0); }}, + {Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }}, + {Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }}, }; } @@ -191,16 +191,16 @@ std::vector> simplificationRuleListPart3( using Builtins = typename Pattern::Builtins; return std::vector> { // operations involving an expression and itself - {Builtins::AND(X, X), [=]{ return X; }, true}, - {Builtins::OR(X, X), [=]{ return X; }, true}, - {Builtins::XOR(X, X), [=]{ return Word(0); }, true}, - {Builtins::SUB(X, X), [=]{ return Word(0); }, true}, - {Builtins::EQ(X, X), [=]{ return Word(1); }, true}, - {Builtins::LT(X, X), [=]{ return Word(0); }, true}, - {Builtins::SLT(X, X), [=]{ return Word(0); }, true}, - {Builtins::GT(X, X), [=]{ return Word(0); }, true}, - {Builtins::SGT(X, X), [=]{ return Word(0); }, true}, - {Builtins::MOD(X, X), [=]{ return Word(0); }, true} + {Builtins::AND(X, X), [=]{ return X; }}, + {Builtins::OR(X, X), [=]{ return X; }}, + {Builtins::XOR(X, X), [=]{ return Word(0); }}, + {Builtins::SUB(X, X), [=]{ return Word(0); }}, + {Builtins::EQ(X, X), [=]{ return Word(1); }}, + {Builtins::LT(X, X), [=]{ return Word(0); }}, + {Builtins::SLT(X, X), [=]{ return Word(0); }}, + {Builtins::GT(X, X), [=]{ return Word(0); }}, + {Builtins::SGT(X, X), [=]{ return Word(0); }}, + {Builtins::MOD(X, X), [=]{ return Word(0); }} }; } @@ -217,23 +217,23 @@ std::vector> simplificationRuleListPart4( using Builtins = typename Pattern::Builtins; return std::vector> { // logical instruction combinations - {Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false}, - {Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true}, - {Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true}, - {Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true}, - {Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true}, - {Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true}, - {Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true}, - {Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true}, - {Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true}, - {Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true}, - {Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true}, - {Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true}, - {Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true}, - {Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true}, - {Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true}, - {Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true}, - {Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true}, + {Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }}, + {Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }}, + {Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }}, + {Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }}, + {Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }}, + {Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }}, + {Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }}, + {Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }}, + {Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }}, + {Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }}, + {Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }}, + {Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }}, + {Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }}, + {Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }}, + {Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }}, + {Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }}, + {Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }}, }; } @@ -249,14 +249,14 @@ std::vector> simplificationRuleListPart4_5( using Builtins = typename Pattern::Builtins; return std::vector>{ // idempotent operations - {Builtins::AND(Builtins::AND(X, Y), Y), [=]{ return Builtins::AND(X, Y); }, true}, - {Builtins::AND(Y, Builtins::AND(X, Y)), [=]{ return Builtins::AND(X, Y); }, true}, - {Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }, true}, - {Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }, true}, - {Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }, true}, - {Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }, true}, - {Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }, true}, - {Builtins::OR(Y, Builtins::OR(Y, X)), [=]{ return Builtins::OR(Y, X); }, true}, + {Builtins::AND(Builtins::AND(X, Y), Y), [=]{ return Builtins::AND(X, Y); }}, + {Builtins::AND(Y, Builtins::AND(X, Y)), [=]{ return Builtins::AND(X, Y); }}, + {Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }}, + {Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }}, + {Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }}, + {Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }}, + {Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }}, + {Builtins::OR(Y, Builtins::OR(Y, X)), [=]{ return Builtins::OR(Y, X); }}, }; } @@ -280,8 +280,7 @@ std::vector> simplificationRuleListPart5( Word value = Word(1) << i; rules.push_back({ Builtins::MOD(X, value), - [=]() -> Pattern { return Builtins::AND(X, value - 1); }, - false + [=]() -> Pattern { return Builtins::AND(X, value - 1); } }); } @@ -289,7 +288,6 @@ std::vector> simplificationRuleListPart5( rules.push_back({ Builtins::SHL(A, X), [=]() -> Pattern { return Word(0); }, - true, [=]() { return A.d() >= Pattern::WordSize; } }); @@ -297,7 +295,6 @@ std::vector> simplificationRuleListPart5( rules.push_back({ Builtins::SHR(A, X), [=]() -> Pattern { return Word(0); }, - true, [=]() { return A.d() >= Pattern::WordSize; } }); @@ -305,7 +302,6 @@ std::vector> simplificationRuleListPart5( rules.push_back({ Builtins::BYTE(A, X), [=]() -> Pattern { return Word(0); }, - true, [=]() { return A.d() >= Pattern::WordSize / 8; } }); @@ -320,13 +316,11 @@ std::vector> simplificationRuleListPart5( Word const mask = (Word(1) << 160) - 1; rules.push_back({ Builtins::AND(Pattern{instr}, mask), - [=]() -> Pattern { return {instr}; }, - false + [=]() -> Pattern { return {instr}; } }); rules.push_back({ Builtins::AND(mask, Pattern{instr}), - [=]() -> Pattern { return {instr}; }, - false + [=]() -> Pattern { return {instr}; } }); } @@ -357,21 +351,18 @@ std::vector> simplificationRuleListPart6( typename Builtins::PatternGeneratorInstance op{instr}; rules.push_back({ Builtins::ISZERO(Builtins::ISZERO(op(X, Y))), - [=]() -> Pattern { return op(X, Y); }, - false + [=]() -> Pattern { return op(X, Y); } }); } rules.push_back({ Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))), - [=]() -> Pattern { return Builtins::ISZERO(X); }, - false + [=]() -> Pattern { return Builtins::ISZERO(X); } }); rules.push_back({ Builtins::ISZERO(Builtins::XOR(X, Y)), - [=]() -> Pattern { return Builtins::EQ(X, Y); }, - false + [=]() -> Pattern { return Builtins::EQ(X, Y); } }); return rules; @@ -409,23 +400,19 @@ std::vector> simplificationRuleListPart7( rules += std::vector>{{ // (X+A)+B -> X+(A+B) op(opXA, B), - [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, - false + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); } }, { // (X+A)+Y -> (X+Y)+A op(opXA, Y), - [=]() -> Pattern { return op(op(X, Y), A); }, - false + [=]() -> Pattern { return op(op(X, Y), A); } }, { // B+(X+A) -> X+(A+B) op(B, opXA), - [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, - false + [=]() -> Pattern { return op(X, fun(A.d(), B.d())); } }, { // Y+(X+A) -> (Y+X)+A op(Y, opXA), - [=]() -> Pattern { return op(op(Y, X), A); }, - false + [=]() -> Pattern { return op(op(Y, X), A); } }}; } } @@ -440,8 +427,7 @@ std::vector> simplificationRuleListPart7( return Builtins::AND(X, Word(0)); else return Builtins::SHL(Word(sum), X); - }, - false + } }); // Combine two SHR by constant @@ -454,8 +440,7 @@ std::vector> simplificationRuleListPart7( return Builtins::AND(X, Word(0)); else return Builtins::SHR(Word(sum), X); - }, - false + } }); // Combine SHL-SHR by constant @@ -472,7 +457,6 @@ std::vector> simplificationRuleListPart7( else return Builtins::AND(X, mask); }, - false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); @@ -490,7 +474,6 @@ std::vector> simplificationRuleListPart7( else return Builtins::AND(X, mask); }, - false, [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } }); @@ -509,14 +492,12 @@ std::vector> simplificationRuleListPart7( // SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) shiftOp(B, Builtins::AND(X, A)), replacement, - false, [=] { return B.d() < Pattern::WordSize; } }); rules.push_back({ // SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) shiftOp(B, Builtins::AND(A, X)), replacement, - false, [=] { return B.d() < Pattern::WordSize; } }); } @@ -526,17 +507,14 @@ std::vector> simplificationRuleListPart7( Builtins::MUL(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { return Builtins::SHL(Y, X); - }, - // Actually only changes the order, does not remove. - true + } }); rules.push_back({ // MUL(SHL(X, 1), Y) -> SHL(X, Y) Builtins::MUL(Builtins::SHL(X, Word(1)), Y), [=]() -> Pattern { return Builtins::SHL(X, Y); - }, - false + } }); rules.push_back({ @@ -544,9 +522,7 @@ std::vector> simplificationRuleListPart7( Builtins::DIV(X, Builtins::SHL(Y, Word(1))), [=]() -> Pattern { return Builtins::SHR(Y, X); - }, - // Actually only changes the order, does not remove. - true + } }); std::function feasibilityFunction = [=]() { @@ -560,7 +536,6 @@ std::vector> simplificationRuleListPart7( // AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B) Builtins::AND(A, Builtins::SHR(B, X)), [=]() -> Pattern { return Builtins::SHR(B, X); }, - false, feasibilityFunction }); @@ -568,22 +543,27 @@ std::vector> simplificationRuleListPart7( // AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B) Builtins::AND(Builtins::SHR(B, X), A), [=]() -> Pattern { return Builtins::SHR(B, X); }, - false, feasibilityFunction }); rules.push_back({ Builtins::BYTE(A, Builtins::SHL(B, X)), [=]() -> Pattern { return Builtins::BYTE(A.d() + B.d() / 8, X); }, - false, [=] { return B.d() % 8 == 0 && A.d() <= 32 && B.d() <= 256; } }); rules.push_back({ Builtins::BYTE(A, Builtins::SHR(B, X)), - [=]() -> Pattern { return A.d() < B.d() / 8 ? Word(0) : Builtins::BYTE(A.d() - B.d() / 8, X); }, - false, - [=] { return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize; } + [=]() -> Pattern { return Word(0); }, + [=] { return A.d() < B.d() / 8; } + }); + + rules.push_back({ + Builtins::BYTE(A, Builtins::SHR(B, X)), + [=]() -> Pattern { return Builtins::BYTE(A.d() - B.d() / 8, X); }, + [=] { + return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize && A.d() >= B.d() / 8; + } }); return rules; @@ -606,76 +586,28 @@ std::vector> simplificationRuleListPart8( { // X - A -> X + (-A) Builtins::SUB(X, A), - [=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }, - false + [=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); } }, { // (X + A) - Y -> (X - Y) + A Builtins::SUB(Builtins::ADD(X, A), Y), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); } }, { // (A + X) - Y -> (X - Y) + A Builtins::SUB(Builtins::ADD(A, X), Y), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); } }, { // X - (Y + A) -> (X - Y) + (-A) Builtins::SUB(X, Builtins::ADD(Y, A)), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); } }, { // X - (A + Y) -> (X - Y) + (-A) Builtins::SUB(X, Builtins::ADD(A, Y)), - [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, - false + [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); } } }; return rules; } -template -std::vector> simplificationRuleListPart9( - Pattern, - Pattern, - Pattern, - Pattern W, - Pattern X, - Pattern Y, - Pattern Z -) -{ - using Word = typename Pattern::Word; - using Builtins = typename Pattern::Builtins; - std::vector> rules; - - assertThrow(Pattern::WordSize > 160, OptimizerException, ""); - Word const mask = (Word(1) << 160) - 1; - // CREATE - rules.push_back({ - Builtins::AND(Builtins::CREATE(W, X, Y), mask), - [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, - false - }); - rules.push_back({ - Builtins::AND(mask, Builtins::CREATE(W, X, Y)), - [=]() -> Pattern { return Builtins::CREATE(W, X, Y); }, - false - }); - // CREATE2 - rules.push_back({ - Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask), - [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, - false - }); - rules.push_back({ - Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)), - [=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); }, - false - }); - - return rules; -} - template std::vector> evmRuleList( langutil::EVMVersion _evmVersion, @@ -683,20 +615,45 @@ std::vector> evmRuleList( Pattern, Pattern, Pattern, - Pattern, + Pattern X, Pattern, Pattern ) { using Builtins = typename Pattern::Builtins; + using Word = typename Pattern::Word; std::vector> rules; if (_evmVersion.hasSelfBalance()) rules.push_back({ Builtins::BALANCE(Instruction::ADDRESS), - []() -> Pattern { return Instruction::SELFBALANCE; }, false + []() -> Pattern { return Instruction::SELFBALANCE; } }); + rules.emplace_back( + Builtins::EXP(0, X), + [=]() -> Pattern { return Builtins::ISZERO(X); } + ); + rules.emplace_back( + Builtins::EXP(1, X), + [=]() -> Pattern { return Word(1); } + ); + if (_evmVersion.hasBitwiseShifting()) + rules.emplace_back( + Builtins::EXP(2, X), + [=]() -> Pattern { return Builtins::SHL(X, 1); } + ); + rules.emplace_back( + Builtins::EXP(Word(-1), X), + [=]() -> Pattern + { + return Builtins::SUB( + Builtins::ISZERO(Builtins::AND(X, Word(1))), + Builtins::AND(X, Word(1)) + ); + } + ); + return rules; } @@ -734,7 +691,6 @@ std::vector> simplificationRuleList( rules += simplificationRuleListPart6(A, B, C, W, X); rules += simplificationRuleListPart7(A, B, C, W, X); rules += simplificationRuleListPart8(A, B, C, W, X); - rules += simplificationRuleListPart9(A, B, C, W, X, Y, Z); if (_evmVersion.has_value()) rules += evmRuleList(*_evmVersion, A, B, C, W, X, Y, Z); diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index f116fe91d327..d1bb580a4605 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -241,7 +241,7 @@ bool SemanticInformation::movable(Instruction _instruction) return true; } -bool SemanticInformation::sideEffectFree(Instruction _instruction) +bool SemanticInformation::canBeRemoved(Instruction _instruction) { // These are not really functional. assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, ""); @@ -249,15 +249,15 @@ bool SemanticInformation::sideEffectFree(Instruction _instruction) return !instructionInfo(_instruction).sideEffects; } -bool SemanticInformation::sideEffectFreeIfNoMSize(Instruction _instruction) +bool SemanticInformation::canBeRemovedIfNoMSize(Instruction _instruction) { if (_instruction == Instruction::KECCAK256 || _instruction == Instruction::MLOAD) return true; else - return sideEffectFree(_instruction); + return canBeRemoved(_instruction); } -bool SemanticInformation::invalidatesMemory(Instruction _instruction) +SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction) { switch (_instruction) { @@ -272,13 +272,47 @@ bool SemanticInformation::invalidatesMemory(Instruction _instruction) case Instruction::CALLCODE: case Instruction::DELEGATECALL: case Instruction::STATICCALL: + return SemanticInformation::Write; + + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::KECCAK256: + case Instruction::MLOAD: + case Instruction::MSIZE: + case Instruction::RETURN: + case Instruction::REVERT: + case Instruction::LOG0: + case Instruction::LOG1: + case Instruction::LOG2: + case Instruction::LOG3: + case Instruction::LOG4: + return SemanticInformation::Read; + + default: + return SemanticInformation::None; + } +} + +bool SemanticInformation::movableApartFromEffects(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::EXTCODEHASH: + case Instruction::EXTCODESIZE: + case Instruction::RETURNDATASIZE: + case Instruction::BALANCE: + case Instruction::SELFBALANCE: + case Instruction::SLOAD: + case Instruction::KECCAK256: + case Instruction::MLOAD: return true; + default: - return false; + return movable(_instruction); } } -bool SemanticInformation::invalidatesStorage(Instruction _instruction) +SemanticInformation::Effect SemanticInformation::storage(Instruction _instruction) { switch (_instruction) { @@ -289,9 +323,45 @@ bool SemanticInformation::invalidatesStorage(Instruction _instruction) case Instruction::CREATE: case Instruction::CREATE2: case Instruction::SSTORE: - return true; + return SemanticInformation::Write; + + case Instruction::SLOAD: + case Instruction::STATICCALL: + return SemanticInformation::Read; + default: - return false; + return SemanticInformation::None; + } +} + +SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::SELFDESTRUCT: + case Instruction::STATICCALL: // because it can affect returndatasize + // Strictly speaking, log0, .., log4 writes to the state, but the EVM cannot read it, so they + // are just marked as having 'other side effects.' + return SemanticInformation::Write; + + case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: + case Instruction::RETURNDATASIZE: + case Instruction::BALANCE: + case Instruction::SELFBALANCE: + case Instruction::RETURNDATACOPY: + case Instruction::EXTCODECOPY: + // PC and GAS are specifically excluded here. Instructions such as CALLER, CALLVALUE, + // ADDRESS are excluded because they cannot change during execution. + return SemanticInformation::Read; + + default: + return SemanticInformation::None; } } @@ -310,6 +380,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::ORIGIN: case Instruction::CALLER: case Instruction::CALLVALUE: + case Instruction::CHAINID: case Instruction::CALLTOKENVALUE: case Instruction::CALLTOKENID: case Instruction::GAS: diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index f919b4207b4c..679766e667f9 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -36,6 +36,15 @@ class AssemblyItem; */ struct SemanticInformation { + /// Corresponds to the effect that a YUL-builtin has on a generic data location (storage, memory + /// and other blockchain state). + enum Effect + { + None, + Read, + Write + }; + /// @returns true if the given items starts a new block for common subexpression analysis. /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize. static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant); @@ -57,20 +66,23 @@ struct SemanticInformation /// without altering the semantics. This means it cannot depend on storage or memory, /// cannot have any side-effects, but it can depend on a call-constant state of the blockchain. static bool movable(Instruction _instruction); + /// If true, the expressions in this code can be moved or copied (together with their arguments) + /// across control flow branches and instructions as long as these instructions' 'effects' do + /// not influence the 'effects' of the aforementioned expressions. + static bool movableApartFromEffects(Instruction _instruction); /// @returns true if the instruction can be removed without changing the semantics. /// This does not mean that it has to be deterministic or retrieve information from /// somewhere else than purely the values of its arguments. - static bool sideEffectFree(Instruction _instruction); + static bool canBeRemoved(Instruction _instruction); /// @returns true if the instruction can be removed without changing the semantics. /// This does not mean that it has to be deterministic or retrieve information from /// somewhere else than purely the values of its arguments. /// If true, the instruction is still allowed to influence the value returned by the /// msize instruction. - static bool sideEffectFreeIfNoMSize(Instruction _instruction); - /// @returns true if the given instruction modifies memory. - static bool invalidatesMemory(Instruction _instruction); - /// @returns true if the given instruction modifies storage (even indirectly). - static bool invalidatesStorage(Instruction _instruction); + static bool canBeRemovedIfNoMSize(Instruction _instruction); + static Effect memory(Instruction _instruction); + static Effect storage(Instruction _instruction); + static Effect otherState(Instruction _instruction); static bool invalidInPureFunctions(Instruction _instruction); static bool invalidInViewFunctions(Instruction _instruction); }; diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index 6ce9a9e7c08b..b6a704a567ab 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -30,9 +30,8 @@ namespace solidity::evmasm /** * Rule that contains a pattern, an action that can be applied - * after the pattern has matched and a bool that indicates - * whether the action would remove something from the expression - * than is not a constant literal. + * after the pattern has matched and optional condition to check if the + * action should be applied. */ template struct SimplificationRule @@ -40,18 +39,15 @@ struct SimplificationRule SimplificationRule( Pattern _pattern, std::function _action, - bool _removesNonConstants, std::function _feasible = {} ): pattern(std::move(_pattern)), action(std::move(_action)), - removesNonConstants(_removesNonConstants), feasible(std::move(_feasible)) {} Pattern pattern; std::function action; - bool removesNonConstants; std::function feasible; }; diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt index ab30ac34e28e..477d3695c999 100644 --- a/liblangutil/CMakeLists.txt +++ b/liblangutil/CMakeLists.txt @@ -21,8 +21,6 @@ set(sources SourceReferenceExtractor.h SourceReferenceFormatter.cpp SourceReferenceFormatter.h - SourceReferenceFormatterHuman.cpp - SourceReferenceFormatterHuman.h Token.cpp Token.h UndefMacros.h diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h index aa8f01af266c..23a0781fc727 100644 --- a/liblangutil/CharStream.h +++ b/liblangutil/CharStream.h @@ -98,6 +98,20 @@ class CharStream std::tuple translatePositionToLineColumn(int _position) const; ///@} + /// Tests whether or not given octet sequence is present at the current position in stream. + /// @returns true if the sequence could be found, false otherwise. + bool prefixMatch(std::string_view _sequence) + { + if (isPastEndOfInput(_sequence.size())) + return false; + + for (size_t i = 0; i < _sequence.size(); ++i) + if (_sequence[i] != get(i)) + return false; + + return true; + } + private: std::string m_source; std::string m_name; diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 29a20a8d8ce0..02a983651c82 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -23,6 +23,7 @@ #include #include +#include #include using namespace std; diff --git a/liblangutil/ParserBase.cpp b/liblangutil/ParserBase.cpp index 19c4d187cc58..089f606fa8fa 100644 --- a/liblangutil/ParserBase.cpp +++ b/liblangutil/ParserBase.cpp @@ -94,6 +94,8 @@ void ParserBase::expectToken(Token _value, bool _advance) void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentNodeName, bool _advance) { + solAssert(m_inParserRecovery, "The function is supposed to be called during parser recovery only."); + Token tok = m_scanner->currentToken(); if (tok != _value) { @@ -103,24 +105,20 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN m_scanner->next(); string const expectedToken = ParserBase::tokenName(_value); - string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead."; if (m_scanner->currentToken() == Token::EOS) { // rollback to where the token started, and raise exception to be caught at a higher level. m_scanner->setPosition(static_cast(startPosition)); - m_inParserRecovery = true; + string const msg = "In " + _currentNodeName + ", " + expectedToken + "is expected; got " + ParserBase::tokenName(tok) + " instead."; fatalParserError(1957_error, errorLoc, msg); } else { - if (m_inParserRecovery) - parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); - else - parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken); + parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); m_inParserRecovery = false; } } - else if (m_inParserRecovery) + else { string expectedToken = ParserBase::tokenName(_value); parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + "."); diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 3e125f04e842..5a9f9fccef3c 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -54,9 +54,10 @@ #include #include -#include +#include + #include -#include +#include #include using namespace std; @@ -79,6 +80,8 @@ string to_string(ScannerError _errorCode) case ScannerError::IllegalExponent: return "Invalid exponent."; case ScannerError::IllegalNumberEnd: return "Identifier-start is not allowed at end of a number."; case ScannerError::OctalNotAllowed: return "Octal numbers not allowed."; + case ScannerError::DirectionalOverrideUnderflow: return "Unicode direction override underflow in comment or string literal."; + case ScannerError::DirectionalOverrideMismatch: return "Mismatching directional override markers in comment or string literal."; default: solAssert(false, "Unhandled case in to_string(ScannerError)"); return ""; @@ -147,7 +150,7 @@ void Scanner::reset(shared_ptr _source) void Scanner::reset() { m_source->reset(); - m_supportPeriodInIdentifier = false; + m_kind = ScannerKind::Solidity; m_char = m_source->get(); skipWhitespace(); next(); @@ -163,12 +166,6 @@ void Scanner::setPosition(size_t _offset) next(); } -void Scanner::supportPeriodInIdentifier(bool _value) -{ - m_supportPeriodInIdentifier = _value; - rescan(); -} - bool Scanner::scanHexByte(char& o_scannedByte) { char x = 0; @@ -277,12 +274,61 @@ bool Scanner::skipWhitespaceExceptUnicodeLinebreak() return sourcePos() != startPosition; } + +namespace +{ + +/// Tries to scan for an RLO/LRO/RLE/LRE/PDF and keeps track of script writing direction override depth. +/// +/// @returns ScannerError::NoError in case of successful parsing and directional encodings are paired +/// and error code in case the input's lexical parser state is invalid and this error should be reported +/// to the user. +static ScannerError validateBiDiMarkup(CharStream& _stream, size_t _startPosition) +{ + static array, 5> constexpr directionalSequences{ + pair{"\xE2\x80\xAD", 1}, // U+202D (LRO - Left-to-Right Override) + pair{"\xE2\x80\xAE", 1}, // U+202E (RLO - Right-to-Left Override) + pair{"\xE2\x80\xAA", 1}, // U+202A (LRE - Left-to-Right Embedding) + pair{"\xE2\x80\xAB", 1}, // U+202B (RLE - Right-to-Left Embedding) + pair{"\xE2\x80\xAC", -1} // U+202C (PDF - Pop Directional Formatting + }; + + size_t endPosition = _stream.position(); + _stream.setPosition(_startPosition); + + int directionOverrideDepth = 0; + + for (size_t currentPos = _startPosition; currentPos < endPosition; ++currentPos) + { + _stream.setPosition(currentPos); + + for (auto const& [sequence, depthChange]: directionalSequences) + if (_stream.prefixMatch(sequence)) + directionOverrideDepth += depthChange; + + if (directionOverrideDepth < 0) + return ScannerError::DirectionalOverrideUnderflow; + } + + _stream.setPosition(endPosition); + + return directionOverrideDepth > 0 ? ScannerError::DirectionalOverrideMismatch : ScannerError::NoError; +} + +} + Token Scanner::skipSingleLineComment() { // Line terminator is not part of the comment. If it is a // non-ascii line terminator, it will result in a parser error. + size_t startPosition = m_source->position(); while (!isUnicodeLinebreak()) - if (!advance()) break; + if (!advance()) + break; + + ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + if (unicodeDirectionError != ScannerError::NoError) + return setError(unicodeDirectionError); return Token::Whitespace; } @@ -355,16 +401,21 @@ size_t Scanner::scanSingleLineDocComment() Token Scanner::skipMultiLineComment() { + size_t startPosition = m_source->position(); while (!isSourcePastEndOfInput()) { - char ch = m_char; + char prevChar = m_char; advance(); // If we have reached the end of the multi-line comment, we // consume the '/' and insert a whitespace. This way all // multi-line comments are treated as whitespace. - if (ch == '*' && m_char == '/') + if (prevChar == '*' && m_char == '/') { + ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + if (unicodeDirectionError != ScannerError::NoError) + return setError(unicodeDirectionError); + m_char = ' '; return Token::Whitespace; } @@ -546,7 +597,7 @@ void Scanner::scanToken() if (m_char == '=') token = selectToken(Token::Equal); else if (m_char == '>') - token = selectToken(Token::Arrow); + token = selectToken(Token::DoubleArrow); else token = Token::Assign; break; @@ -569,12 +620,14 @@ void Scanner::scanToken() token = Token::Add; break; case '-': - // - -- -= + // - -- -= -> advance(); if (m_char == '-') token = selectToken(Token::Dec); else if (m_char == '=') token = selectToken(Token::AssignSub); + else if (m_char == '>') + token = selectToken(Token::RightArrow); else token = Token::Sub; break; @@ -684,7 +737,7 @@ void Scanner::scanToken() else token = setError(ScannerError::IllegalToken); } - else if (token == Token::Unicode) + else if (token == Token::Unicode && m_kind != ScannerKind::Yul) { // reset m = 0; @@ -732,12 +785,6 @@ bool Scanner::scanEscape() case '"': // fall through case '\\': break; - case 'b': - c = '\b'; - break; - case 'f': - c = '\f'; - break; case 'n': c = '\n'; break; @@ -747,9 +794,6 @@ bool Scanner::scanEscape() case 't': c = '\t'; break; - case 'v': - c = '\v'; - break; case 'u': { if (auto const codepoint = scanUnicode(); codepoint.has_value()) @@ -789,6 +833,7 @@ bool Scanner::isUnicodeLinebreak() Token Scanner::scanString(bool const _isUnicode) { + size_t startPosition = m_source->position(); char const quote = m_char; advance(); // consume quote LiteralScope literal(this, LITERAL_TYPE_STRING); @@ -816,6 +861,14 @@ Token Scanner::scanString(bool const _isUnicode) } if (m_char != quote) return setError(ScannerError::IllegalStringEndQuote); + + if (_isUnicode) + { + ScannerError unicodeDirectionError = validateBiDiMarkup(*m_source, startPosition); + if (unicodeDirectionError != ScannerError::NoError) + return setError(unicodeDirectionError); + } + literal.complete(); advance(); // consume quote return _isUnicode ? Token::UnicodeStringLiteral : Token::StringLiteral; @@ -970,10 +1023,20 @@ tuple Scanner::scanIdentifierOrKeyword() LiteralScope literal(this, LITERAL_TYPE_STRING); addLiteralCharAndAdvance(); // Scan the rest of the identifier characters. - while (isIdentifierPart(m_char) || (m_char == '.' && m_supportPeriodInIdentifier)) + while (isIdentifierPart(m_char) || (m_char == '.' && m_kind == ScannerKind::Yul)) addLiteralCharAndAdvance(); literal.complete(); - return TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); + auto const token = TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal); + if (m_kind == ScannerKind::Yul) + { + // Turn Solidity identifier into a Yul keyword + if (m_tokens[NextNext].literal == "leave") + return std::make_tuple(Token::Leave, 0, 0); + // Turn non-Yul keywords into identifiers. + if (!TokenTraits::isYulKeyword(std::get<0>(token))) + return std::make_tuple(Token::Identifier, 0, 0); + } + return token; } } // namespace solidity::langutil diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h index 24ec098abd89..97f60bbda8d5 100644 --- a/liblangutil/Scanner.h +++ b/liblangutil/Scanner.h @@ -68,6 +68,12 @@ class AstRawString; class AstValueFactory; class ParserRecorder; +enum class ScannerKind +{ + Solidity, + Yul +}; + enum class ScannerError { NoError, @@ -83,6 +89,9 @@ enum class ScannerError IllegalExponent, IllegalNumberEnd, + DirectionalOverrideUnderflow, + DirectionalOverrideMismatch, + OctalNotAllowed, }; @@ -107,9 +116,14 @@ class Scanner /// Resets scanner to the start of input. void reset(); - /// Enables or disables support for period in identifier. - /// This re-scans the current token and comment literal and thus invalidates it. - void supportPeriodInIdentifier(bool _value); + /// Changes the scanner mode. + void setScannerMode(ScannerKind _kind) + { + m_kind = _kind; + + // Invalidate lookahead buffer. + rescan(); + } /// @returns the next token and advances input Token next(); @@ -172,6 +186,7 @@ class Scanner ///@} private: + inline Token setError(ScannerError _error) noexcept { m_tokens[NextNext].error = _error; @@ -249,8 +264,6 @@ class Scanner size_t sourcePos() const { return m_source->position(); } bool isSourcePastEndOfInput() const { return m_source->isPastEndOfInput(); } - bool m_supportPeriodInIdentifier = false; - enum TokenIndex { Current, Next, NextNext }; TokenDesc m_skippedComments[3] = {}; // desc for the current, next and nextnext skipped comment @@ -258,6 +271,8 @@ class Scanner std::shared_ptr m_source; + ScannerKind m_kind = ScannerKind::Solidity; + /// one character look-ahead, equals 0 at end of input char m_char; }; diff --git a/liblangutil/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp index d7a0a21690ff..28b4e34166cc 100644 --- a/liblangutil/SourceReferenceFormatter.cpp +++ b/liblangutil/SourceReferenceFormatter.cpp @@ -16,62 +16,160 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * @author Christian - * @date 2014 * Formatting functions for errors referencing positions and locations in the source. */ #include #include #include +#include +#include +#include using namespace std; using namespace solidity; -using namespace solidity::util; using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::util::formatting; + +namespace +{ + +std::string replaceNonTabs(std::string_view _utf8Input, char _filler) +{ + std::string output; + for (char const c: _utf8Input) + if ((c & 0xc0) != 0x80) + output.push_back(c == '\t' ? '\t' : _filler); + return output; +} + +} + +AnsiColorized SourceReferenceFormatter::normalColored() const +{ + return AnsiColorized(m_stream, m_colored, {WHITE}); +} + +AnsiColorized SourceReferenceFormatter::frameColored() const +{ + return AnsiColorized(m_stream, m_colored, {BOLD, BLUE}); +} -void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location) +AnsiColorized SourceReferenceFormatter::errorColored() const { - printSourceLocation(SourceReferenceExtractor::extract(_location)); + return AnsiColorized(m_stream, m_colored, {BOLD, RED}); +} + +AnsiColorized SourceReferenceFormatter::messageColored() const +{ + return AnsiColorized(m_stream, m_colored, {BOLD, WHITE}); +} + +AnsiColorized SourceReferenceFormatter::secondaryColored() const +{ + return AnsiColorized(m_stream, m_colored, {BOLD, CYAN}); +} + +AnsiColorized SourceReferenceFormatter::highlightColored() const +{ + return AnsiColorized(m_stream, m_colored, {YELLOW}); +} + +AnsiColorized SourceReferenceFormatter::diagColored() const +{ + return AnsiColorized(m_stream, m_colored, {BOLD, YELLOW}); } void SourceReferenceFormatter::printSourceLocation(SourceReference const& _ref) { - if (_ref.position.line < 0) + if (_ref.sourceName.empty()) return; // Nothing we can print here + if (_ref.position.line < 0) + { + frameColored() << "-->"; + m_stream << ' ' << _ref.sourceName << '\n'; + return; // No line available, nothing else to print + } + + string line = std::to_string(_ref.position.line + 1); // one-based line number as string + string leftpad = string(line.size(), ' '); + + // line 0: source name + m_stream << leftpad; + frameColored() << "-->"; + m_stream << ' ' << _ref.sourceName << ':' << line << ':' << (_ref.position.column + 1) << ":\n"; + + string_view text = _ref.text; + if (!_ref.multiline) { - m_stream << _ref.text << endl; + size_t const locationLength = static_cast(_ref.endColumn - _ref.startColumn); + + // line 1: + m_stream << leftpad << ' '; + frameColored() << '|'; + m_stream << '\n'; + + // line 2: + frameColored() << line << " |"; - // mark the text-range like this: ^-----^ - for_each( - _ref.text.cbegin(), - _ref.text.cbegin() + _ref.startColumn, - [this](char ch) { m_stream << (ch == '\t' ? '\t' : ' '); } + m_stream << ' ' << text.substr(0, static_cast(_ref.startColumn)); + highlightColored() << text.substr(static_cast(_ref.startColumn), locationLength); + m_stream << text.substr(static_cast(_ref.endColumn)) << '\n'; + + // line 3: + m_stream << leftpad << ' '; + frameColored() << '|'; + + m_stream << ' ' << replaceNonTabs(text.substr(0, static_cast(_ref.startColumn)), ' '); + diagColored() << ( + locationLength == 0 ? + "^" : + replaceNonTabs(text.substr(static_cast(_ref.startColumn), locationLength), '^') ); - m_stream << "^"; - if (_ref.endColumn > _ref.startColumn + 2) - m_stream << string(static_cast(_ref.endColumn - _ref.startColumn - 2), '-'); - if (_ref.endColumn > _ref.startColumn + 1) - m_stream << "^"; - m_stream << endl; + m_stream << '\n'; } else - m_stream << - _ref.text << - endl << - string(static_cast(_ref.startColumn), ' ') << - "^ (Relevant source part starts here and spans across multiple lines)." << - endl; + { + // line 1: + m_stream << leftpad << ' '; + frameColored() << '|'; + m_stream << '\n'; + + // line 2: + frameColored() << line << " |"; + m_stream << ' ' << text.substr(0, static_cast(_ref.startColumn)); + highlightColored() << text.substr(static_cast(_ref.startColumn)) << '\n'; + + // line 3: + m_stream << leftpad << ' '; + frameColored() << '|'; + m_stream << ' ' << replaceNonTabs(text.substr(0, static_cast(_ref.startColumn)), ' '); + diagColored() << "^ (Relevant source part starts here and spans across multiple lines)."; + m_stream << '\n'; + } } -void SourceReferenceFormatter::printSourceName(SourceReference const& _ref) +void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtractor::Message const& _msg) { - if (_ref.position.line != -1) - m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": "; - else if (!_ref.sourceName.empty()) - m_stream << _ref.sourceName << ": "; + // exception header line + errorColored() << _msg.category; + if (m_withErrorIds && _msg.errorId.has_value()) + errorColored() << " (" << _msg.errorId.value().error << ")"; + messageColored() << ": " << _msg.primary.message << '\n'; + + printSourceLocation(_msg.primary); + + for (auto const& secondary: _msg.secondary) + { + secondaryColored() << "Note"; + messageColored() << ":" << (secondary.message.empty() ? "" : (" " + secondary.message)) << '\n'; + printSourceLocation(secondary); + } + + m_stream << '\n'; } void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category) @@ -83,19 +181,3 @@ void SourceReferenceFormatter::printErrorInformation(Error const& _error) { printExceptionInformation(SourceReferenceExtractor::extract(_error)); } - -void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtractor::Message const& _msg) -{ - printSourceName(_msg.primary); - - m_stream << _msg.category << ": " << _msg.primary.message << endl; - - printSourceLocation(_msg.primary); - - for (auto const& ref: _msg.secondary) - { - printSourceName(ref); - m_stream << ref.message << endl; - printSourceLocation(ref); - } -} diff --git a/liblangutil/SourceReferenceFormatter.h b/liblangutil/SourceReferenceFormatter.h index 636ddfcb03b4..dac76912db14 100644 --- a/liblangutil/SourceReferenceFormatter.h +++ b/liblangutil/SourceReferenceFormatter.h @@ -16,71 +16,71 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * @author Christian - * @date 2014 * Formatting functions for errors referencing positions and locations in the source. */ #pragma once -#include -#include -#include #include #include -namespace solidity::util -{ -struct Exception; // forward -} +#include + +#include +#include +#include namespace solidity::langutil { struct SourceLocation; -class Scanner; class SourceReferenceFormatter { public: - explicit SourceReferenceFormatter(std::ostream& _stream): - m_stream(_stream) + SourceReferenceFormatter(std::ostream& _stream, bool _colored, bool _withErrorIds): + m_stream(_stream), m_colored(_colored), m_withErrorIds(_withErrorIds) {} - virtual ~SourceReferenceFormatter() = default; - /// Prints source location if it is given. - virtual void printSourceLocation(SourceReference const& _ref); - virtual void printExceptionInformation(SourceReferenceExtractor::Message const& _msg); - - virtual void printSourceLocation(SourceLocation const* _location); - virtual void printExceptionInformation(util::Exception const& _exception, std::string const& _category); - virtual void printErrorInformation(Error const& _error); - - static std::string formatErrorInformation(Error const& _error) - { - return formatExceptionInformation( - _error, - (_error.type() == Error::Type::Warning) ? "Warning" : "Error" - ); - } + void printSourceLocation(SourceReference const& _ref); + void printExceptionInformation(SourceReferenceExtractor::Message const& _msg); + void printExceptionInformation(util::Exception const& _exception, std::string const& _category); + void printErrorInformation(Error const& _error); static std::string formatExceptionInformation( util::Exception const& _exception, - std::string const& _name + std::string const& _name, + bool _colored = false, + bool _withErrorIds = false ) { std::ostringstream errorOutput; - - SourceReferenceFormatter formatter(errorOutput); + SourceReferenceFormatter formatter(errorOutput, _colored, _withErrorIds); formatter.printExceptionInformation(_exception, _name); return errorOutput.str(); } -protected: - /// Prints source name if location is given. - void printSourceName(SourceReference const& _ref); + static std::string formatErrorInformation(Error const& _error) + { + return formatExceptionInformation( + _error, + (_error.type() == Error::Type::Warning) ? "Warning" : "Error" + ); + } + +private: + util::AnsiColorized normalColored() const; + util::AnsiColorized frameColored() const; + util::AnsiColorized errorColored() const; + util::AnsiColorized messageColored() const; + util::AnsiColorized secondaryColored() const; + util::AnsiColorized highlightColored() const; + util::AnsiColorized diagColored() const; +private: std::ostream& m_stream; + bool m_colored; + bool m_withErrorIds; }; } diff --git a/liblangutil/SourceReferenceFormatterHuman.cpp b/liblangutil/SourceReferenceFormatterHuman.cpp deleted file mode 100644 index 96bef2ed7753..000000000000 --- a/liblangutil/SourceReferenceFormatterHuman.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -// SPDX-License-Identifier: GPL-3.0 -/** - * Formatting functions for errors referencing positions and locations in the source. - */ - -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace solidity; -using namespace solidity::langutil; -using namespace solidity::util; -using namespace solidity::util::formatting; - -namespace -{ - -std::string replaceNonTabs(std::string_view _utf8Input, char _filler) -{ - std::string output; - for (char const c: _utf8Input) - if ((c & 0xc0) != 0x80) - output.push_back(c == '\t' ? '\t' : _filler); - return output; -} - -} - -AnsiColorized SourceReferenceFormatterHuman::normalColored() const -{ - return AnsiColorized(m_stream, m_colored, {WHITE}); -} - -AnsiColorized SourceReferenceFormatterHuman::frameColored() const -{ - return AnsiColorized(m_stream, m_colored, {BOLD, BLUE}); -} - -AnsiColorized SourceReferenceFormatterHuman::errorColored() const -{ - return AnsiColorized(m_stream, m_colored, {BOLD, RED}); -} - -AnsiColorized SourceReferenceFormatterHuman::messageColored() const -{ - return AnsiColorized(m_stream, m_colored, {BOLD, WHITE}); -} - -AnsiColorized SourceReferenceFormatterHuman::secondaryColored() const -{ - return AnsiColorized(m_stream, m_colored, {BOLD, CYAN}); -} - -AnsiColorized SourceReferenceFormatterHuman::highlightColored() const -{ - return AnsiColorized(m_stream, m_colored, {YELLOW}); -} - -AnsiColorized SourceReferenceFormatterHuman::diagColored() const -{ - return AnsiColorized(m_stream, m_colored, {BOLD, YELLOW}); -} - -void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _ref) -{ - if (_ref.sourceName.empty()) - return; // Nothing we can print here - - if (_ref.position.line < 0) - { - frameColored() << "-->"; - m_stream << ' ' << _ref.sourceName << '\n'; - return; // No line available, nothing else to print - } - - string line = std::to_string(_ref.position.line + 1); // one-based line number as string - string leftpad = string(line.size(), ' '); - - // line 0: source name - m_stream << leftpad; - frameColored() << "-->"; - m_stream << ' ' << _ref.sourceName << ':' << line << ':' << (_ref.position.column + 1) << ":\n"; - - string_view text = _ref.text; - - if (!_ref.multiline) - { - auto const locationLength = static_cast(_ref.endColumn - _ref.startColumn); - - // line 1: - m_stream << leftpad << ' '; - frameColored() << '|'; - m_stream << '\n'; - - // line 2: - frameColored() << line << " |"; - - m_stream << ' ' << text.substr(0, static_cast(_ref.startColumn)); - highlightColored() << text.substr(static_cast(_ref.startColumn), locationLength); - m_stream << text.substr(static_cast(_ref.endColumn)) << '\n'; - - // line 3: - m_stream << leftpad << ' '; - frameColored() << '|'; - - m_stream << ' ' << replaceNonTabs(text.substr(0, static_cast(_ref.startColumn)), ' '); - diagColored() << replaceNonTabs(text.substr(static_cast(_ref.startColumn), locationLength), '^'); - m_stream << '\n'; - } - else - { - // line 1: - m_stream << leftpad << ' '; - frameColored() << '|'; - m_stream << '\n'; - - // line 2: - frameColored() << line << " |"; - m_stream << ' ' << text.substr(0, static_cast(_ref.startColumn)); - highlightColored() << text.substr(static_cast(_ref.startColumn)) << '\n'; - - // line 3: - m_stream << leftpad << ' '; - frameColored() << '|'; - m_stream << ' ' << replaceNonTabs(text.substr(0, static_cast(_ref.startColumn)), ' '); - diagColored() << "^ (Relevant source part starts here and spans across multiple lines)."; - m_stream << '\n'; - } -} - -void SourceReferenceFormatterHuman::printExceptionInformation(SourceReferenceExtractor::Message const& _msg) -{ - // exception header line - errorColored() << _msg.category; - if (m_withErrorIds && _msg.errorId.has_value()) - errorColored() << " (" << _msg.errorId.value().error << ")"; - messageColored() << ": " << _msg.primary.message << '\n'; - - printSourceLocation(_msg.primary); - - for (auto const& secondary: _msg.secondary) - { - secondaryColored() << "Note"; - messageColored() << ": " << secondary.message << '\n'; - printSourceLocation(secondary); - } - - m_stream << '\n'; -} diff --git a/liblangutil/SourceReferenceFormatterHuman.h b/liblangutil/SourceReferenceFormatterHuman.h deleted file mode 100644 index 21816ce67004..000000000000 --- a/liblangutil/SourceReferenceFormatterHuman.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -// SPDX-License-Identifier: GPL-3.0 -/** - * Formatting functions for errors referencing positions and locations in the source. - */ - -#pragma once - -#include -#include // SourceReferenceFormatterBase - -#include - -#include -#include -#include - -namespace solidity::langutil -{ - -class SourceReferenceFormatterHuman: public SourceReferenceFormatter -{ -public: - SourceReferenceFormatterHuman(std::ostream& _stream, bool _colored, bool _withErrorIds): - SourceReferenceFormatter{_stream}, m_colored{_colored}, m_withErrorIds(_withErrorIds) - {} - - void printSourceLocation(SourceReference const& _ref) override; - void printExceptionInformation(SourceReferenceExtractor::Message const& _msg) override; - using SourceReferenceFormatter::printExceptionInformation; - - static std::string formatExceptionInformation( - util::Exception const& _exception, - std::string const& _name, - bool _colored = false, - bool _withErrorIds = false - ) - { - std::ostringstream errorOutput; - - SourceReferenceFormatterHuman formatter(errorOutput, _colored, _withErrorIds); - formatter.printExceptionInformation(_exception, _name); - return errorOutput.str(); - } - -private: - util::AnsiColorized normalColored() const; - util::AnsiColorized frameColored() const; - util::AnsiColorized errorColored() const; - util::AnsiColorized messageColored() const; - util::AnsiColorized secondaryColored() const; - util::AnsiColorized highlightColored() const; - util::AnsiColorized diagColored() const; - -private: - bool m_colored; - bool m_withErrorIds; -}; - -} diff --git a/liblangutil/Token.cpp b/liblangutil/Token.cpp index 70b645992491..b6cb1e0edae8 100644 --- a/liblangutil/Token.cpp +++ b/liblangutil/Token.cpp @@ -41,7 +41,6 @@ // along with solidity. If not, see . #include -#include #include using namespace std; @@ -115,29 +114,6 @@ std::string friendlyName(Token tok) return std::string(ret); } -#define T(name, string, precedence) precedence, -int precedence(Token tok) -{ - int8_t const static precs[TokenTraits::count()] = - { - TOKEN_LIST(T, T) - }; - return precs[static_cast(tok)]; -} -#undef T - -int parseSize(string::const_iterator _begin, string::const_iterator _end) -{ - try - { - int m = boost::lexical_cast(boost::make_iterator_range(_begin, _end)); - return m; - } - catch(boost::bad_lexical_cast const&) - { - return -1; - } -} static Token keywordByName(string const& _name) { @@ -152,8 +128,39 @@ static Token keywordByName(string const& _name) return it == keywords.end() ? Token::Identifier : it->second; } +bool isYulKeyword(string const& _literal) +{ + return _literal == "leave" || isYulKeyword(keywordByName(_literal)); +} + tuple fromIdentifierOrKeyword(string const& _literal) { + // Used for `bytesM`, `uintM`, `intM`, `fixedMxN`, `ufixedMxN`. + // M/N must be shortest representation. M can never be 0. N can be zero. + auto parseSize = [](string::const_iterator _begin, string::const_iterator _end) -> int + { + // No number. + if (distance(_begin, _end) == 0) + return -1; + + // Disallow leading zero. + if (distance(_begin, _end) > 1 && *_begin == '0') + return -1; + + int ret = 0; + for (auto it = _begin; it != _end; it++) + { + if (*it < '0' || *it > '9') + return -1; + // Overflow check. The largest acceptable value is 256 in the callers. + if (ret >= 256) + return -1; + ret *= 10; + ret += *it - '0'; + } + return ret; + }; + auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); if (positionM != _literal.end()) { diff --git a/liblangutil/Token.h b/liblangutil/Token.h index f7fd241d3996..aef9ffe25bea 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -83,7 +83,8 @@ namespace solidity::langutil T(Semicolon, ";", 0) \ T(Period, ".", 0) \ T(Conditional, "?", 3) \ - T(Arrow, "=>", 0) \ + T(DoubleArrow, "=>", 0) \ + T(RightArrow, "->", 0) \ \ /* Assignment operators. */ \ /* IsAssignmentOp() relies on this block of enum values being */ \ @@ -190,6 +191,7 @@ namespace solidity::langutil K(Throw, "throw", 0) \ K(Try, "try", 0) \ K(Type, "type", 0) \ + K(Unchecked, "unchecked", 0) \ K(Unicode, "unicode", 0) \ K(Using, "using", 0) \ K(View, "view", 0) \ @@ -212,7 +214,6 @@ namespace solidity::langutil K(Int, "int", 0) \ K(UInt, "uint", 0) \ K(Bytes, "bytes", 0) \ - K(Byte, "byte", 0) \ K(String, "string", 0) \ K(Address, "address", 0) \ K(TrcToken, "trcToken", 0) \ @@ -243,6 +244,7 @@ namespace solidity::langutil K(Alias, "alias", 0) \ K(Apply, "apply", 0) \ K(Auto, "auto", 0) \ + K(Byte, "byte", 0) \ K(Case, "case", 0) \ K(CopyOf, "copyof", 0) \ K(Default, "default", 0) \ @@ -268,9 +270,11 @@ namespace solidity::langutil K(Switch, "switch", 0) \ K(Typedef, "typedef", 0) \ K(TypeOf, "typeof", 0) \ - K(Unchecked, "unchecked", 0) \ K(Var, "var", 0) \ \ + /* Yul-specific tokens, but not keywords. */ \ + T(Leave, "leave", 0) \ + \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -318,7 +322,16 @@ namespace TokenTraits constexpr bool isEtherSubdenomination(Token op) { return op >= Token::SubWei && op <= Token::SubEther; } constexpr bool isTronSubdenomination(Token op) { return op == Token::SubTrx || op == Token::SubSun; } constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } - constexpr bool isReservedKeyword(Token op) { return (Token::After <= op && op <= Token::Unchecked); } + constexpr bool isReservedKeyword(Token op) { return (Token::After <= op && op <= Token::Var); } + + constexpr bool isYulKeyword(Token tok) + { + return tok == Token::Function || tok == Token::Let || tok == Token::If || tok == Token::Switch || tok == Token::Case || + tok == Token::Default || tok == Token::For || tok == Token::Break || tok == Token::Continue || tok == Token::Leave || + tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex; + } + + bool isYulKeyword(std::string const& _literal); inline Token AssignmentToBinaryOp(Token op) { @@ -328,7 +341,28 @@ namespace TokenTraits // @returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. - int precedence(Token tok); + constexpr int precedence(Token tok) + { + int8_t constexpr precs[TokenTraits::count()] = + { + #define T(name, string, precedence) precedence, + TOKEN_LIST(T, T) + #undef T + }; + return precs[static_cast(tok)]; + } + + constexpr bool hasExpHighestPrecedence() + { + constexpr int expPrecedence = TokenTraits::precedence(Token::Exp); + static_assert(expPrecedence == 14, "Exp precedence changed."); + + #define T(name, string, precedence) ((Token::name == Token::Exp) || precedence < expPrecedence) && + return + TOKEN_LIST(T, T) + true; + #undef T + } std::tuple fromIdentifierOrKeyword(std::string const& _literal); diff --git a/libsmtutil/CHCSmtLib2Interface.cpp b/libsmtutil/CHCSmtLib2Interface.cpp index cc30c127bb51..bbbc63bfe07a 100644 --- a/libsmtutil/CHCSmtLib2Interface.cpp +++ b/libsmtutil/CHCSmtLib2Interface.cpp @@ -37,10 +37,12 @@ using namespace solidity::smtutil; CHCSmtLib2Interface::CHCSmtLib2Interface( map const& _queryResponses, - ReadCallback::Callback const& _smtCallback + ReadCallback::Callback _smtCallback, + optional _queryTimeout ): - m_smtlib2(make_unique(_queryResponses, _smtCallback)), - m_queryResponses(_queryResponses), + CHCSolverInterface(_queryTimeout), + m_smtlib2(make_unique(_queryResponses, _smtCallback, m_queryTimeout)), + m_queryResponses(move(_queryResponses)), m_smtCallback(_smtCallback) { reset(); @@ -50,6 +52,9 @@ void CHCSmtLib2Interface::reset() { m_accumulatedOutput.clear(); m_variables.clear(); + m_unhandledQueries.clear(); + if (m_queryTimeout) + write("(set-option :timeout " + to_string(*m_queryTimeout) + ")"); } void CHCSmtLib2Interface::registerRelation(Expression const& _expr) diff --git a/libsmtutil/CHCSmtLib2Interface.h b/libsmtutil/CHCSmtLib2Interface.h index 7039431c41c5..17227867e91b 100644 --- a/libsmtutil/CHCSmtLib2Interface.h +++ b/libsmtutil/CHCSmtLib2Interface.h @@ -33,8 +33,9 @@ class CHCSmtLib2Interface: public CHCSolverInterface { public: explicit CHCSmtLib2Interface( - std::map const& _queryResponses, - frontend::ReadCallback::Callback const& _smtCallback + std::map const& _queryResponses = {}, + frontend::ReadCallback::Callback _smtCallback = {}, + std::optional _queryTimeout = {} ); void reset(); diff --git a/libsmtutil/CHCSolverInterface.h b/libsmtutil/CHCSolverInterface.h index 1fe279a8b082..2032553451a5 100644 --- a/libsmtutil/CHCSolverInterface.h +++ b/libsmtutil/CHCSolverInterface.h @@ -33,6 +33,8 @@ namespace solidity::smtutil class CHCSolverInterface { public: + CHCSolverInterface(std::optional _queryTimeout = {}): m_queryTimeout(_queryTimeout) {} + virtual ~CHCSolverInterface() = default; virtual void declareVariable(std::string const& _name, SortPointer const& _sort) = 0; @@ -44,9 +46,7 @@ class CHCSolverInterface /// Needs to bound all vars as universally quantified. virtual void addRule(Expression const& _expr, std::string const& _name) = 0; - /// first: predicate name - /// second: predicate arguments - using CexNode = std::pair>; + using CexNode = Expression; struct CexGraph { std::map nodes; @@ -58,6 +58,9 @@ class CHCSolverInterface virtual std::pair query( Expression const& _expr ) = 0; + +protected: + std::optional m_queryTimeout; }; } diff --git a/libsmtutil/CMakeLists.txt b/libsmtutil/CMakeLists.txt index 62b52f5c6f09..af7f47b60c04 100644 --- a/libsmtutil/CMakeLists.txt +++ b/libsmtutil/CMakeLists.txt @@ -9,6 +9,7 @@ set(sources SolverInterface.h Sorts.cpp Sorts.h + Helpers.h ) if (${Z3_FOUND}) @@ -23,10 +24,25 @@ else() set(cvc4_SRCS) endif() +if (${USE_Z3_DLOPEN}) + file(GLOB Z3_HEADERS ${Z3_HEADER_PATH}/z3*.h) + set(Z3_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/z3wrapper.cpp) + add_custom_command( + OUTPUT ${Z3_WRAPPER} + COMMAND ${Python3_EXECUTABLE} genz3wrapper.py ${Z3_HEADERS} > ${Z3_WRAPPER} + DEPENDS ${Z3_HEADERS} genz3wrapper.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + ) + set(z3_SRCS ${z3_SRCS} ${Z3_WRAPPER} Z3Loader.cpp Z3Loader.h) +endif() + add_library(smtutil ${sources} ${z3_SRCS} ${cvc4_SRCS}) target_link_libraries(smtutil PUBLIC solutil Boost::boost) -if (${Z3_FOUND}) +if (${USE_Z3_DLOPEN}) + target_include_directories(smtutil PUBLIC ${Z3_HEADER_PATH}) + target_link_libraries(smtutil PUBLIC ${CMAKE_DL_LIBS}) +elseif (${Z3_FOUND}) target_link_libraries(smtutil PUBLIC z3::libz3) endif() diff --git a/libsmtutil/CVC4Interface.cpp b/libsmtutil/CVC4Interface.cpp index 2a795d827586..daac2ab71119 100644 --- a/libsmtutil/CVC4Interface.cpp +++ b/libsmtutil/CVC4Interface.cpp @@ -27,7 +27,8 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::smtutil; -CVC4Interface::CVC4Interface(): +CVC4Interface::CVC4Interface(optional _queryTimeout): + SolverInterface(_queryTimeout), m_solver(&m_context) { reset(); @@ -38,7 +39,10 @@ void CVC4Interface::reset() m_variables.clear(); m_solver.reset(); m_solver.setOption("produce-models", true); - m_solver.setResourceLimit(resourceLimit); + if (m_queryTimeout) + m_solver.setTimeLimit(*m_queryTimeout); + else + m_solver.setResourceLimit(resourceLimit); } void CVC4Interface::push() @@ -188,12 +192,24 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr) return m_context.mkExpr(CVC4::kind::INTS_DIVISION_TOTAL, arguments[0], arguments[1]); else if (n == "mod") return m_context.mkExpr(CVC4::kind::INTS_MODULUS, arguments[0], arguments[1]); + else if (n == "bvnot") + return m_context.mkExpr(CVC4::kind::BITVECTOR_NOT, arguments[0]); else if (n == "bvand") return m_context.mkExpr(CVC4::kind::BITVECTOR_AND, arguments[0], arguments[1]); + else if (n == "bvor") + return m_context.mkExpr(CVC4::kind::BITVECTOR_OR, arguments[0], arguments[1]); + else if (n == "bvxor") + return m_context.mkExpr(CVC4::kind::BITVECTOR_XOR, arguments[0], arguments[1]); + else if (n == "bvshl") + return m_context.mkExpr(CVC4::kind::BITVECTOR_SHL, arguments[0], arguments[1]); + else if (n == "bvlshr") + return m_context.mkExpr(CVC4::kind::BITVECTOR_LSHR, arguments[0], arguments[1]); + else if (n == "bvashr") + return m_context.mkExpr(CVC4::kind::BITVECTOR_ASHR, arguments[0], arguments[1]); else if (n == "int2bv") { size_t size = std::stoul(_expr.arguments[1].name); - auto i2bvOp = m_context.mkConst(CVC4::IntToBitVector(size)); + auto i2bvOp = m_context.mkConst(CVC4::IntToBitVector(static_cast(size))); // CVC4 treats all BVs as unsigned, so we need to manually apply 2's complement if needed. return m_context.mkExpr( CVC4::kind::ITE, @@ -283,6 +299,8 @@ CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort) return m_context.booleanType(); case Kind::Int: return m_context.integerType(); + case Kind::BitVector: + return m_context.mkBitVectorType(dynamic_cast(_sort).size); case Kind::Function: { FunctionSort const& fSort = dynamic_cast(_sort); diff --git a/libsmtutil/CVC4Interface.h b/libsmtutil/CVC4Interface.h index 16f9f1f81860..5727be0e2986 100644 --- a/libsmtutil/CVC4Interface.h +++ b/libsmtutil/CVC4Interface.h @@ -40,7 +40,7 @@ namespace solidity::smtutil class CVC4Interface: public SolverInterface, public boost::noncopyable { public: - CVC4Interface(); + CVC4Interface(std::optional _queryTimeout = {}); void reset() override; diff --git a/libsmtutil/Helpers.h b/libsmtutil/Helpers.h new file mode 100644 index 000000000000..feeb9a7d583c --- /dev/null +++ b/libsmtutil/Helpers.h @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +namespace solidity::smtutil +{ + +/// Signed division in SMTLIB2 rounds differently than EVM. +/// This does not check for division by zero! +inline Expression signedDivisionEVM(Expression _left, Expression _right) +{ + return Expression::ite( + _left >= 0, + Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))), + Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right)) + ); +} + +inline Expression abs(Expression _value) +{ + return Expression::ite(_value >= 0, _value, 0 - _value); +} + +/// Signed modulo in SMTLIB2 behaves differently with regards +/// to the sign than EVM. +/// This does not check for modulo by zero! +inline Expression signedModuloEVM(Expression _left, Expression _right) +{ + return Expression::ite( + _left >= 0, + _left % _right, + Expression::ite( + (_left % _right) == 0, + 0, + (_left % _right) - abs(_right) + ) + ); +} + +} diff --git a/libsmtutil/SMTLib2Interface.cpp b/libsmtutil/SMTLib2Interface.cpp index f1d21c2cc668..e4ea165450a7 100644 --- a/libsmtutil/SMTLib2Interface.cpp +++ b/libsmtutil/SMTLib2Interface.cpp @@ -38,11 +38,13 @@ using namespace solidity::frontend; using namespace solidity::smtutil; SMTLib2Interface::SMTLib2Interface( - map const& _queryResponses, - ReadCallback::Callback _smtCallback + map _queryResponses, + ReadCallback::Callback _smtCallback, + optional _queryTimeout ): - m_queryResponses(_queryResponses), - m_smtCallback(std::move(_smtCallback)) + SolverInterface(_queryTimeout), + m_queryResponses(move(_queryResponses)), + m_smtCallback(move(_smtCallback)) { reset(); } @@ -54,6 +56,8 @@ void SMTLib2Interface::reset() m_variables.clear(); m_userSorts.clear(); write("(set-option :produce-models true)"); + if (m_queryTimeout) + write("(set-option :timeout " + to_string(*m_queryTimeout) + ")"); write("(set-logic ALL)"); } @@ -215,6 +219,8 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort) return "Int"; case Kind::Bool: return "Bool"; + case Kind::BitVector: + return "(_ BitVec " + to_string(dynamic_cast(_sort).size) + ")"; case Kind::Array: { auto const& arraySort = dynamic_cast(_sort); diff --git a/libsmtutil/SMTLib2Interface.h b/libsmtutil/SMTLib2Interface.h index 2a43c04418b5..104862f27c9d 100644 --- a/libsmtutil/SMTLib2Interface.h +++ b/libsmtutil/SMTLib2Interface.h @@ -39,8 +39,9 @@ class SMTLib2Interface: public SolverInterface, public boost::noncopyable { public: explicit SMTLib2Interface( - std::map const& _queryResponses, - frontend::ReadCallback::Callback _smtCallback + std::map _queryResponses = {}, + frontend::ReadCallback::Callback _smtCallback = {}, + std::optional _queryTimeout = {} ); void reset() override; @@ -77,7 +78,7 @@ class SMTLib2Interface: public SolverInterface, public boost::noncopyable std::map m_variables; std::set m_userSorts; - std::map const& m_queryResponses; + std::map m_queryResponses; std::vector m_unhandledQueries; frontend::ReadCallback::Callback m_smtCallback; diff --git a/libsmtutil/SMTPortfolio.cpp b/libsmtutil/SMTPortfolio.cpp index 334d91dd73b6..de00c1f79578 100644 --- a/libsmtutil/SMTPortfolio.cpp +++ b/libsmtutil/SMTPortfolio.cpp @@ -33,19 +33,21 @@ using namespace solidity::frontend; using namespace solidity::smtutil; SMTPortfolio::SMTPortfolio( - map const& _smtlib2Responses, - frontend::ReadCallback::Callback const& _smtCallback, - [[maybe_unused]] SMTSolverChoice _enabledSolvers -) + map _smtlib2Responses, + frontend::ReadCallback::Callback _smtCallback, + [[maybe_unused]] SMTSolverChoice _enabledSolvers, + optional _queryTimeout +): + SolverInterface(_queryTimeout) { - m_solvers.emplace_back(make_unique(_smtlib2Responses, _smtCallback)); + m_solvers.emplace_back(make_unique(move(_smtlib2Responses), move(_smtCallback), m_queryTimeout)); #ifdef HAVE_Z3 - if (_enabledSolvers.z3) - m_solvers.emplace_back(make_unique()); + if (_enabledSolvers.z3 && Z3Interface::available()) + m_solvers.emplace_back(make_unique(m_queryTimeout)); #endif #ifdef HAVE_CVC4 if (_enabledSolvers.cvc4) - m_solvers.emplace_back(make_unique()); + m_solvers.emplace_back(make_unique(m_queryTimeout)); #endif } diff --git a/libsmtutil/SMTPortfolio.h b/libsmtutil/SMTPortfolio.h index 3e933df5d133..aab92f99647b 100644 --- a/libsmtutil/SMTPortfolio.h +++ b/libsmtutil/SMTPortfolio.h @@ -40,9 +40,10 @@ class SMTPortfolio: public SolverInterface, public boost::noncopyable { public: SMTPortfolio( - std::map const& _smtlib2Responses, - frontend::ReadCallback::Callback const& _smtCallback, - SMTSolverChoice _enabledSolvers + std::map _smtlib2Responses = {}, + frontend::ReadCallback::Callback _smtCallback = {}, + SMTSolverChoice _enabledSolvers = SMTSolverChoice::All(), + std::optional _queryTimeout = {} ); void reset() override; @@ -57,7 +58,7 @@ class SMTPortfolio: public SolverInterface, public boost::noncopyable std::pair> check(std::vector const& _expressionsToEvaluate) override; std::vector unhandledQueries() override; - unsigned solvers() override { return m_solvers.size(); } + size_t solvers() override { return m_solvers.size(); } private: static bool solverAnswered(CheckResult result); diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index d4c4d7fefa8a..4e9cf22584df 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,8 @@ class Expression public: explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {} explicit Expression(std::shared_ptr _sort, std::string _name = ""): Expression(std::move(_name), {}, _sort) {} + explicit Expression(std::string _name, std::vector _arguments, SortPointer _sort): + name(std::move(_name)), arguments(std::move(_arguments)), sort(std::move(_sort)) {} Expression(size_t _number): Expression(std::to_string(_number), {}, SortProvider::sintSort) {} Expression(u256 const& _number): Expression(_number.str(), {}, SortProvider::sintSort) {} Expression(s256 const& _number): Expression(_number.str(), {}, SortProvider::sintSort) {} @@ -95,7 +98,13 @@ class Expression {"*", 2}, {"/", 2}, {"mod", 2}, + {"bvnot", 1}, {"bvand", 2}, + {"bvor", 2}, + {"bvxor", 2}, + {"bvshl", 2}, + {"bvlshr", 2}, + {"bvashr", 2}, {"int2bv", 2}, {"bv2int", 1}, {"select", 2}, @@ -227,14 +236,26 @@ class Expression friend Expression operator!(Expression _a) { + if (_a.sort->kind == Kind::BitVector) + return ~_a; return Expression("not", std::move(_a), Kind::Bool); } friend Expression operator&&(Expression _a, Expression _b) { + if (_a.sort->kind == Kind::BitVector) + { + smtAssert(_b.sort->kind == Kind::BitVector, ""); + return _a & _b; + } return Expression("and", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator||(Expression _a, Expression _b) { + if (_a.sort->kind == Kind::BitVector) + { + smtAssert(_b.sort->kind == Kind::BitVector, ""); + return _a | _b; + } return Expression("or", std::move(_a), std::move(_b), Kind::Bool); } friend Expression operator==(Expression _a, Expression _b) @@ -286,11 +307,41 @@ class Expression auto intSort = _a.sort; return Expression("mod", {std::move(_a), std::move(_b)}, intSort); } + friend Expression operator~(Expression _a) + { + auto bvSort = _a.sort; + return Expression("bvnot", {std::move(_a)}, bvSort); + } friend Expression operator&(Expression _a, Expression _b) { auto bvSort = _a.sort; return Expression("bvand", {std::move(_a), std::move(_b)}, bvSort); } + friend Expression operator|(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvor", {std::move(_a), std::move(_b)}, bvSort); + } + friend Expression operator^(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvxor", {std::move(_a), std::move(_b)}, bvSort); + } + friend Expression operator<<(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvshl", {std::move(_a), std::move(_b)}, bvSort); + } + friend Expression operator>>(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvlshr", {std::move(_a), std::move(_b)}, bvSort); + } + static Expression ashr(Expression _a, Expression _b) + { + auto bvSort = _a.sort; + return Expression("bvashr", {std::move(_a), std::move(_b)}, bvSort); + } Expression operator()(std::vector _arguments) const { smtAssert( @@ -308,8 +359,6 @@ class Expression private: /// Manual constructors, should only be used by SolverInterface and this class itself. - Expression(std::string _name, std::vector _arguments, SortPointer _sort): - name(std::move(_name)), arguments(std::move(_arguments)), sort(std::move(_sort)) {} Expression(std::string _name, std::vector _arguments, Kind _kind): Expression(std::move(_name), std::move(_arguments), std::make_shared(_kind)) {} @@ -326,6 +375,8 @@ DEV_SIMPLE_EXCEPTION(SolverError); class SolverInterface { public: + SolverInterface(std::optional _queryTimeout = {}): m_queryTimeout(_queryTimeout) {} + virtual ~SolverInterface() = default; virtual void reset() = 0; @@ -352,7 +403,10 @@ class SolverInterface virtual std::vector unhandledQueries() { return {}; } /// @returns how many SMT solvers this interface has. - virtual unsigned solvers() { return 1; } + virtual size_t solvers() { return 1; } + +protected: + std::optional m_queryTimeout; }; } diff --git a/libsmtutil/Sorts.cpp b/libsmtutil/Sorts.cpp index 303e8bb8448a..543c7ba0f840 100644 --- a/libsmtutil/Sorts.cpp +++ b/libsmtutil/Sorts.cpp @@ -35,4 +35,6 @@ shared_ptr SortProvider::intSort(bool _signed) return uintSort; } +shared_ptr const SortProvider::bitVectorSort{make_shared(256)}; + } diff --git a/libsmtutil/Sorts.h b/libsmtutil/Sorts.h index de94e8fff6bc..45668f269908 100644 --- a/libsmtutil/Sorts.h +++ b/libsmtutil/Sorts.h @@ -195,6 +195,7 @@ struct SortProvider static std::shared_ptr const uintSort; static std::shared_ptr const sintSort; static std::shared_ptr intSort(bool _signed = false); + static std::shared_ptr const bitVectorSort; }; } diff --git a/libsmtutil/Z3CHCInterface.cpp b/libsmtutil/Z3CHCInterface.cpp index 9ea15562124d..1196b27ff87b 100644 --- a/libsmtutil/Z3CHCInterface.cpp +++ b/libsmtutil/Z3CHCInterface.cpp @@ -27,14 +27,26 @@ using namespace std; using namespace solidity; using namespace solidity::smtutil; -Z3CHCInterface::Z3CHCInterface(): - m_z3Interface(make_unique()), +Z3CHCInterface::Z3CHCInterface(optional _queryTimeout): + CHCSolverInterface(_queryTimeout), + m_z3Interface(make_unique(m_queryTimeout)), m_context(m_z3Interface->context()), m_solver(*m_context) { + Z3_get_version( + &get<0>(m_version), + &get<1>(m_version), + &get<2>(m_version), + &get<3>(m_version) + ); + // These need to be set globally. z3::set_param("rewriter.pull_cheap_ite", true); - z3::set_param("rlimit", Z3Interface::resourceLimit); + + if (m_queryTimeout) + m_context->set("timeout", int(*m_queryTimeout)); + else + z3::set_param("rlimit", Z3Interface::resourceLimit); setSpacerOptions(); } @@ -68,7 +80,6 @@ void Z3CHCInterface::addRule(Expression const& _expr, string const& _name) pair Z3CHCInterface::query(Expression const& _expr) { CheckResult result; - CHCSolverInterface::CexGraph cex; try { z3::expr z3Expr = m_z3Interface->toZ3Expr(_expr); @@ -77,9 +88,14 @@ pair Z3CHCInterface::query(Expression case z3::check_result::sat: { result = CheckResult::SATISFIABLE; - auto proof = m_solver.get_answer(); - auto cex = cexGraph(proof); - return {result, cex}; + // z3 version 4.8.8 modified Spacer to also return + // proofs containing nonlinear clauses. + if (m_version >= tuple(4, 8, 8, 0)) + { + auto proof = m_solver.get_answer(); + return {result, cexGraph(proof)}; + } + break; } case z3::check_result::unsat: { @@ -95,13 +111,21 @@ pair Z3CHCInterface::query(Expression } // TODO retrieve model / invariants } - catch (z3::exception const&) + catch (z3::exception const& _err) { - result = CheckResult::ERROR; - cex = {}; + set msgs{ + /// Resource limit (rlimit) exhausted. + "max. resource limit exceeded", + /// User given timeout exhausted. + "canceled" + }; + if (msgs.count(_err.msg())) + result = CheckResult::UNKNOWN; + else + result = CheckResult::ERROR; } - return {result, cex}; + return {result, {}}; } void Z3CHCInterface::setSpacerOptions(bool _preProcessing) @@ -142,20 +166,23 @@ instead of a path. */ CHCSolverInterface::CexGraph Z3CHCInterface::cexGraph(z3::expr const& _proof) { - CexGraph graph; - /// The root fact of the refutation proof is `false`. /// The node itself is not a hyper resolution, so we need to /// extract the `query` hyper resolution node from the /// `false` node (the first child). - smtAssert(_proof.is_app(), ""); - smtAssert(fact(_proof).decl().decl_kind() == Z3_OP_FALSE, ""); + /// The proof has the shape above for z3 >=4.8.8. + /// If an older version is used, this check will fail and no + /// counterexample will be generated. + if (!_proof.is_app() || fact(_proof).decl().decl_kind() != Z3_OP_FALSE) + return {}; + + CexGraph graph; stack proofStack; proofStack.push(_proof.arg(0)); auto const& root = proofStack.top(); - graph.nodes[root.id()] = {name(fact(root)), arguments(fact(root))}; + graph.nodes.emplace(root.id(), m_z3Interface->fromZ3Expr(fact(root))); set visited; visited.insert(root.id()); @@ -180,7 +207,7 @@ CHCSolverInterface::CexGraph Z3CHCInterface::cexGraph(z3::expr const& _proof) if (!graph.nodes.count(child.id())) { - graph.nodes[child.id()] = {name(fact(child)), arguments(fact(child))}; + graph.nodes.emplace(child.id(), m_z3Interface->fromZ3Expr(fact(child))); graph.edges[child.id()] = {}; } diff --git a/libsmtutil/Z3CHCInterface.h b/libsmtutil/Z3CHCInterface.h index 00e4ca2852f8..a0e4bfa671a5 100644 --- a/libsmtutil/Z3CHCInterface.h +++ b/libsmtutil/Z3CHCInterface.h @@ -25,6 +25,7 @@ #include #include +#include #include namespace solidity::smtutil @@ -33,7 +34,7 @@ namespace solidity::smtutil class Z3CHCInterface: public CHCSolverInterface { public: - Z3CHCInterface(); + Z3CHCInterface(std::optional _queryTimeout = {}); /// Forwards variable declaration to Z3Interface. void declareVariable(std::string const& _name, SortPointer const& _sort) override; @@ -64,6 +65,8 @@ class Z3CHCInterface: public CHCSolverInterface z3::context* m_context; // Horn solver. z3::fixedpoint m_solver; + + std::tuple m_version = std::tuple(0, 0, 0, 0); }; } diff --git a/libsmtutil/Z3Interface.cpp b/libsmtutil/Z3Interface.cpp index 501446553bf5..8d7a03392a26 100644 --- a/libsmtutil/Z3Interface.cpp +++ b/libsmtutil/Z3Interface.cpp @@ -18,17 +18,37 @@ #include +#include #include +#ifdef HAVE_Z3_DLOPEN +#include +#endif + using namespace std; using namespace solidity::smtutil; +using namespace solidity::util; -Z3Interface::Z3Interface(): +bool Z3Interface::available() +{ +#ifdef HAVE_Z3_DLOPEN + return Z3Loader::get().available(); +#else + return true; +#endif +} + +Z3Interface::Z3Interface(std::optional _queryTimeout): + SolverInterface(_queryTimeout), m_solver(m_context) { // These need to be set globally. z3::set_param("rewriter.pull_cheap_ite", true); - z3::set_param("rlimit", resourceLimit); + + if (m_queryTimeout) + m_context.set("timeout", int(*m_queryTimeout)); + else + z3::set_param("rlimit", resourceLimit); } void Z3Interface::reset() @@ -100,9 +120,19 @@ pair> Z3Interface::check(vector const& _ values.push_back(util::toString(m.eval(toZ3Expr(e)))); } } - catch (z3::exception const&) + catch (z3::exception const& _err) { - result = CheckResult::ERROR; + set msgs{ + /// Resource limit (rlimit) exhausted. + "max. resource limit exceeded", + /// User given timeout exhausted. + "canceled" + }; + + if (msgs.count(_err.msg())) + result = CheckResult::UNKNOWN; + else + result = CheckResult::ERROR; values.clear(); } @@ -181,12 +211,24 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return arguments[0] / arguments[1]; else if (n == "mod") return z3::mod(arguments[0], arguments[1]); + else if (n == "bvnot") + return ~arguments[0]; else if (n == "bvand") return arguments[0] & arguments[1]; + else if (n == "bvor") + return arguments[0] | arguments[1]; + else if (n == "bvxor") + return arguments[0] ^ arguments[1]; + else if (n == "bvshl") + return z3::shl(arguments[0], arguments[1]); + else if (n == "bvlshr") + return z3::lshr(arguments[0], arguments[1]); + else if (n == "bvashr") + return z3::ashr(arguments[0], arguments[1]); else if (n == "int2bv") { size_t size = std::stoul(_expr.arguments[1].name); - return z3::int2bv(size, arguments[0]); + return z3::int2bv(static_cast(size), arguments[0]); } else if (n == "bv2int") { @@ -209,7 +251,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) else if (n == "tuple_get") { size_t index = stoul(_expr.arguments[1].name); - return z3::func_decl(m_context, Z3_get_tuple_sort_field_decl(m_context, z3Sort(*_expr.arguments[0].sort), index))(arguments[0]); + return z3::func_decl(m_context, Z3_get_tuple_sort_field_decl(m_context, z3Sort(*_expr.arguments[0].sort), static_cast(index)))(arguments[0]); } else if (n == "tuple_constructor") { @@ -231,6 +273,82 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) smtAssert(false, ""); } +Expression Z3Interface::fromZ3Expr(z3::expr const& _expr) +{ + auto sort = fromZ3Sort(_expr.get_sort()); + if (_expr.is_const() || _expr.is_var()) + return Expression(_expr.to_string(), {}, sort); + + smtAssert(_expr.is_app(), ""); + vector arguments; + for (unsigned i = 0; i < _expr.num_args(); ++i) + arguments.push_back(fromZ3Expr(_expr.arg(i))); + + auto kind = _expr.decl().decl_kind(); + if (_expr.is_ite()) + return Expression::ite(arguments[0], arguments[1], arguments[2]); + else if (_expr.is_not()) + return !arguments[0]; + else if (_expr.is_and()) + return arguments[0] && arguments[1]; + else if (_expr.is_or()) + return arguments[0] || arguments[1]; + else if (_expr.is_implies()) + return Expression::implies(arguments[0], arguments[1]); + else if (_expr.is_eq()) + return arguments[0] == arguments[1]; + else if (kind == Z3_OP_ULT || kind == Z3_OP_SLT) + return arguments[0] < arguments[1]; + else if (kind == Z3_OP_ULEQ || kind == Z3_OP_SLEQ) + return arguments[0] <= arguments[1]; + else if (kind == Z3_OP_GT || kind == Z3_OP_SGT) + return arguments[0] > arguments[1]; + else if (kind == Z3_OP_UGEQ || kind == Z3_OP_SGEQ) + return arguments[0] >= arguments[1]; + else if (kind == Z3_OP_ADD) + return arguments[0] + arguments[1]; + else if (kind == Z3_OP_SUB) + return arguments[0] - arguments[1]; + else if (kind == Z3_OP_MUL) + return arguments[0] * arguments[1]; + else if (kind == Z3_OP_DIV) + return arguments[0] / arguments[1]; + else if (kind == Z3_OP_MOD) + return arguments[0] % arguments[1]; + else if (kind == Z3_OP_XOR) + return arguments[0] ^ arguments[1]; + else if (kind == Z3_OP_BSHL) + return arguments[0] << arguments[1]; + else if (kind == Z3_OP_BLSHR) + return arguments[0] >> arguments[1]; + else if (kind == Z3_OP_BASHR) + return Expression::ashr(arguments[0], arguments[1]); + else if (kind == Z3_OP_INT2BV) + smtAssert(false, ""); + else if (kind == Z3_OP_BV2INT) + smtAssert(false, ""); + else if (kind == Z3_OP_SELECT) + return Expression::select(arguments[0], arguments[1]); + else if (kind == Z3_OP_STORE) + return Expression::store(arguments[0], arguments[1], arguments[2]); + else if (kind == Z3_OP_CONST_ARRAY) + { + auto sortSort = make_shared(fromZ3Sort(_expr.get_sort())); + return Expression::const_array(Expression(sortSort), arguments[0]); + } + else if (kind == Z3_OP_DT_CONSTRUCTOR) + { + auto sortSort = make_shared(fromZ3Sort(_expr.get_sort())); + return Expression::tuple_constructor(Expression(sortSort), arguments); + } + else if (kind == Z3_OP_DT_ACCESSOR) + smtAssert(false, ""); + else if (kind == Z3_OP_UNINTERPRETED) + return Expression(_expr.decl().name().str(), arguments, fromZ3Sort(_expr.get_sort())); + + smtAssert(false, ""); +} + z3::sort Z3Interface::z3Sort(Sort const& _sort) { switch (_sort.kind) @@ -239,6 +357,8 @@ z3::sort Z3Interface::z3Sort(Sort const& _sort) return m_context.bool_sort(); case Kind::Int: return m_context.int_sort(); + case Kind::BitVector: + return m_context.bv_sort(dynamic_cast(_sort).size); case Kind::Array: { auto const& arraySort = dynamic_cast(_sort); @@ -258,7 +378,7 @@ z3::sort Z3Interface::z3Sort(Sort const& _sort) z3::func_decl_vector projs(m_context); z3::func_decl tupleConstructor = m_context.tuple_sort( tupleSort.name.c_str(), - tupleSort.members.size(), + static_cast(tupleSort.members.size()), cMembers.data(), sorts.data(), projs @@ -281,3 +401,35 @@ z3::sort_vector Z3Interface::z3Sort(vector const& _sorts) z3Sorts.push_back(z3Sort(*_sort)); return z3Sorts; } + +SortPointer Z3Interface::fromZ3Sort(z3::sort const& _sort) +{ + if (_sort.is_bool()) + return SortProvider::boolSort; + if (_sort.is_int()) + return SortProvider::sintSort; + if (_sort.is_bv()) + return make_shared(_sort.bv_size()); + if (_sort.is_array()) + return make_shared(fromZ3Sort(_sort.array_domain()), fromZ3Sort(_sort.array_range())); + if (_sort.is_datatype()) + { + auto name = _sort.name().str(); + auto constructor = z3::func_decl(m_context, Z3_get_tuple_sort_mk_decl(m_context, _sort)); + vector memberNames; + vector memberSorts; + for (unsigned i = 0; i < constructor.arity(); ++i) + { + auto accessor = z3::func_decl(m_context, Z3_get_tuple_sort_field_decl(m_context, _sort, i)); + memberNames.push_back(accessor.name().str()); + memberSorts.push_back(fromZ3Sort(accessor.range())); + } + return make_shared(name, memberNames, memberSorts); + } + smtAssert(false, ""); +} + +vector Z3Interface::fromZ3Sort(z3::sort_vector const& _sorts) +{ + return applyMap(_sorts, [this](auto const& sort) { return fromZ3Sort(sort); }); +} diff --git a/libsmtutil/Z3Interface.h b/libsmtutil/Z3Interface.h index 22f79333785a..d565b62f19db 100644 --- a/libsmtutil/Z3Interface.h +++ b/libsmtutil/Z3Interface.h @@ -28,7 +28,9 @@ namespace solidity::smtutil class Z3Interface: public SolverInterface, public boost::noncopyable { public: - Z3Interface(); + Z3Interface(std::optional _queryTimeout = {}); + + static bool available(); void reset() override; @@ -41,6 +43,7 @@ class Z3Interface: public SolverInterface, public boost::noncopyable std::pair> check(std::vector const& _expressionsToEvaluate) override; z3::expr toZ3Expr(Expression const& _expr); + smtutil::Expression fromZ3Expr(z3::expr const& _expr); std::map constants() const { return m_constants; } std::map functions() const { return m_functions; } @@ -49,15 +52,15 @@ class Z3Interface: public SolverInterface, public boost::noncopyable // Z3 "basic resources" limit. // This is used to make the runs more deterministic and platform/machine independent. - // The tests start failing for Z3 with less than 10000000, - // so using double that. - static int const resourceLimit = 20000000; + static int const resourceLimit = 1000000; private: void declareFunction(std::string const& _name, Sort const& _sort); z3::sort z3Sort(Sort const& _sort); z3::sort_vector z3Sort(std::vector const& _sorts); + smtutil::SortPointer fromZ3Sort(z3::sort const& _sort); + std::vector fromZ3Sort(z3::sort_vector const& _sorts); z3::context m_context; z3::solver m_solver; diff --git a/libsmtutil/Z3Loader.cpp b/libsmtutil/Z3Loader.cpp new file mode 100644 index 000000000000..86b17aa0300b --- /dev/null +++ b/libsmtutil/Z3Loader.cpp @@ -0,0 +1,70 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include +#include +#include + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include + +using namespace std; +using namespace solidity; +using namespace solidity::smtutil; + +Z3Loader const& Z3Loader::get() +{ + static Z3Loader z3; + return z3; +} + +void* Z3Loader::loadSymbol(char const* _name) const +{ + smtAssert(m_handle, "Attempted to use dynamically loaded Z3, even though it is not available."); + void* sym = dlsym(m_handle, _name); + smtAssert(sym, string("Symbol \"") + _name + "\" not found in libz3.so"); + return sym; +} + +bool Z3Loader::available() const +{ + if (m_handle == nullptr) + return false; + unsigned major = 0; + unsigned minor = 0; + unsigned build = 0; + unsigned rev = 0; + Z3_get_version(&major, &minor, &build, &rev); + return major == Z3_MAJOR_VERSION && minor == Z3_MINOR_VERSION; +} + +Z3Loader::Z3Loader() +{ + string libname{"libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION)}; + m_handle = dlmopen(LM_ID_NEWLM, libname.c_str(), RTLD_NOW); +} + +Z3Loader::~Z3Loader() +{ + if (m_handle) + dlclose(m_handle); +} diff --git a/libsmtutil/Z3Loader.h b/libsmtutil/Z3Loader.h new file mode 100644 index 000000000000..5e755cb24b0a --- /dev/null +++ b/libsmtutil/Z3Loader.h @@ -0,0 +1,38 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +namespace solidity::smtutil +{ + +class Z3Loader +{ +public: + Z3Loader(Z3Loader const&) = delete; + Z3Loader& operator=(Z3Loader const&) = delete; + static Z3Loader const& get(); + void* loadSymbol(char const* _name) const; + bool available() const; +private: + Z3Loader(); + ~Z3Loader(); + void* m_handle = nullptr; +}; + +} \ No newline at end of file diff --git a/libsmtutil/genz3wrapper.py b/libsmtutil/genz3wrapper.py new file mode 100755 index 000000000000..9b24b32dbfe1 --- /dev/null +++ b/libsmtutil/genz3wrapper.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +#------------------------------------------------------------------------------ +# +# Script that generates a dlsym-wrapper for Z3 from the header files. +# Expects all Z3 headers as arguments and outputs the wrapper code to stdout. + +import sys +import re + +# Patterns to match Z3 API entry point definitions. +def_pat = re.compile(" *def_API(.*)") +extradef_pat = re.compile(" *extra_API(.*)") +# Pattern to extract name and arguments from the above. +def_args_pat = re.compile("\('([^']*)'[^\(\)]*\((.*)\)\s*\)") +# Pattern to extract a list of arguments from the above. +arg_list_pat = re.compile("[^\(]*\([^\)]*\)[, ]*") + +def generateEntryPoint(line, args): + m = def_args_pat.match(args) + if not m: + raise Exception('Could not parse entry point definition: ' + line) + name = m.group(1) + num_args = len(arg_list_pat.findall(m.group(2))) + arglist = ', '.join(f"_{i}" for i in range(num_args)) + paramlist = ', '.join(f"ArgType<&{name}, {i}> _{i}" for i in range(num_args)) + print(f'ResultType<&{name}> Z3_API {name}({paramlist})') + print('{') + print(f'\tstatic auto sym = reinterpret_cast(Z3Loader::get().loadSymbol(\"{name}\"));') + print(f'\treturn sym({arglist});') + print('}') + + +print(r"""// This file is auto-generated from genz3wrapper.py +#include +#include +#include + +namespace +{ + +template +struct FunctionTrait; + +template +struct FunctionTrait +{ + using ResultType = R; + template + using ArgType = std::tuple_element_t>; +}; + +template +using ResultType = typename FunctionTrait::ResultType; + +template +using ArgType = typename FunctionTrait::template ArgType; + +} + +using namespace solidity; +using namespace solidity::smtutil; + +extern "C" +{ + +void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h) +{ + static auto sym = reinterpret_cast(Z3Loader::get().loadSymbol("Z3_set_error_handler")); + sym(c, h); +} +""") + +for header in sys.argv[1:]: + with open(header, 'r') as f: + for line in f: + line = line.strip('\r\n\t ') + m = def_pat.match(line) + if m: + generateEntryPoint(line, m.group(1).strip('\r\n\t ')) + m = extradef_pat.match(line) + if m: + generateEntryPoint(line, m.group(1).strip('\r\n\t ')) + +print('}') diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 74f4a9a21e02..298b11074bf2 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -30,6 +30,8 @@ set(sources analysis/PostTypeChecker.h analysis/ReferencesResolver.cpp analysis/ReferencesResolver.h + analysis/Scoper.cpp + analysis/Scoper.h analysis/StaticAnalyzer.cpp analysis/StaticAnalyzer.h analysis/SyntaxChecker.cpp @@ -45,8 +47,6 @@ set(sources ast/ASTAnnotations.h ast/ASTEnums.h ast/ASTForward.h - ast/AsmJsonImporter.cpp - ast/AsmJsonImporter.h ast/ASTJsonConverter.cpp ast/ASTJsonConverter.h ast/ASTUtils.cpp @@ -92,6 +92,8 @@ set(sources codegen/ir/IRLValue.h codegen/ir/IRVariable.cpp codegen/ir/IRVariable.h + formal/ArraySlicePredicate.cpp + formal/ArraySlicePredicate.h formal/BMC.cpp formal/BMC.h formal/CHC.cpp @@ -100,6 +102,12 @@ set(sources formal/EncodingContext.h formal/ModelChecker.cpp formal/ModelChecker.h + formal/Predicate.cpp + formal/Predicate.h + formal/PredicateInstance.cpp + formal/PredicateInstance.h + formal/PredicateSort.cpp + formal/PredicateSort.h formal/SMTEncoder.cpp formal/SMTEncoder.h formal/SSAVariable.cpp @@ -138,4 +146,3 @@ set(sources add_library(solidity ${sources}) target_link_libraries(solidity PUBLIC yul evmasm langutil smtutil solutil Boost::boost) - diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index d00affb91a9b..c3b4b9a7f503 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -32,85 +32,363 @@ using namespace solidity; using namespace solidity::frontend; using namespace solidity::langutil; -void ConstantEvaluator::endVisit(UnaryOperation const& _operation) +using TypedRational = ConstantEvaluator::TypedRational; + +namespace +{ + +/// Check whether (_base ** _exp) fits into 4096 bits. +bool fitsPrecisionExp(bigint const& _base, bigint const& _exp) { - auto sub = type(_operation.subExpression()); - if (sub) - setType(_operation, sub->unaryOperatorResult(_operation.getOperator())); + if (_base == 0) + return true; + + solAssert(_base > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantBaseBit = boost::multiprecision::msb(_base); + if (mostSignificantBaseBit == 0) // _base == 1 + return true; + if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096 + return false; + + bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1); + + return bitsNeeded <= bitsMax; } -void ConstantEvaluator::endVisit(BinaryOperation const& _operation) +/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) { - auto left = type(_operation.leftExpression()); - auto right = type(_operation.rightExpression()); - if (left && right) + return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); +} + +} + +optional ConstantEvaluator::evaluateBinaryOperator(Token _operator, rational const& _left, rational const& _right) +{ + bool fractional = _left.denominator() != 1 || _right.denominator() != 1; + switch (_operator) { - TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right); - if (!commonType) - m_errorReporter.fatalTypeError( - 6020_error, - _operation.location(), - "Operator " + - string(TokenTraits::toString(_operation.getOperator())) + - " not compatible with types " + - left->toString() + - " and " + - right->toString() - ); - setType( - _operation, - TokenTraits::isCompareOp(_operation.getOperator()) ? - TypeProvider::boolean() : - commonType - ); + //bit operations will only be enabled for integers and fixed types that resemble integers + case Token::BitOr: + if (fractional) + return nullopt; + else + return _left.numerator() | _right.numerator(); + case Token::BitXor: + if (fractional) + return nullopt; + else + return _left.numerator() ^ _right.numerator(); + case Token::BitAnd: + if (fractional) + return nullopt; + else + return _left.numerator() & _right.numerator(); + case Token::Add: return _left + _right; + case Token::Sub: return _left - _right; + case Token::Mul: return _left * _right; + case Token::Div: + if (_right == rational(0)) + return nullopt; + else + return _left / _right; + case Token::Mod: + if (_right == rational(0)) + return nullopt; + else if (fractional) + { + rational tempValue = _left / _right; + return _left - (tempValue.numerator() / tempValue.denominator()) * _right; + } + else + return _left.numerator() % _right.numerator(); + break; + case Token::Exp: + { + if (_right.denominator() != 1) + return nullopt; + bigint const& exp = _right.numerator(); + + // x ** 0 = 1 + // for 0, 1 and -1 the size of the exponent doesn't have to be restricted + if (exp == 0) + return 1; + else if (_left == 0 || _left == 1) + return _left; + else if (_left == -1) + { + bigint isOdd = abs(exp) & bigint(1); + return 1 - 2 * isOdd.convert_to(); + } + else + { + if (abs(exp) > numeric_limits::max()) + return nullopt; // This will need too much memory to represent. + + uint32_t absExp = bigint(abs(exp)).convert_to(); + + if (!fitsPrecisionExp(abs(_left.numerator()), absExp) || !fitsPrecisionExp(abs(_left.denominator()), absExp)) + return nullopt; + + static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint { + if (_base == 1) + return 1; + else if (_base == -1) + return 1 - 2 * static_cast(_exponent & 1); + else + return boost::multiprecision::pow(_base, _exponent); + }; + + bigint numerator = optimizedPow(_left.numerator(), absExp); + bigint denominator = optimizedPow(_left.denominator(), absExp); + + if (exp >= 0) + return makeRational(numerator, denominator); + else + // invert + return makeRational(denominator, numerator); + } + break; + } + case Token::SHL: + { + if (fractional) + return nullopt; + else if (_right < 0) + return nullopt; + else if (_right > numeric_limits::max()) + return nullopt; + if (_left.numerator() == 0) + return 0; + else + { + uint32_t exponent = _right.numerator().convert_to(); + if (!fitsPrecisionBase2(abs(_left.numerator()), exponent)) + return nullopt; + return _left.numerator() * boost::multiprecision::pow(bigint(2), exponent); + } + break; + } + // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue + // determines the resulting type and the type of shift (SAR or SHR). + case Token::SAR: + { + if (fractional) + return nullopt; + else if (_right < 0) + return nullopt; + else if (_right > numeric_limits::max()) + return nullopt; + if (_left.numerator() == 0) + return 0; + else + { + uint32_t exponent = _right.numerator().convert_to(); + if (exponent > boost::multiprecision::msb(boost::multiprecision::abs(_left.numerator()))) + return _left.numerator() < 0 ? -1 : 0; + else + { + if (_left.numerator() < 0) + // Add 1 to the negative value before dividing to get a result that is strictly too large, + // then subtract 1 afterwards to round towards negative infinity. + // This is the same algorithm as used in ExpressionCompiler::appendShiftOperatorCode(...). + // To see this note that for negative x, xor(x,all_ones) = (-x-1) and + // therefore xor(div(xor(x,all_ones), exp(2, shift_amount)), all_ones) is + // -(-x - 1) / 2^shift_amount - 1, which is the same as + // (x + 1) / 2^shift_amount - 1. + return rational((_left.numerator() + 1) / boost::multiprecision::pow(bigint(2), exponent) - bigint(1), 1); + else + return rational(_left.numerator() / boost::multiprecision::pow(bigint(2), exponent), 1); + } + } + break; + } + default: + return nullopt; } } -void ConstantEvaluator::endVisit(Literal const& _literal) +optional ConstantEvaluator::evaluateUnaryOperator(Token _operator, rational const& _input) { - setType(_literal, TypeProvider::forLiteral(_literal)); + switch (_operator) + { + case Token::BitNot: + if (_input.denominator() != 1) + return nullopt; + else + return ~_input.numerator(); + case Token::Sub: + return -_input; + default: + return nullopt; + } } -void ConstantEvaluator::endVisit(Identifier const& _identifier) +namespace { - VariableDeclaration const* variableDeclaration = dynamic_cast(_identifier.annotation().referencedDeclaration); - if (!variableDeclaration) - return; - if (!variableDeclaration->isConstant()) + +optional convertType(rational const& _value, Type const& _type) +{ + if (_type.category() == Type::Category::RationalNumber) + return TypedRational{TypeProvider::rationalNumber(_value), _value}; + else if (auto const* integerType = dynamic_cast(&_type)) + { + if (_value > integerType->maxValue() || _value < integerType->minValue()) + return nullopt; + else + return TypedRational{&_type, _value.numerator() / _value.denominator()}; + } + else + return nullopt; +} + +optional convertType(optional const& _value, Type const& _type) +{ + return _value ? convertType(_value->value, _type) : nullopt; +} + +optional constantToTypedValue(Type const& _type) +{ + if (_type.category() == Type::Category::RationalNumber) + return TypedRational{&_type, dynamic_cast(_type).value()}; + else + return nullopt; +} + +} + +optional ConstantEvaluator::evaluate( + langutil::ErrorReporter& _errorReporter, + Expression const& _expr +) +{ + return ConstantEvaluator{_errorReporter}.evaluate(_expr); +} + + +optional ConstantEvaluator::evaluate(ASTNode const& _node) +{ + if (!m_values.count(&_node)) + { + if (auto const* varDecl = dynamic_cast(&_node)) + { + solAssert(varDecl->isConstant(), ""); + // In some circumstances, we do not yet have a type for the variable. + if (!varDecl->value() || !varDecl->type()) + m_values[&_node] = nullopt; + else + { + m_depth++; + if (m_depth > 32) + m_errorReporter.fatalTypeError( + 5210_error, + varDecl->location(), + "Cyclic constant definition (or maximum recursion depth exhausted)." + ); + m_values[&_node] = convertType(evaluate(*varDecl->value()), *varDecl->type()); + m_depth--; + } + } + else if (auto const* expression = dynamic_cast(&_node)) + { + expression->accept(*this); + if (!m_values.count(&_node)) + m_values[&_node] = nullopt; + } + } + return m_values.at(&_node); +} + +void ConstantEvaluator::endVisit(UnaryOperation const& _operation) +{ + optional value = evaluate(_operation.subExpression()); + if (!value) return; - ASTPointer const& value = variableDeclaration->value(); + TypePointer resultType = value->type->unaryOperatorResult(_operation.getOperator()); + if (!resultType) + return; + value = convertType(value, *resultType); if (!value) return; - else if (!m_types->count(value.get())) + + if (optional result = evaluateUnaryOperator(_operation.getOperator(), value->value)) { - if (m_depth > 32) - m_errorReporter.fatalTypeError(5210_error, _identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted)."); - ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value); + optional convertedValue = convertType(*result, *resultType); + if (!convertedValue) + m_errorReporter.fatalTypeError( + 3667_error, + _operation.location(), + "Arithmetic error when computing constant value." + ); + m_values[&_operation] = convertedValue; } - - setType(_identifier, type(*value)); } -void ConstantEvaluator::endVisit(TupleExpression const& _tuple) +void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { - if (!_tuple.isInlineArray() && _tuple.components().size() == 1) - setType(_tuple, type(*_tuple.components().front())); + optional left = evaluate(_operation.leftExpression()); + optional right = evaluate(_operation.rightExpression()); + if (!left || !right) + return; + + // If this is implemented in the future: Comparison operators have a "binaryOperatorResult" + // that is non-bool, but the result has to be bool. + if (TokenTraits::isCompareOp(_operation.getOperator())) + return; + + TypePointer resultType = left->type->binaryOperatorResult(_operation.getOperator(), right->type); + if (!resultType) + { + m_errorReporter.fatalTypeError( + 6020_error, + _operation.location(), + "Operator " + + string(TokenTraits::toString(_operation.getOperator())) + + " not compatible with types " + + left->type->toString() + + " and " + + right->type->toString() + ); + return; + } + + left = convertType(left, *resultType); + right = convertType(right, *resultType); + if (!left || !right) + return; + + if (optional value = evaluateBinaryOperator(_operation.getOperator(), left->value, right->value)) + { + optional convertedValue = convertType(*value, *resultType); + if (!convertedValue) + m_errorReporter.fatalTypeError( + 2643_error, + _operation.location(), + "Arithmetic error when computing constant value." + ); + m_values[&_operation] = convertedValue; + } } -void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type) +void ConstantEvaluator::endVisit(Literal const& _literal) { - if (_type && _type->category() == Type::Category::RationalNumber) - (*m_types)[&_node] = _type; + if (Type const* literalType = TypeProvider::forLiteral(_literal)) + m_values[&_literal] = constantToTypedValue(*literalType); } -TypePointer ConstantEvaluator::type(ASTNode const& _node) +void ConstantEvaluator::endVisit(Identifier const& _identifier) { - return (*m_types)[&_node]; + VariableDeclaration const* variableDeclaration = dynamic_cast(_identifier.annotation().referencedDeclaration); + if (variableDeclaration && variableDeclaration->isConstant()) + m_values[&_identifier] = evaluate(*variableDeclaration); } -TypePointer ConstantEvaluator::evaluate(Expression const& _expr) +void ConstantEvaluator::endVisit(TupleExpression const& _tuple) { - _expr.accept(*this); - return type(_expr); + if (!_tuple.isInlineArray() && _tuple.components().size() == 1) + m_values[&_tuple] = evaluate(*_tuple.components().front()); } diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 521f463384ad..7172258c3a22 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -39,37 +39,48 @@ class TypeChecker; /** * Small drop-in replacement for TypeChecker to evaluate simple expressions of integer constants. + * + * Note: This always use "checked arithmetic" in the sense that any over- or underflow + * results in "unknown" value. */ class ConstantEvaluator: private ASTConstVisitor { public: - ConstantEvaluator( - langutil::ErrorReporter& _errorReporter, - size_t _newDepth = 0, - std::shared_ptr> _types = std::make_shared>() - ): - m_errorReporter(_errorReporter), - m_depth(_newDepth), - m_types(std::move(_types)) + struct TypedRational { - } + TypePointer type; + rational value; + }; + + static std::optional evaluate( + langutil::ErrorReporter& _errorReporter, + Expression const& _expr + ); + + /// Performs arbitrary-precision evaluation of a binary operator. Returns nullopt on cases like + /// division by zero or e.g. bit operators applied to fractional values. + static std::optional evaluateBinaryOperator(Token _operator, rational const& _left, rational const& _right); - TypePointer evaluate(Expression const& _expr); + /// Performs arbitrary-precision evaluation of a unary operator. Returns nullopt on cases like + /// bit operators applied to fractional values. + static std::optional evaluateUnaryOperator(Token _operator, rational const& _input); private: + explicit ConstantEvaluator(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + + std::optional evaluate(ASTNode const& _node); + void endVisit(BinaryOperation const& _operation) override; void endVisit(UnaryOperation const& _operation) override; void endVisit(Literal const& _literal) override; void endVisit(Identifier const& _identifier) override; void endVisit(TupleExpression const& _tuple) override; - void setType(ASTNode const& _node, TypePointer const& _type); - TypePointer type(ASTNode const& _node); - langutil::ErrorReporter& m_errorReporter; /// Current recursion depth. size_t m_depth = 0; - std::shared_ptr> m_types; + /// Values of sub-expressions and variable declarations. + std::map> m_values; }; } diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index ff6b30e79ede..784c03d078c7 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -38,19 +38,51 @@ namespace { template -bool hasEqualNameAndParameters(T const& _a, B const& _b) +bool hasEqualParameters(T const& _a, B const& _b) { - return - _a.name() == _b.name() && - FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes( - *FunctionType(_b).asExternallyCallableFunction(false) - ); + return FunctionType(_a).asExternallyCallableFunction(false)->hasEqualParameterTypes( + *FunctionType(_b).asExternallyCallableFunction(false) + ); +} + +template +map> filterDeclarations( + map> const& _declarations) +{ + map> filteredDeclarations; + for (auto const& [name, overloads]: _declarations) + for (auto const* declaration: overloads) + if (auto typedDeclaration = dynamic_cast(declaration)) + filteredDeclarations[name].push_back(typedDeclaration); + return filteredDeclarations; +} + } +bool ContractLevelChecker::check(SourceUnit const& _sourceUnit) +{ + bool noErrors = true; + findDuplicateDefinitions( + filterDeclarations(*_sourceUnit.annotation().exportedSymbols) + ); + // This check flags duplicate free events when free events become + // a Solidity feature + findDuplicateDefinitions( + filterDeclarations(*_sourceUnit.annotation().exportedSymbols) + ); + if (!Error::containsOnlyWarnings(m_errorReporter.errors())) + noErrors = false; + for (ASTPointer const& node: _sourceUnit.nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + if (!check(*contract)) + noErrors = false; + return noErrors; } bool ContractLevelChecker::check(ContractDefinition const& _contract) { + _contract.annotation().unimplementedDeclarations = std::vector(); + checkDuplicateFunctions(_contract); checkDuplicateEvents(_contract); m_overrideChecker.check(_contract); @@ -141,8 +173,21 @@ void ContractLevelChecker::findDuplicateDefinitions(map> const SecondarySourceLocation ssl; for (size_t j = i + 1; j < overloads.size(); ++j) - if (hasEqualNameAndParameters(*overloads[i], *overloads[j])) + if (hasEqualParameters(*overloads[i], *overloads[j])) { + solAssert( + ( + dynamic_cast(overloads[i]->scope()) && + dynamic_cast(overloads[j]->scope()) && + overloads[i]->name() == overloads[j]->name() + ) || + ( + dynamic_cast(overloads[i]->scope()) && + dynamic_cast(overloads[j]->scope()) + ), + "Override is neither a namesake function/event in contract scope nor " + "a free function/event (alias)." + ); ssl.append("Other declaration is here:", overloads[j]->location()); reported.insert(j); } @@ -210,9 +255,10 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c // Set to not fully implemented if at least one flag is false. // Note that `_contract.annotation().unimplementedDeclarations` has already been // pre-filled by `checkBaseConstructorArguments`. + // for (auto const& proxy: proxies) if (proxy.unimplemented()) - _contract.annotation().unimplementedDeclarations.push_back(proxy.declaration()); + _contract.annotation().unimplementedDeclarations->push_back(proxy.declaration()); if (_contract.abstract()) { @@ -229,17 +275,17 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c if ( _contract.contractKind() == ContractKind::Contract && !_contract.abstract() && - !_contract.annotation().unimplementedDeclarations.empty() + !_contract.annotation().unimplementedDeclarations->empty() ) { SecondarySourceLocation ssl; - for (auto declaration: _contract.annotation().unimplementedDeclarations) + for (auto declaration: *_contract.annotation().unimplementedDeclarations) ssl.append("Missing implementation: ", declaration->location()); m_errorReporter.typeError( 3656_error, _contract.location(), ssl, - "Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract." + "Contract \"" + *_contract.annotation().canonicalName + "\" should be marked as abstract." ); } } @@ -255,7 +301,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons if (FunctionDefinition const* constructor = contract->constructor()) for (auto const& modifier: constructor->modifiers()) if (auto baseContract = dynamic_cast( - modifier->name()->annotation().referencedDeclaration + modifier->name().annotation().referencedDeclaration )) { if (modifier->arguments()) @@ -289,7 +335,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons if (FunctionDefinition const* constructor = contract->constructor()) if (contract != &_contract && !constructor->parameters().empty()) if (!_contract.annotation().baseConstructorArguments.count(constructor)) - _contract.annotation().unimplementedDeclarations.push_back(constructor); + _contract.annotation().unimplementedDeclarations->push_back(constructor); } void ContractLevelChecker::annotateBaseConstructorArguments( @@ -404,7 +450,7 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract) { - if (_contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)) + if (*_contract.sourceUnit().annotation().useABICoderV2) return; if (_contract.isLibrary()) @@ -423,12 +469,12 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _ { solAssert(func.second->hasDeclaration(), "Function has no declaration?!"); - if (!func.second->declaration().sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)) + if (!*func.second->declaration().sourceUnit().annotation().useABICoderV2) continue; auto const& currentLoc = func.second->declaration().location(); - for (TypePointer const& paramType: func.second->parameterTypes() + func.second->parameterTypes()) + for (TypePointer const& paramType: func.second->parameterTypes() + func.second->returnParameterTypes()) if (!TypeChecker::typeSupportedByOldABIEncoder(*paramType, false)) { errors.append("Type only supported by ABIEncoderV2", currentLoc); @@ -443,9 +489,9 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _ errors, std::string("Contract \"") + _contract.name() + - "\" does not use ABIEncoderV2 but wants to inherit from a contract " + + "\" does not use ABI coder v2 but wants to inherit from a contract " + "which uses types that require it. " + - "Use \"pragma experimental ABIEncoderV2;\" for the inheriting contract as well to enable the feature." + "Use \"pragma abicoder v2;\" for the inheriting contract as well to enable the feature." ); } @@ -465,7 +511,6 @@ void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition void ContractLevelChecker::checkStorageSize(ContractDefinition const& _contract) { bigint size = 0; - vector variables; for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) for (VariableDeclaration const* variable: contract->stateVariables()) if (!(variable->isConstant() || variable->immutable())) @@ -473,7 +518,7 @@ void ContractLevelChecker::checkStorageSize(ContractDefinition const& _contract) size += variable->annotation().type->storageSizeUpperBound(); if (size >= bigint(1) << 256) { - m_errorReporter.typeError(7676_error, _contract.location(), "Contract too large for storage."); + m_errorReporter.typeError(7676_error, _contract.location(), "Contract requires too much storage."); break; } } diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h index 2af0963d7d60..5f890681a20f 100644 --- a/libsolidity/analysis/ContractLevelChecker.h +++ b/libsolidity/analysis/ContractLevelChecker.h @@ -39,7 +39,7 @@ namespace solidity::frontend /** * Component that verifies overloads, abstract contracts, function clashes and others - * checks at contract or function level. + * checks at file, contract, or function level. */ class ContractLevelChecker { @@ -51,11 +51,14 @@ class ContractLevelChecker m_errorReporter(_errorReporter) {} - /// Performs checks on the given contract. + /// Performs checks on the given source ast. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings - bool check(ContractDefinition const& _contract); + bool check(SourceUnit const& _sourceUnit); private: + /// Performs checks on the given contract. + /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings + bool check(ContractDefinition const& _contract); /// Checks that two functions defined in this contract with the same name have different /// arguments and that there is at most one constructor. void checkDuplicateFunctions(ContractDefinition const& _contract); diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 1ed66f72b6db..9346f5565245 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -37,13 +37,13 @@ bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function) if (_function.isImplemented()) { auto const& functionFlow = m_cfg.functionFlow(_function); - checkUninitializedAccess(functionFlow.entry, functionFlow.exit); + checkUninitializedAccess(functionFlow.entry, functionFlow.exit, _function.body().statements().empty()); checkUnreachable(functionFlow.entry, functionFlow.exit, functionFlow.revert, functionFlow.transactionReturn); } return false; } -void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const +void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody) const { struct NodeInfo { @@ -95,14 +95,10 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod case VariableOccurrence::Kind::Return: if (unassignedVariables.count(&variableOccurrence.declaration())) { - if ( - variableOccurrence.declaration().type()->dataStoredIn(DataLocation::Storage) || - variableOccurrence.declaration().type()->dataStoredIn(DataLocation::CallData) - ) - // Merely store the unassigned access. We do not generate an error right away, since this - // path might still always revert. It is only an error if this is propagated to the exit - // node of the function (i.e. there is a path with an uninitialized access). - nodeInfo.uninitializedVariableAccesses.insert(&variableOccurrence); + // Merely store the unassigned access. We do not generate an error right away, since this + // path might still always revert. It is only an error if this is propagated to the exit + // node of the function (i.e. there is a path with an uninitialized access). + nodeInfo.uninitializedVariableAccesses.insert(&variableOccurrence); } break; case VariableOccurrence::Kind::Declaration: @@ -140,18 +136,26 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod ssl.append("The variable was declared here.", variableOccurrence->declaration().location()); bool isStorage = variableOccurrence->declaration().type()->dataStoredIn(DataLocation::Storage); - m_errorReporter.typeError( - 3464_error, - variableOccurrence->occurrence() ? - *variableOccurrence->occurrence() : + bool isCalldata = variableOccurrence->declaration().type()->dataStoredIn(DataLocation::CallData); + if (isStorage || isCalldata) + m_errorReporter.typeError( + 3464_error, + variableOccurrence->occurrence() ? + *variableOccurrence->occurrence() : + variableOccurrence->declaration().location(), + ssl, + "This variable is of " + + string(isStorage ? "storage" : "calldata") + + " pointer type and can be " + + (variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") + + " without prior assignment, which would lead to undefined behaviour." + ); + else if (!_emptyBody && variableOccurrence->declaration().name().empty()) + m_errorReporter.warning( + 6321_error, variableOccurrence->declaration().location(), - ssl, - "This variable is of " + - string(isStorage ? "storage" : "calldata") + - " pointer type and can be " + - (variableOccurrence->kind() == VariableOccurrence::Kind::Return ? "returned" : "accessed") + - " without prior assignment, which would lead to undefined behaviour." - ); + "Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable." + ); } } } diff --git a/libsolidity/analysis/ControlFlowAnalyzer.h b/libsolidity/analysis/ControlFlowAnalyzer.h index c731cc31fc92..4e53fef78e4d 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.h +++ b/libsolidity/analysis/ControlFlowAnalyzer.h @@ -36,7 +36,7 @@ class ControlFlowAnalyzer: private ASTConstVisitor private: /// Checks for uninitialized variable accesses in the control flow between @param _entry and @param _exit. - void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit) const; + void checkUninitializedAccess(CFGNode const* _entry, CFGNode const* _exit, bool _emptyBody) const; /// Checks for unreachable code, i.e. code ending in @param _exit, @param _revert or @param _transactionReturn /// that can not be reached from @param _entry. void checkUnreachable(CFGNode const* _entry, CFGNode const* _exit, CFGNode const* _revert, CFGNode const* _transactionReturn) const; diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index 4eb4658e1d50..975e4ec6d9ce 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -17,7 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include -#include +#include #include using namespace solidity; @@ -290,7 +290,7 @@ bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation) appendControlFlow(*argument); auto modifierDefinition = dynamic_cast( - _modifierInvocation.name()->annotation().referencedDeclaration + _modifierInvocation.name().annotation().referencedDeclaration ); if (!modifierDefinition) return false; solAssert(!!modifierDefinition, ""); diff --git a/libsolidity/analysis/ControlFlowGraph.h b/libsolidity/analysis/ControlFlowGraph.h index 7d51f2196a8d..e3c17f2d1600 100644 --- a/libsolidity/analysis/ControlFlowGraph.h +++ b/libsolidity/analysis/ControlFlowGraph.h @@ -71,12 +71,11 @@ class VariableOccurrence using KindCompareType = std::underlying_type::type; return std::make_pair(m_declaration.id(), static_cast(m_occurrenceKind)) < - std::make_pair(_rhs.m_declaration.id(), static_cast(_rhs.m_occurrenceKind)) - ; + std::make_pair(_rhs.m_declaration.id(), static_cast(_rhs.m_occurrenceKind)); } VariableDeclaration const& declaration() const { return m_declaration; } - Kind kind() const { return m_occurrenceKind; }; + Kind kind() const { return m_occurrenceKind; } std::optional const& occurrence() const { return m_occurrence; } private: /// Declaration of the occurring variable. diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index b514c2c3973f..25bd37b9f14b 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -100,6 +100,7 @@ bool DeclarationContainer::isInvisible(ASTString const& _name) const bool DeclarationContainer::registerDeclaration( Declaration const& _declaration, ASTString const* _name, + langutil::SourceLocation const* _location, bool _invisible, bool _update ) @@ -115,8 +116,18 @@ bool DeclarationContainer::registerDeclaration( m_declarations.erase(*_name); m_invisibleDeclarations.erase(*_name); } - else if (conflictingDeclaration(_declaration, _name)) - return false; + else + { + if (conflictingDeclaration(_declaration, _name)) + return false; + + // Do not warn about shadowing for structs and enums because their members are + // not accessible without prefixes. Also do not warn about event parameters + // because they do not participate in any proper scope. + bool special = _declaration.scope() && (_declaration.isStructMember() || _declaration.isEnumValue() || _declaration.isEventParameter()); + if (m_enclosingContainer && !special) + m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location()); + } vector& decls = _invisible ? m_invisibleDeclarations[*_name] : m_declarations[*_name]; if (!util::contains(decls, &_declaration)) @@ -124,6 +135,15 @@ bool DeclarationContainer::registerDeclaration( return true; } +bool DeclarationContainer::registerDeclaration( + Declaration const& _declaration, + bool _invisible, + bool _update +) +{ + return registerDeclaration(_declaration, nullptr, nullptr, _invisible, _update); +} + vector DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); @@ -164,3 +184,16 @@ vector DeclarationContainer::similarNames(ASTString const& _name) con return similar; } + +void DeclarationContainer::populateHomonyms(back_insert_iterator _it) const +{ + for (DeclarationContainer const* innerContainer: m_innerContainers) + innerContainer->populateHomonyms(_it); + + for (auto [name, location]: m_homonymCandidates) + { + vector const& declarations = m_enclosingContainer->resolveName(name, true, true); + if (!declarations.empty()) + _it = make_pair(location, declarations); + } +} diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h index 8036a5a58e81..a131775b2cfc 100644 --- a/libsolidity/analysis/DeclarationContainer.h +++ b/libsolidity/analysis/DeclarationContainer.h @@ -24,9 +24,9 @@ #pragma once #include +#include +#include #include -#include -#include namespace solidity::frontend { @@ -38,17 +38,26 @@ namespace solidity::frontend class DeclarationContainer { public: + using Homonyms = std::vector>>; + explicit DeclarationContainer( ASTNode const* _enclosingNode = nullptr, - DeclarationContainer const* _enclosingContainer = nullptr + DeclarationContainer* _enclosingContainer = nullptr ): - m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer) {} + m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer) + { + if (_enclosingContainer) + _enclosingContainer->m_innerContainers.emplace_back(this); + } /// Registers the declaration in the scope unless its name is already declared or the name is empty. /// @param _name the name to register, if nullptr the intrinsic name of @a _declaration is used. - /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName - /// @param _update if true, replaces a potential declaration that is already present + /// @param _location alternative location, used to point at homonymous declarations. + /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName. + /// @param _update if true, replaces a potential declaration that is already present. /// @returns false if the name was already declared. - bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false); + bool registerDeclaration(Declaration const& _declaration, ASTString const* _name, langutil::SourceLocation const* _location, bool _invisible, bool _update); + bool registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update); + std::vector resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const; ASTNode const* enclosingNode() const { return m_enclosingNode; } DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; } @@ -67,11 +76,18 @@ class DeclarationContainer /// Searches this and all parent containers. std::vector similarNames(ASTString const& _name) const; + /// Populates a vector of (location, declaration) pairs, where location is a location of an inner-scope declaration, + /// and declaration is the corresponding homonymous outer-scope declaration. + void populateHomonyms(std::back_insert_iterator _it) const; + private: ASTNode const* m_enclosingNode; DeclarationContainer const* m_enclosingContainer; + std::vector m_innerContainers; std::map> m_declarations; std::map> m_invisibleDeclarations; + /// List of declarations (name and location) to check later for homonymity. + std::vector> m_homonymCandidates; }; } diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index 15cbc30bae10..5c267b2859df 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -62,6 +62,18 @@ bool DeclarationTypeChecker::visit(ElementaryTypeName const& _typeName) return true; } +bool DeclarationTypeChecker::visit(EnumDefinition const& _enum) +{ + if (_enum.members().size() > 256) + m_errorReporter.declarationError( + 1611_error, + _enum.location(), + "Enum with more than 256 members is not allowed." + ); + + return false; +} + bool DeclarationTypeChecker::visit(StructDefinition const& _struct) { if (_struct.annotation().recursive.has_value()) @@ -133,7 +145,7 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName) if (_typeName.annotation().type) return; - Declaration const* declaration = _typeName.annotation().referencedDeclaration; + Declaration const* declaration = _typeName.pathNode().annotation().referencedDeclaration; solAssert(declaration, ""); if (StructDefinition const* structDef = dynamic_cast(declaration)) @@ -157,6 +169,16 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName) } } +void DeclarationTypeChecker::endVisit(IdentifierPath const& _path) +{ + Declaration const* declaration = _path.annotation().referencedDeclaration; + solAssert(declaration, ""); + + if (ContractDefinition const* contract = dynamic_cast(declaration)) + if (contract->isLibrary()) + m_errorReporter.typeError(1130_error, _path.location(), "Invalid use of a library name."); +} + bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName) { if (_typeName.annotation().type) @@ -201,23 +223,19 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping) return; if (auto const* typeName = dynamic_cast(&_mapping.keyType())) - { - if (auto const* contractType = dynamic_cast(typeName->annotation().type)) + switch (typeName->annotation().type->category()) { - if (contractType->contractDefinition().isLibrary()) + case Type::Category::Enum: + case Type::Category::Contract: + break; + default: m_errorReporter.fatalTypeError( - 1665_error, + 7804_error, typeName->location(), - "Library types cannot be used as mapping keys." + "Only elementary types, contract types or enums are allowed as mapping keys." ); + break; } - else if (typeName->annotation().type->category() != Type::Category::Enum) - m_errorReporter.fatalTypeError( - 7804_error, - typeName->location(), - "Only elementary types, contract types or enums are allowed as mapping keys." - ); - } else solAssert(dynamic_cast(&_mapping.keyType()), ""); @@ -243,34 +261,34 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) solAssert(!m_errorReporter.errors().empty(), ""); return; } - if (baseType->storageBytes() == 0) - m_errorReporter.fatalTypeError( - 6493_error, - _typeName.baseType().location(), - "Illegal base type of storage size zero for array." - ); + + solAssert(baseType->storageBytes() != 0, "Illegal base type of storage size zero for array."); if (Expression const* length = _typeName.length()) { - TypePointer& lengthTypeGeneric = length->annotation().type; - if (!lengthTypeGeneric) - lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length); - RationalNumberType const* lengthType = dynamic_cast(lengthTypeGeneric); - u256 lengthValue = 0; - if (!lengthType || !lengthType->mobileType()) + optional lengthValue; + if (length->annotation().type && length->annotation().type->category() == Type::Category::RationalNumber) + lengthValue = dynamic_cast(*length->annotation().type).value(); + else if (optional value = ConstantEvaluator::evaluate(m_errorReporter, *length)) + lengthValue = value->value; + + if (!lengthValue || lengthValue > TypeProvider::uint256()->max()) m_errorReporter.typeError( 5462_error, length->location(), "Invalid array length, expected integer literal or constant expression." ); - else if (lengthType->isZero()) + else if (*lengthValue == 0) m_errorReporter.typeError(1406_error, length->location(), "Array with zero length specified."); - else if (lengthType->isFractional()) + else if (lengthValue->denominator() != 1) m_errorReporter.typeError(3208_error, length->location(), "Array with fractional length specified."); - else if (lengthType->isNegative()) + else if (*lengthValue < 0) m_errorReporter.typeError(3658_error, length->location(), "Array with negative length specified."); - else - lengthValue = lengthType->literalValue(nullptr); - _typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue); + + _typeName.annotation().type = TypeProvider::array( + DataLocation::Storage, + baseType, + lengthValue ? u256(lengthValue->numerator()) : u256(0) + ); } else _typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType); @@ -281,11 +299,17 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) if (_variable.annotation().type) return; - if (_variable.isConstant() && !_variable.isStateVariable()) + if (_variable.isFileLevelVariable() && !_variable.isConstant()) + m_errorReporter.declarationError( + 8342_error, + _variable.location(), + "Only constant variables are allowed at file level." + ); + if (_variable.isConstant() && (!_variable.isStateVariable() && !_variable.isFileLevelVariable())) m_errorReporter.declarationError( 1788_error, _variable.location(), - "The \"constant\" keyword can only be used for state variables." + "The \"constant\" keyword can only be used for state variables or variables at file level." ); if (_variable.immutable() && !_variable.isStateVariable()) m_errorReporter.declarationError( @@ -349,6 +373,11 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) solAssert(varLoc == Location::Unspecified, ""); typeLoc = DataLocation::Memory; } + else if (_variable.isFileLevelVariable()) + { + solAssert(varLoc == Location::Unspecified, ""); + typeLoc = DataLocation::Memory; + } else if (_variable.isStateVariable()) { solAssert(varLoc == Location::Unspecified, ""); @@ -384,16 +413,37 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable) } _variable.annotation().type = type; - } -void DeclarationTypeChecker::endVisit(UsingForDirective const& _usingFor) +bool DeclarationTypeChecker::visit(UsingForDirective const& _usingFor) { ContractDefinition const* library = dynamic_cast( _usingFor.libraryName().annotation().referencedDeclaration ); + if (!library || !library->isLibrary()) m_errorReporter.fatalTypeError(4357_error, _usingFor.libraryName().location(), "Library name expected."); + + if (_usingFor.typeName()) + _usingFor.typeName()->accept(*this); + + return false; +} + +bool DeclarationTypeChecker::visit(InheritanceSpecifier const& _inheritanceSpecifier) +{ + auto const* contract = dynamic_cast(_inheritanceSpecifier.name().annotation().referencedDeclaration); + solAssert(contract, ""); + if (contract->isLibrary()) + { + m_errorReporter.typeError( + 2571_error, + _inheritanceSpecifier.name().location(), + "Libraries cannot be inherited from." + ); + return false; + } + return true; } bool DeclarationTypeChecker::check(ASTNode const& _node) diff --git a/libsolidity/analysis/DeclarationTypeChecker.h b/libsolidity/analysis/DeclarationTypeChecker.h index 6ba9d1aad6c2..c0437a643dac 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.h +++ b/libsolidity/analysis/DeclarationTypeChecker.h @@ -54,12 +54,15 @@ class DeclarationTypeChecker: private ASTConstVisitor bool visit(ElementaryTypeName const& _typeName) override; void endVisit(UserDefinedTypeName const& _typeName) override; + void endVisit(IdentifierPath const& _identifierPath) override; bool visit(FunctionTypeName const& _typeName) override; void endVisit(Mapping const& _mapping) override; void endVisit(ArrayTypeName const& _typeName) override; void endVisit(VariableDeclaration const& _variable) override; + bool visit(EnumDefinition const& _enum) override; bool visit(StructDefinition const& _struct) override; - void endVisit(UsingForDirective const& _usingForDirective) override; + bool visit(UsingForDirective const& _usingForDirective) override; + bool visit(InheritanceSpecifier const& _inheritanceSpecifier) override; langutil::ErrorReporter& m_errorReporter; langutil::EVMVersion m_evmVersion; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index 94a841e00e81..9de1d89798b8 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -36,21 +36,66 @@ using namespace solidity::frontend; namespace { -void copyMissingTags(StructurallyDocumentedAnnotation& _target, set const& _baseFunctions) +void copyMissingTags(set const& _baseFunctions, StructurallyDocumentedAnnotation& _target, CallableDeclaration const* _declaration = nullptr) { + // Only copy if there is exactly one direct base function. if (_baseFunctions.size() != 1) return; - auto& sourceDoc = dynamic_cast((*_baseFunctions.begin())->annotation()); + CallableDeclaration const& baseFunction = **_baseFunctions.begin(); + + auto hasReturnParameter = [](CallableDeclaration const& declaration, size_t _n) + { + return declaration.returnParameterList() && + declaration.returnParameters().size() > _n; + }; + + auto& sourceDoc = dynamic_cast(baseFunction.annotation()); + + for (auto it = sourceDoc.docTags.begin(); it != sourceDoc.docTags.end();) + { + string const& tag = it->first; + // Don't copy tag "inheritdoc" or already existing tags + if (tag == "inheritdoc" || _target.docTags.count(tag)) + { + it++; + continue; + } + + size_t n = 0; + // Iterate over all values of the current tag (it's a multimap) + for (auto next = sourceDoc.docTags.upper_bound(tag); it != next; it++, n++) + { + DocTag content = it->second; + + // Update the parameter name for @return tags + if (_declaration && tag == "return") + { + size_t docParaNameEndPos = content.content.find_first_of(" \t"); + string const docParameterName = content.content.substr(0, docParaNameEndPos); + + if ( + hasReturnParameter(*_declaration, n) && + docParameterName != _declaration->returnParameters().at(n)->name() + ) + { + bool baseHasNoName = + hasReturnParameter(baseFunction, n) && + baseFunction.returnParameters().at(n)->name().empty(); + + string paramName = _declaration->returnParameters().at(n)->name(); + content.content = + (paramName.empty() ? "" : std::move(paramName) + " ") + ( + string::npos == docParaNameEndPos || baseHasNoName ? + content.content : + content.content.substr(docParaNameEndPos + 1) + ); + } + } - set existingTags; - - for (auto const& iterator: _target.docTags) - existingTags.insert(iterator.first); - - for (auto const& [tag, content]: sourceDoc.docTags) - if (tag != "inheritdoc" && !existingTags.count(tag)) _target.docTags.emplace(tag, content); + } + } } CallableDeclaration const* findBaseCallable(set const& _baseFunctions, int64_t _contractId) @@ -87,13 +132,13 @@ bool DocStringAnalyser::visit(FunctionDefinition const& _function) bool DocStringAnalyser::visit(VariableDeclaration const& _variable) { - if (!_variable.isStateVariable()) + if (!_variable.isStateVariable() && !_variable.isFileLevelVariable()) return false; if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation())) - copyMissingTags(_variable.annotation(), {baseFunction}); + copyMissingTags({baseFunction}, _variable.annotation()); else if (_variable.annotation().docTags.empty()) - copyMissingTags(_variable.annotation(), _variable.annotation().baseFunctions); + copyMissingTags(_variable.annotation().baseFunctions, _variable.annotation()); return false; } @@ -119,13 +164,13 @@ void DocStringAnalyser::handleCallable( ) { if (CallableDeclaration const* baseFunction = resolveInheritDoc(_callable.annotation().baseFunctions, _node, _annotation)) - copyMissingTags(_annotation, {baseFunction}); + copyMissingTags({baseFunction}, _annotation, &_callable); else if ( _annotation.docTags.empty() && _callable.annotation().baseFunctions.size() == 1 && parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin()) ) - copyMissingTags(_annotation, _callable.annotation().baseFunctions); + copyMissingTags(_callable.annotation().baseFunctions, _annotation, &_callable); } CallableDeclaration const* DocStringAnalyser::resolveInheritDoc( diff --git a/libsolidity/analysis/DocStringTagParser.cpp b/libsolidity/analysis/DocStringTagParser.cpp index 70f677c6a7ec..02c22e9b83d3 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -61,13 +61,13 @@ bool DocStringTagParser::visit(VariableDeclaration const& _variable) { if (_variable.isStateVariable()) { - static set const validPublicTags = set{"dev", "notice", "return", "inheritdoc"}; - static set const validNonPublicTags = set{"dev", "inheritdoc"}; if (_variable.isPublic()) - parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables"); + parseDocStrings(_variable, _variable.annotation(), {"dev", "notice", "return", "inheritdoc"}, "public state variables"); else - parseDocStrings(_variable, _variable.annotation(), validNonPublicTags, "non-public state variables"); + parseDocStrings(_variable, _variable.annotation(), {"dev", "inheritdoc"}, "non-public state variables"); } + else if (_variable.isFileLevelVariable()) + parseDocStrings(_variable, _variable.annotation(), {"dev"}, "file-level variables"); return false; } @@ -127,10 +127,13 @@ void DocStringTagParser::handleCallable( ) { static set const validEventTags = set{"dev", "notice", "return", "param"}; + static set const validModifierTags = set{"dev", "notice", "param", "inheritdoc"}; static set const validTags = set{"dev", "notice", "return", "param", "inheritdoc"}; if (dynamic_cast(&_callable)) parseDocStrings(_node, _annotation, validEventTags, "events"); + else if (dynamic_cast(&_callable)) + parseDocStrings(_node, _annotation, validModifierTags, "modifiers"); else parseDocStrings(_node, _annotation, validTags, "functions"); @@ -165,12 +168,7 @@ void DocStringTagParser::parseDocStrings( returnTagsVisited++; if (auto const* varDecl = dynamic_cast(&_node)) { - if (!varDecl->isPublic()) - m_errorReporter.docstringParsingError( - 9440_error, - _node.documentation()->location(), - "Documentation tag \"@" + docTag.first + "\" is only allowed on public state-variables." - ); + solAssert(varDecl->isPublic(), "@return is only allowed on public state-variables."); if (returnTagsVisited > 1) m_errorReporter.docstringParsingError( 5256_error, diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 603d420de5c4..53ae49850569 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -34,6 +34,8 @@ using namespace std; namespace solidity::frontend { +namespace +{ /// Magic variables get negative ids for easy differentiation int magicVariableToID(std::string const& _name) { @@ -45,11 +47,6 @@ int magicVariableToID(std::string const& _name) else if (_name == "ecrecover") return -6; else if (_name == "gasleft") return -7; else if (_name == "keccak256") return -8; - else if (_name == "log0") return -10; - else if (_name == "log1") return -11; - else if (_name == "log2") return -12; - else if (_name == "log3") return -13; - else if (_name == "log4") return -14; else if (_name == "msg") return -15; else if (_name == "mulmod") return -16; else if (_name == "now") return -17; @@ -92,11 +89,6 @@ inline vector> constructMagicVariable magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), - magicVarDecl("log0", TypeProvider::function(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), - magicVarDecl("log1", TypeProvider::function(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)), - magicVarDecl("log2", TypeProvider::function(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)), - magicVarDecl("log3", TypeProvider::function(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log3)), - magicVarDecl("log4", TypeProvider::function(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log4)), magicVarDecl("msg", TypeProvider::magic(MagicType::Kind::Message)), magicVarDecl("mulmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)), magicVarDecl("now", TypeProvider::uint256()), @@ -401,15 +393,26 @@ vector GlobalContext::declarations() const MagicVariableDeclaration const* GlobalContext::currentThis() const { if (!m_thisPointer[m_currentContract]) - m_thisPointer[m_currentContract] = make_shared(magicVariableToID("this"), "this", TypeProvider::contract(*m_currentContract)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::contract(*m_currentContract); + m_thisPointer[m_currentContract] = + make_shared(magicVariableToID("this"), "this", type); + } return m_thisPointer[m_currentContract].get(); - } MagicVariableDeclaration const* GlobalContext::currentSuper() const { if (!m_superPointer[m_currentContract]) - m_superPointer[m_currentContract] = make_shared(magicVariableToID("super"), "super", TypeProvider::contract(*m_currentContract, true)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::typeType(TypeProvider::contract(*m_currentContract, true)); + m_superPointer[m_currentContract] = + make_shared(magicVariableToID("super"), "super", type); + } return m_superPointer[m_currentContract].get(); } diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h index 2a578f66cae5..1ae1f3aa63ac 100644 --- a/libsolidity/analysis/GlobalContext.h +++ b/libsolidity/analysis/GlobalContext.h @@ -46,6 +46,7 @@ class GlobalContext: private boost::noncopyable public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + void resetCurrentContract() { m_currentContract = nullptr; } MagicVariableDeclaration const* currentThis() const; MagicVariableDeclaration const* currentSuper() const; diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index 5af640e58537..bc24c6674094 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -34,10 +34,12 @@ void ImmutableValidator::analyze() for (ContractDefinition const* contract: linearizedContracts) for (VariableDeclaration const* stateVar: contract->stateVariables()) if (stateVar->value()) - { + m_initializedStateVariables.emplace(stateVar); + + for (ContractDefinition const* contract: linearizedContracts) + for (VariableDeclaration const* stateVar: contract->stateVariables()) + if (stateVar->value()) stateVar->value()->accept(*this); - solAssert(m_initializedStateVariables.emplace(stateVar).second, ""); - } for (ContractDefinition const* contract: linearizedContracts) if (contract->constructor()) @@ -120,10 +122,22 @@ bool ImmutableValidator::visit(WhileStatement const& _whileStatement) return false; } +void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath) +{ + if (auto const callableDef = dynamic_cast(_identifierPath.annotation().referencedDeclaration)) + visitCallableIfNew( + *_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ? + callableDef->resolveVirtual(m_currentContract) : + *callableDef + ); + + solAssert(!dynamic_cast(_identifierPath.annotation().referencedDeclaration), ""); +} + void ImmutableValidator::endVisit(Identifier const& _identifier) { if (auto const callableDef = dynamic_cast(_identifier.annotation().referencedDeclaration)) - visitCallableIfNew(callableDef->resolveVirtual(m_currentContract)); + visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_currentContract) : *callableDef); if (auto const varDecl = dynamic_cast(_identifier.annotation().referencedDeclaration)) analyseVariableReference(*varDecl, _identifier); } @@ -163,41 +177,44 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va if (!_variableReference.isStateVariable() || !_variableReference.immutable()) return; - if (_expression.annotation().willBeWrittenTo && _expression.annotation().lValueOfOrdinaryAssignment) + // If this is not an ordinary assignment, we write and read at the same time. + bool write = _expression.annotation().willBeWrittenTo; + bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment; + if (write) { if (!m_currentConstructor) m_errorReporter.typeError( 1581_error, _expression.location(), - "Immutable variables can only be initialized inline or assigned directly in the constructor." + "Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor." ); else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) m_errorReporter.typeError( 7484_error, _expression.location(), - "Immutable variables must be initialized in the constructor of the contract they are defined in." + "Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in." ); else if (m_inLoop) m_errorReporter.typeError( 6672_error, _expression.location(), - "Immutable variables can only be initialized once, not in a while statement." + "Cannot write to immutable here: Immutable variables cannot be initialized inside a loop." ); else if (m_inBranch) m_errorReporter.typeError( 4599_error, _expression.location(), - "Immutable variables must be initialized unconditionally, not in an if statement." + "Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement." ); - - if (!m_initializedStateVariables.emplace(&_variableReference).second) + else if (m_initializedStateVariables.count(&_variableReference)) m_errorReporter.typeError( 1574_error, _expression.location(), "Immutable state variable already initialized." ); + m_initializedStateVariables.emplace(&_variableReference); } - else if (m_inConstructionContext) + if (read && m_inConstructionContext) m_errorReporter.typeError( 7733_error, _expression.location(), diff --git a/libsolidity/analysis/ImmutableValidator.h b/libsolidity/analysis/ImmutableValidator.h index ffa45e74cb51..1ff58f0eb2cf 100644 --- a/libsolidity/analysis/ImmutableValidator.h +++ b/libsolidity/analysis/ImmutableValidator.h @@ -53,6 +53,7 @@ class ImmutableValidator: private ASTConstVisitor bool visit(MemberAccess const& _memberAccess); bool visit(IfStatement const& _ifStatement); bool visit(WhileStatement const& _whileStatement); + void endVisit(IdentifierPath const& _identifierPath); void endVisit(Identifier const& _identifier); void endVisit(Return const& _return); diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 0c6407850414..53e008eb3459 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using namespace std; using namespace solidity::langutil; @@ -47,7 +48,7 @@ NameAndTypeResolver::NameAndTypeResolver( m_scopes[nullptr] = make_shared(); for (Declaration const* declaration: _globalContext.declarations()) { - solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration."); + solAssert(m_scopes[nullptr]->registerDeclaration(*declaration, false, false), "Unable to register global declaration."); } } @@ -74,7 +75,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map(node.get())) { - string const& path = imp->annotation().absolutePath; + string const& path = *imp->annotation().absolutePath; if (!_sourceUnits.count(path)) { m_errorReporter.declarationError( @@ -109,7 +110,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapsecond->declarations()) for (auto const& declaration: nameAndDeclaration.second) if (!DeclarationRegistrationHelper::registerDeclaration( - target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter + target, *declaration, &nameAndDeclaration.first, &imp->location(), false, m_errorReporter )) error = true; } + _sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations(); return !error; } @@ -129,8 +131,11 @@ bool NameAndTypeResolver::resolveNamesAndTypes(SourceUnit& _source) try { for (shared_ptr const& node: _source.nodes()) + { + setScope(&_source); if (!resolveNamesAndTypesInternal(*node, true)) return false; + } } catch (langutil::FatalError const&) { @@ -145,7 +150,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) { try { - m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true); + m_scopes[nullptr]->registerDeclaration(_declaration, false, true); solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope."); } catch (langutil::FatalError const&) @@ -198,7 +203,7 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector c return nullptr; } -void NameAndTypeResolver::warnVariablesNamedLikeInstructions() +void NameAndTypeResolver::warnVariablesNamedLikeInstructions() const { for (auto const& instruction: evmasm::c_instructions) { @@ -219,6 +224,52 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() } } +void NameAndTypeResolver::warnHomonymDeclarations() const +{ + DeclarationContainer::Homonyms homonyms; + m_scopes.at(nullptr)->populateHomonyms(back_inserter(homonyms)); + + for (auto [innerLocation, outerDeclarations]: homonyms) + { + solAssert(innerLocation && !outerDeclarations.empty(), ""); + + bool magicShadowed = false; + SecondarySourceLocation homonymousLocations; + SecondarySourceLocation shadowedLocations; + for (Declaration const* outerDeclaration: outerDeclarations) + { + solAssert(outerDeclaration, ""); + if (dynamic_cast(outerDeclaration)) + magicShadowed = true; + else if (!outerDeclaration->isVisibleInContract()) + homonymousLocations.append("The other declaration is here:", outerDeclaration->location()); + else + shadowedLocations.append("The shadowed declaration is here:", outerDeclaration->location()); + } + + if (magicShadowed) + m_errorReporter.warning( + 2319_error, + *innerLocation, + "This declaration shadows a builtin symbol." + ); + if (!homonymousLocations.infos.empty()) + m_errorReporter.warning( + 8760_error, + *innerLocation, + "This declaration has the same name as another declaration.", + homonymousLocations + ); + if (!shadowedLocations.infos.empty()) + m_errorReporter.warning( + 2519_error, + *innerLocation, + "This declaration shadows an existing declaration.", + shadowedLocations + ); + } +} + void NameAndTypeResolver::setScope(ASTNode const* _node) { m_currentScope = m_scopes[_node].get(); @@ -234,7 +285,8 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res solAssert(_resolveInsideCode, ""); m_globalContext.setCurrentContract(*contract); - updateDeclaration(*m_globalContext.currentSuper()); + if (!contract->isLibrary()) + updateDeclaration(*m_globalContext.currentSuper()); updateDeclaration(*m_globalContext.currentThis()); for (ASTPointer const& baseContract: contract->baseContracts()) @@ -275,6 +327,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res if (!resolveNamesAndTypesInternal(*node, true)) success = false; } + + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), true, true); + m_globalContext.resetCurrentContract(); + return success; } else @@ -293,7 +351,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base) for (auto const& declaration: nameAndDeclaration.second) // Import if it was declared in the base, is not the constructor and is visible in derived classes if (declaration->scope() == &_base && declaration->isVisibleInDerivedContracts()) - if (!m_currentScope->registerDeclaration(*declaration)) + if (!m_currentScope->registerDeclaration(*declaration, false, false)) { SourceLocation firstDeclarationLocation; SourceLocation secondDeclarationLocation; @@ -343,7 +401,7 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) list> input(1, list{}); for (ASTPointer const& baseSpecifier: _contract.baseContracts()) { - UserDefinedTypeName const& baseName = baseSpecifier->name(); + IdentifierPath const& baseName = baseSpecifier->name(); auto base = dynamic_cast(baseName.annotation().referencedDeclaration); if (!base) m_errorReporter.fatalTypeError(8758_error, baseName.location(), "Contract expected."); @@ -440,7 +498,6 @@ bool DeclarationRegistrationHelper::registerDeclaration( Declaration const& _declaration, string const* _name, SourceLocation const* _errorLocation, - bool _warnOnShadow, bool _inactive, ErrorReporter& _errorReporter ) @@ -449,15 +506,38 @@ bool DeclarationRegistrationHelper::registerDeclaration( _errorLocation = &_declaration.location(); string name = _name ? *_name : _declaration.name(); - Declaration const* shadowedDeclaration = nullptr; - if (_warnOnShadow && !name.empty() && _container.enclosingContainer()) - for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true)) - shadowedDeclaration = decl; // We use "invisible" for both inactive variables in blocks and for members invisible in contracts. // They cannot both be true at the same time. solAssert(!(_inactive && !_declaration.isVisibleInContract()), ""); - if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive)) + + static set illegalNames{"_", "super", "this"}; + + if (illegalNames.count(name)) + { + auto isPublicFunctionOrEvent = [](Declaration const* _d) -> bool + { + if (auto functionDefinition = dynamic_cast(_d)) + { + if (!functionDefinition->isFree() && functionDefinition->isPublic()) + return true; + } + else if (dynamic_cast(_d)) + return true; + + return false; + }; + + // We allow an exception for public functions or events. + if (!isPublicFunctionOrEvent(&_declaration)) + _errorReporter.declarationError( + 3726_error, + *_errorLocation, + "The name \"" + name + "\" is reserved." + ); + } + + if (!_container.registerDeclaration(_declaration, _name, _errorLocation, !_declaration.isVisibleInContract() || _inactive, false)) { SourceLocation firstDeclarationLocation; SourceLocation secondDeclarationLocation; @@ -486,25 +566,7 @@ bool DeclarationRegistrationHelper::registerDeclaration( ); return false; } - else if (shadowedDeclaration) - { - if (dynamic_cast(shadowedDeclaration)) - _errorReporter.warning( - 2319_error, - *_errorLocation, - "This declaration shadows a builtin symbol." - ); - else - { - auto shadowedLocation = shadowedDeclaration->location(); - _errorReporter.warning( - 2519_error, - _declaration.location(), - "This declaration shadows an existing declaration.", - SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation) - ); - } - } + return true; } @@ -513,14 +575,12 @@ bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit) if (!m_scopes[&_sourceUnit]) // By importing, it is possible that the container already exists. m_scopes[&_sourceUnit] = make_shared(m_currentScope, m_scopes[m_currentScope].get()); - m_currentScope = &_sourceUnit; - return true; + return ASTVisitor::visit(_sourceUnit); } void DeclarationRegistrationHelper::endVisit(SourceUnit& _sourceUnit) { - _sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations(); - closeCurrentScope(); + ASTVisitor::endVisit(_sourceUnit); } bool DeclarationRegistrationHelper::visit(ImportDirective& _import) @@ -530,130 +590,27 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import) if (!m_scopes[importee]) m_scopes[importee] = make_shared(nullptr, m_scopes[nullptr].get()); m_scopes[&_import] = m_scopes[importee]; - registerDeclaration(_import, false); - return true; + return ASTVisitor::visit(_import); } bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) { m_globalContext.setCurrentContract(_contract); - m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, false, true); - m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, false, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), false, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), false, true); m_currentContract = &_contract; - registerDeclaration(_contract, true); - _contract.annotation().canonicalName = currentCanonicalName(); - return true; + return ASTVisitor::visit(_contract); } -void DeclarationRegistrationHelper::endVisit(ContractDefinition&) +void DeclarationRegistrationHelper::endVisit(ContractDefinition& _contract) { + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), true, true); + m_globalContext.resetCurrentContract(); m_currentContract = nullptr; - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(StructDefinition& _struct) -{ - registerDeclaration(_struct, true); - _struct.annotation().canonicalName = currentCanonicalName(); - return true; -} - -void DeclarationRegistrationHelper::endVisit(StructDefinition&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum) -{ - registerDeclaration(_enum, true); - _enum.annotation().canonicalName = currentCanonicalName(); - return true; -} - -void DeclarationRegistrationHelper::endVisit(EnumDefinition&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(EnumValue& _value) -{ - registerDeclaration(_value, false); - return true; -} - -bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function) -{ - registerDeclaration(_function, true); - m_currentFunction = &_function; - _function.annotation().contract = m_currentContract; - return true; -} - -void DeclarationRegistrationHelper::endVisit(FunctionDefinition&) -{ - m_currentFunction = nullptr; - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(TryCatchClause& _tryCatchClause) -{ - _tryCatchClause.annotation().scope = m_currentScope; - enterNewSubScope(_tryCatchClause); - return true; -} - -void DeclarationRegistrationHelper::endVisit(TryCatchClause&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier) -{ - registerDeclaration(_modifier, true); - m_currentFunction = &_modifier; - return true; -} - -void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) -{ - m_currentFunction = nullptr; - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(FunctionTypeName& _funTypeName) -{ - enterNewSubScope(_funTypeName); - return true; -} - -void DeclarationRegistrationHelper::endVisit(FunctionTypeName&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(Block& _block) -{ - _block.annotation().scope = m_currentScope; - enterNewSubScope(_block); - return true; -} - -void DeclarationRegistrationHelper::endVisit(Block&) -{ - closeCurrentScope(); -} - -bool DeclarationRegistrationHelper::visit(ForStatement& _for) -{ - _for.annotation().scope = m_currentScope; - enterNewSubScope(_for); - return true; -} - -void DeclarationRegistrationHelper::endVisit(ForStatement&) -{ - closeCurrentScope(); + ASTVisitor::endVisit(_contract); } void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) @@ -664,32 +621,45 @@ void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _vari for (ASTPointer const& var: _variableDeclarationStatement.declarations()) if (var) m_currentFunction->addLocalVariable(*var); + ASTVisitor::endVisit(_variableDeclarationStatement); } -bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) +bool DeclarationRegistrationHelper::visitNode(ASTNode& _node) { - registerDeclaration(_declaration, false); - return true; -} + if (auto const* scopable = dynamic_cast(&_node)) + solAssert(scopable->annotation().scope == m_currentScope, ""); + + if (auto* declaration = dynamic_cast(&_node)) + registerDeclaration(*declaration); + if (dynamic_cast(&_node)) + enterNewSubScope(_node); + + if (auto* variableScope = dynamic_cast(&_node)) + m_currentFunction = variableScope; + if (auto* annotation = dynamic_cast(&_node.annotation())) + annotation->canonicalName = currentCanonicalName(); -bool DeclarationRegistrationHelper::visit(EventDefinition& _event) -{ - registerDeclaration(_event, true); return true; } -void DeclarationRegistrationHelper::endVisit(EventDefinition&) +void DeclarationRegistrationHelper::endVisitNode(ASTNode& _node) { - closeCurrentScope(); + if (dynamic_cast(&_node)) + closeCurrentScope(); + if (dynamic_cast(&_node)) + m_currentFunction = nullptr; } void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) { - map>::iterator iter; - bool newlyAdded; - shared_ptr container{make_shared(m_currentScope, m_scopes[m_currentScope].get())}; - tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container)); - solAssert(newlyAdded, "Unable to add new scope."); + if (m_scopes.count(&_subScope)) + // Source units are the only AST nodes for which containers can be created from multiple places due to imports. + solAssert(dynamic_cast(&_subScope), "Unexpected scope type."); + else + { + bool newlyAdded = m_scopes.emplace(&_subScope, make_shared(m_currentScope, m_scopes[m_currentScope].get())).second; + solAssert(newlyAdded, "Unable to add new scope."); + } m_currentScope = &_subScope; } @@ -699,31 +669,19 @@ void DeclarationRegistrationHelper::closeCurrentScope() m_currentScope = m_scopes[m_currentScope]->enclosingNode(); } -void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) +void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration) { solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope."); - - bool warnAboutShadowing = true; - // Do not warn about shadowing for structs and enums because their members are - // not accessible without prefixes. Also do not warn about event parameters - // because they don't participate in any proper scope. - if ( - dynamic_cast(m_currentScope) || - dynamic_cast(m_currentScope) || - dynamic_cast(m_currentScope) - ) - warnAboutShadowing = false; + solAssert(m_currentScope == _declaration.scope(), "Unexpected current scope."); // Register declaration as inactive if we are in block scope. bool inactive = (dynamic_cast(m_currentScope) || dynamic_cast(m_currentScope)); - registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter); + registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, inactive, m_errorReporter); - _declaration.annotation().scope = m_currentScope; - _declaration.annotation().contract = m_currentContract; - if (_opensScope) - enterNewSubScope(_declaration); + solAssert(_declaration.annotation().scope == m_currentScope, ""); + solAssert(_declaration.annotation().contract == m_currentContract, ""); } string DeclarationRegistrationHelper::currentCanonicalName() const diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index d72822653044..da90c690b15a 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -93,7 +93,10 @@ class NameAndTypeResolver: private boost::noncopyable Declaration const* pathFromCurrentScope(std::vector const& _path) const; /// Generate and store warnings about variables that are named like instructions. - void warnVariablesNamedLikeInstructions(); + void warnVariablesNamedLikeInstructions() const; + + /// Generate and store warnings about declarations with the same name. + void warnHomonymDeclarations() const; /// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions. std::string similarNameSuggestions(ASTString const& _name) const; @@ -152,7 +155,6 @@ class DeclarationRegistrationHelper: private ASTVisitor Declaration const& _declaration, std::string const* _name, langutil::SourceLocation const* _errorLocation, - bool _warnOnShadow, bool _inactive, langutil::ErrorReporter& _errorReporter ); @@ -163,31 +165,15 @@ class DeclarationRegistrationHelper: private ASTVisitor bool visit(ImportDirective& _import) override; bool visit(ContractDefinition& _contract) override; void endVisit(ContractDefinition& _contract) override; - bool visit(StructDefinition& _struct) override; - void endVisit(StructDefinition& _struct) override; - bool visit(EnumDefinition& _enum) override; - void endVisit(EnumDefinition& _enum) override; - bool visit(EnumValue& _value) override; - bool visit(FunctionDefinition& _function) override; - void endVisit(FunctionDefinition& _function) override; - bool visit(TryCatchClause& _tryCatchClause) override; - void endVisit(TryCatchClause& _tryCatchClause) override; - bool visit(ModifierDefinition& _modifier) override; - void endVisit(ModifierDefinition& _modifier) override; - bool visit(FunctionTypeName& _funTypeName) override; - void endVisit(FunctionTypeName& _funTypeName) override; - bool visit(Block& _block) override; - void endVisit(Block& _block) override; - bool visit(ForStatement& _forLoop) override; - void endVisit(ForStatement& _forLoop) override; void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override; - bool visit(VariableDeclaration& _declaration) override; - bool visit(EventDefinition& _event) override; - void endVisit(EventDefinition& _event) override; + + bool visitNode(ASTNode& _node) override; + void endVisitNode(ASTNode& _node) override; + void enterNewSubScope(ASTNode& _subScope); void closeCurrentScope(); - void registerDeclaration(Declaration& _declaration, bool _opensScope); + void registerDeclaration(Declaration& _declaration); static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2); diff --git a/libsolidity/analysis/OverrideChecker.cpp b/libsolidity/analysis/OverrideChecker.cpp index 0bfb032b1406..0b87f95029df 100644 --- a/libsolidity/analysis/OverrideChecker.cpp +++ b/libsolidity/analysis/OverrideChecker.cpp @@ -154,12 +154,12 @@ vector resolveDirectBaseContracts(ContractDefinition return resolvedContracts; } -vector> sortByContract(vector> const& _list) +vector> sortByContract(vector> const& _list) { auto sorted = _list; stable_sort(sorted.begin(), sorted.end(), - [] (ASTPointer _a, ASTPointer _b) { + [] (ASTPointer _a, ASTPointer _b) { if (!_a || !_b) return _a < _b; @@ -394,6 +394,10 @@ bool OverrideProxy::OverrideComparator::operator<(OverrideComparator const& _oth if (functionKind != _other.functionKind) return *functionKind < *_other.functionKind; + // Parameters do not matter for non-regular functions. + if (functionKind != Token::Function) + return false; + if (!parameterTypes || !_other.parameterTypes) return false; @@ -574,16 +578,19 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr FunctionType const* functionType = _overriding.functionType(); FunctionType const* superType = _super.functionType(); - solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!"); - - if (!functionType->hasEqualReturnTypes(*superType)) - overrideError( - _overriding, - _super, - 4822_error, - "Overriding " + _overriding.astNodeName() + " return types differ.", - "Overridden " + _overriding.astNodeName() + " is here:" - ); + if (_overriding.functionKind() != Token::Fallback) + { + solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!"); + + if (!functionType->hasEqualReturnTypes(*superType)) + overrideError( + _overriding, + _super, + 4822_error, + "Overriding " + _overriding.astNodeName() + " return types differ.", + "Overridden " + _overriding.astNodeName() + " is here:" + ); + } // Stricter mutability is always okay except when super is Payable if ( @@ -773,7 +780,7 @@ set OverrideChecker::re { set resolved; - for (ASTPointer const& override: _overrides.overrides()) + for (ASTPointer const& override: _overrides.overrides()) { Declaration const* decl = override->annotation().referencedDeclaration; solAssert(decl, "Expected declaration to be resolved."); @@ -798,7 +805,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign if (_item.overrides() && specifiedContracts.size() != _item.overrides()->overrides().size()) { // Sort by contract id to find duplicate for error reporting - vector> list = + vector> list = sortByContract(_item.overrides()->overrides()); // Find duplicates and output error @@ -818,7 +825,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign list[i]->location(), ssl, "Duplicate contract \"" + - joinHumanReadable(list[i]->namePath(), ".") + + joinHumanReadable(list[i]->path(), ".") + "\" found in override list of \"" + _item.name() + "\"." diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index cb04eec6f825..e11ed9e3e6d2 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -38,6 +38,13 @@ bool PostTypeChecker::check(ASTNode const& _astRoot) return Error::containsOnlyWarnings(m_errorReporter.errors()); } +bool PostTypeChecker::finalize() +{ + for (auto& checker: m_checkers) + checker->finalize(); + return Error::containsOnlyWarnings(m_errorReporter.errors()); +} + bool PostTypeChecker::visit(ContractDefinition const& _contractDefinition) { return callVisit(_contractDefinition); @@ -83,6 +90,11 @@ bool PostTypeChecker::visit(Identifier const& _identifier) return callVisit(_identifier); } +bool PostTypeChecker::visit(MemberAccess const& _memberAccess) +{ + return callVisit(_memberAccess); +} + bool PostTypeChecker::visit(StructDefinition const& _struct) { return callVisit(_struct); @@ -110,14 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker ConstStateVarCircularReferenceChecker(ErrorReporter& _errorReporter): Checker(_errorReporter) {} - bool visit(ContractDefinition const&) override - { - solAssert(!m_currentConstVariable, ""); - solAssert(m_constVariableDependencies.empty(), ""); - return true; - } - - void endVisit(ContractDefinition const&) override + void finalize() override { solAssert(!m_currentConstVariable, ""); for (auto declaration: m_constVariables) @@ -128,9 +133,12 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker "The value of the constant " + declaration->name() + " has a cyclic dependency via " + identifier->name() + "." ); + } - m_constVariables.clear(); - m_constVariableDependencies.clear(); + bool visit(ContractDefinition const&) override + { + solAssert(!m_currentConstVariable, ""); + return true; } bool visit(VariableDeclaration const& _variable) override @@ -162,6 +170,15 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker return true; } + bool visit(MemberAccess const& _memberAccess) override + { + if (m_currentConstVariable) + if (auto var = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + if (var->isConstant()) + m_constVariableDependencies[m_currentConstVariable].insert(var); + return true; + } + VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom) { auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector& _cycleDetector, size_t _depth) @@ -200,9 +217,9 @@ struct OverrideSpecifierChecker: public PostTypeChecker::Checker void endVisit(OverrideSpecifier const& _overrideSpecifier) override { - for (ASTPointer const& override: _overrideSpecifier.overrides()) + for (ASTPointer const& override: _overrideSpecifier.overrides()) { - Declaration const* decl = override->annotation().referencedDeclaration; + Declaration const* decl = override->annotation().referencedDeclaration; solAssert(decl, "Expected declaration to be resolved."); if (dynamic_cast(decl)) @@ -277,7 +294,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker bool visit(FunctionCall const& _functionCall) override { - if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall) return true; if (FunctionTypePointer const functionType = dynamic_cast(_functionCall.expression().annotation().type)) diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index d3f23918c6db..fd94785e1521 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -35,7 +35,7 @@ namespace solidity::frontend /** * This module performs analyses on the AST that are done after type checking and assignments of types: - * - whether there are circular references in constant state variables + * - whether there are circular references in constant variables * - whether override specifiers are actually contracts * - whether a modifier is in a function header * - whether an event is used outside of an emit statement @@ -54,6 +54,9 @@ class PostTypeChecker: private ASTConstVisitor { Checker(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + + /// Called after all source units have been visited. + virtual void finalize() {} protected: langutil::ErrorReporter& m_errorReporter; }; @@ -63,6 +66,9 @@ class PostTypeChecker: private ASTConstVisitor bool check(ASTNode const& _astRoot); + /// Called after all source units have been visited. + bool finalize(); + private: bool visit(ContractDefinition const& _contract) override; void endVisit(ContractDefinition const& _contract) override; @@ -77,6 +83,7 @@ class PostTypeChecker: private ASTConstVisitor bool visit(FunctionCall const& _functionCall) override; bool visit(Identifier const& _identifier) override; + bool visit(MemberAccess const& _identifier) override; bool visit(StructDefinition const& _struct) override; void endVisit(StructDefinition const& _struct) override; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 82cb1893712d..930534924485 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include @@ -171,16 +171,16 @@ void ReferencesResolver::endVisit(ModifierDefinition const&) m_returnParameters.pop_back(); } -void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) +void ReferencesResolver::endVisit(IdentifierPath const& _path) { - Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); + Declaration const* declaration = m_resolver.pathFromCurrentScope(_path.path()); if (!declaration) { - m_errorReporter.fatalDeclarationError(7920_error, _typeName.location(), "Identifier not found or not unique."); + m_errorReporter.fatalDeclarationError(7920_error, _path.location(), "Identifier not found or not unique."); return; } - _typeName.annotation().referencedDeclaration = declaration; + _path.annotation().referencedDeclaration = declaration; } bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) @@ -215,31 +215,22 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) void ReferencesResolver::operator()(yul::Identifier const& _identifier) { - bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), ".slot"); - bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), ".offset"); + static set suffixes{"slot", "offset", "length"}; + string suffix; + for (string const& s: suffixes) + if (boost::algorithm::ends_with(_identifier.name.str(), "." + s)) + suffix = s; // Could also use `pathFromCurrentScope`, split by '.' auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); - if (isSlot || isOffset) + if (!suffix.empty()) { // special mode to access storage variables if (!declarations.empty()) // the special identifier exists itself, we should not allow that. return; - string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( - isSlot ? - string(".slot").size() : - string(".offset").size() - )); - if (realName.empty()) - { - m_errorReporter.declarationError( - 4794_error, - _identifier.location, - "In variable names .slot and .offset can only be used as a suffix." - ); - return; - } + string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - suffix.size() - 1); + solAssert(!realName.empty(), "Empty name."); declarations = m_resolver.nameFromCurrentScope(realName); if (!declarations.empty()) // To support proper path resolution, we have to use pathFromCurrentScope. @@ -263,7 +254,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) m_errorReporter.declarationError( 9467_error, _identifier.location, - "Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables." + "Identifier not found. Use \".slot\" and \".offset\" to access storage variables." ); return; } @@ -278,8 +269,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) return; } - m_yulAnnotation->externalReferences[&_identifier].isSlot = isSlot; - m_yulAnnotation->externalReferences[&_identifier].isOffset = isOffset; + m_yulAnnotation->externalReferences[&_identifier].suffix = move(suffix); m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front(); } @@ -321,8 +311,29 @@ void ReferencesResolver::resolveInheritDoc(StructuredDocumentation const& _docum case 1: { string const& name = _annotation.docTags.find("inheritdoc")->second.content; + if (name.empty()) + { + m_errorReporter.docstringParsingError( + 1933_error, + _documentation.location(), + "Expected contract name following documentation tag @inheritdoc." + ); + return; + } + vector path; boost::split(path, name, boost::is_any_of(".")); + if (any_of(path.begin(), path.end(), [](auto& _str) { return _str.empty(); })) + { + m_errorReporter.docstringParsingError( + 5967_error, + _documentation.location(), + "Documentation tag @inheritdoc reference \"" + + name + + "\" is malformed." + ); + return; + } Declaration const* result = m_resolver.pathFromCurrentScope(path); if (result == nullptr) @@ -369,4 +380,11 @@ void ReferencesResolver::validateYulIdentifierName(yul::YulString _name, SourceL _location, "User-defined identifiers in inline assembly cannot contain '.'." ); + + if (set{"this", "super", "_"}.count(_name.str())) + m_errorReporter.declarationError( + 4113_error, + _location, + "The identifier name \"" + _name.str() + "\" is reserved." + ); } diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 2c984f9b28b6..cd3aaa717eb5 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -82,7 +82,7 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker void endVisit(FunctionDefinition const& _functionDefinition) override; bool visit(ModifierDefinition const& _modifierDefinition) override; void endVisit(ModifierDefinition const& _modifierDefinition) override; - void endVisit(UserDefinedTypeName const& _typeName) override; + void endVisit(IdentifierPath const& _path) override; bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(Return const& _return) override; diff --git a/libsolidity/analysis/Scoper.cpp b/libsolidity/analysis/Scoper.cpp new file mode 100644 index 000000000000..98716cf0f867 --- /dev/null +++ b/libsolidity/analysis/Scoper.cpp @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::frontend; + +void Scoper::assignScopes(ASTNode const& _astRoot) +{ + Scoper scoper; + _astRoot.accept(scoper); +} + +bool Scoper::visit(ContractDefinition const& _contract) +{ + solAssert(m_contract == nullptr, ""); + m_contract = &_contract; + return ASTConstVisitor::visit(_contract); +} + +void Scoper::endVisit(ContractDefinition const& _contract) +{ + solAssert(m_contract == &_contract, ""); + m_contract = nullptr; + ASTConstVisitor::endVisit(_contract); +} + +bool Scoper::visitNode(ASTNode const& _node) +{ + if (auto const* scopable = dynamic_cast(&_node)) + { + scopable->annotation().scope = m_scopes.empty() ? nullptr : m_scopes.back(); + scopable->annotation().contract = m_contract; + } + if (dynamic_cast(&_node)) + m_scopes.push_back(&_node); + return true; +} + +void Scoper::endVisitNode(ASTNode const& _node) +{ + if (dynamic_cast(&_node)) + m_scopes.pop_back(); +} diff --git a/libsolidity/analysis/Scoper.h b/libsolidity/analysis/Scoper.h new file mode 100644 index 000000000000..c55e58040acd --- /dev/null +++ b/libsolidity/analysis/Scoper.h @@ -0,0 +1,45 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +namespace solidity::frontend +{ + +/** + * AST visitor that assigns syntactic scopes. + */ +class Scoper: private ASTConstVisitor +{ +public: + static void assignScopes(ASTNode const& _astRoot); + +private: + bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; + bool visitNode(ASTNode const& _node) override; + void endVisitNode(ASTNode const& _node) override; + + ContractDefinition const* m_contract = nullptr; + std::vector m_scopes; +}; + +} diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 68f52ac26b6c..4931401cbff1 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -156,6 +156,19 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) // This is not a no-op, the entry might pre-exist. m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0; } + + if (_variable.isStateVariable() || _variable.referenceLocation() == VariableDeclaration::Location::Storage) + if (auto varType = dynamic_cast(_variable.annotation().type)) + for (Type const* type: varType->fullDecomposition()) + if (type->storageSizeUpperBound() >= (bigint(1) << 64)) + { + string message = "Type " + type->toString(true) + + " covers a large part of storage and thus makes collisions likely." + " Either use mappings or dynamic arrays and allow their size to be increased only" + " in small quantities per transaction."; + m_errorReporter.warning(7325_error, _variable.typeName().location(), message); + } + return true; } @@ -172,7 +185,7 @@ bool StaticAnalyzer::visit(Return const& _return) bool StaticAnalyzer::visit(ExpressionStatement const& _statement) { - if (_statement.expression().annotation().isPure) + if (*_statement.expression().annotation().isPure) m_errorReporter.warning( 6133_error, _statement.location(), @@ -211,6 +224,17 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) "Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode." ); } + else if ( + m_currentFunction && + m_currentFunction->isReceive() && + type->kind() == MagicType::Kind::Message && + _memberAccess.memberName() == "data" + ) + m_errorReporter.typeError( + 7139_error, + _memberAccess.location(), + R"("msg.data" cannot be used inside of "receive" function.)" + ); } if (_memberAccess.memberName() == "callcode") @@ -274,13 +298,11 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) bool StaticAnalyzer::visit(BinaryOperation const& _operation) { if ( - _operation.rightExpression().annotation().isPure && + *_operation.rightExpression().annotation().isPure && (_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod) ) - if (auto rhs = dynamic_cast( - ConstantEvaluator(m_errorReporter).evaluate(_operation.rightExpression()) - )) - if (rhs->isZero()) + if (auto rhs = ConstantEvaluator::evaluate(m_errorReporter, _operation.rightExpression())) + if (rhs->value == 0) m_errorReporter.typeError( 1211_error, _operation.location(), @@ -292,18 +314,16 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation) bool StaticAnalyzer::visit(FunctionCall const& _functionCall) { - if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind == FunctionCallKind::FunctionCall) { auto functionType = dynamic_cast(_functionCall.expression().annotation().type); solAssert(functionType, ""); if (functionType->kind() == FunctionType::Kind::AddMod || functionType->kind() == FunctionType::Kind::MulMod) { solAssert(_functionCall.arguments().size() == 3, ""); - if (_functionCall.arguments()[2]->annotation().isPure) - if (auto lastArg = dynamic_cast( - ConstantEvaluator(m_errorReporter).evaluate(*(_functionCall.arguments())[2]) - )) - if (lastArg->isZero()) + if (*_functionCall.arguments()[2]->annotation().isPure) + if (auto lastArg = ConstantEvaluator::evaluate(m_errorReporter, *(_functionCall.arguments())[2])) + if (lastArg->value == 0) m_errorReporter.typeError( 4195_error, _functionCall.location(), @@ -311,6 +331,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) ); } if ( + m_currentContract && m_currentContract->isLibrary() && functionType->kind() == FunctionType::Kind::DelegateCall && functionType->declaration().scope() == m_currentContract diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index c0380ffe56c9..9fac5f6d5de7 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -73,6 +73,8 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) // when reporting the warning, print the source name only m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString); } + if (!m_sourceUnit->annotation().useABICoderV2.set()) + m_sourceUnit->annotation().useABICoderV2 = true; m_sourceUnit = nullptr; } @@ -113,9 +115,45 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_sourceUnit->annotation().experimentalFeatures.insert(feature); if (!ExperimentalFeatureWithoutWarning.count(feature)) m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + + if (feature == ExperimentalFeature::ABIEncoderV2) + { + if (m_sourceUnit->annotation().useABICoderV2.set()) + { + if (!*m_sourceUnit->annotation().useABICoderV2) + m_errorReporter.syntaxError( + 8273_error, + _pragma.location(), + "ABI coder v1 has already been selected through \"pragma abicoder v1\"." + ); + } + else + m_sourceUnit->annotation().useABICoderV2 = true; + } } } } + else if (_pragma.literals()[0] == "abicoder") + { + solAssert(m_sourceUnit, ""); + if ( + _pragma.literals().size() != 2 || + !set{"v1", "v2"}.count(_pragma.literals()[1]) + ) + m_errorReporter.syntaxError( + 2745_error, + _pragma.location(), + "Expected either \"pragma abicoder v1\" or \"pragma abicoder v2\"." + ); + else if (m_sourceUnit->annotation().useABICoderV2.set()) + m_errorReporter.syntaxError( + 3845_error, + _pragma.location(), + "ABI coder has already been selected for this source unit." + ); + else + m_sourceUnit->annotation().useABICoderV2 = (_pragma.literals()[1] == "v2"); + } else if (_pragma.literals()[0] == "solidity") { vector tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); @@ -135,6 +173,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) } else m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); + return true; } @@ -190,6 +229,28 @@ void SyntaxChecker::endVisit(ForStatement const&) m_inLoopDepth--; } +bool SyntaxChecker::visit(Block const& _block) +{ + if (_block.unchecked()) + { + if (m_uncheckedArithmetic) + m_errorReporter.syntaxError( + 1941_error, + _block.location(), + "\"unchecked\" blocks cannot be nested." + ); + + m_uncheckedArithmetic = true; + } + return true; +} + +void SyntaxChecker::endVisit(Block const& _block) +{ + if (_block.unchecked()) + m_uncheckedArithmetic = false; +} + bool SyntaxChecker::visit(Continue const& _continueStatement) { if (m_inLoopDepth <= 0) @@ -219,11 +280,12 @@ bool SyntaxChecker::visit(Throw const& _throwStatement) bool SyntaxChecker::visit(Literal const& _literal) { - if ((_literal.token() == Token::UnicodeStringLiteral) && !validateUTF8(_literal.value())) + size_t invalidSequence; + if ((_literal.token() == Token::UnicodeStringLiteral) && !validateUTF8(_literal.value(), invalidSequence)) m_errorReporter.syntaxError( 8452_error, _literal.location(), - "Invalid UTF-8 sequence found" + "Contains invalid UTF-8 sequence at position " + toString(invalidSequence) + "." ); if (_literal.token() != Token::Number) @@ -287,15 +349,22 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly) return false; } -bool SyntaxChecker::visit(PlaceholderStatement const&) +bool SyntaxChecker::visit(PlaceholderStatement const& _placeholder) { + if (m_uncheckedArithmetic) + m_errorReporter.syntaxError( + 2573_error, + _placeholder.location(), + "The placeholder statement \"_\" cannot be used inside an \"unchecked\" block." + ); + m_placeholderFound = true; return true; } bool SyntaxChecker::visit(ContractDefinition const& _contract) { - m_isInterface = _contract.isInterface(); + m_currentContractKind = _contract.contractKind(); ASTString const& contractName = _contract.name(); for (FunctionDefinition const* function: _contract.definedFunctions()) @@ -309,19 +378,41 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract) return true; } +void SyntaxChecker::endVisit(ContractDefinition const&) +{ + m_currentContractKind = std::nullopt; +} + bool SyntaxChecker::visit(FunctionDefinition const& _function) { - if (!_function.isConstructor() && _function.noVisibilitySpecified()) + solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); + + if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified()) { - string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public"; + string suggestedVisibility = + _function.isFallback() || + _function.isReceive() || + m_currentContractKind == ContractKind::Interface + ? "external" : "public"; m_errorReporter.syntaxError( 4937_error, _function.location(), "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" ); } + else if (_function.isFree()) + { + if (!_function.noVisibilitySpecified()) + m_errorReporter.syntaxError( + 4126_error, + _function.location(), + "Free functions cannot have visibility." + ); + if (!_function.isImplemented()) + m_errorReporter.typeError(4668_error, _function.location(), "Free functions must be implemented."); + } - if (m_isInterface && !_function.modifiers().empty()) + if (m_currentContractKind == ContractKind::Interface && !_function.modifiers().empty()) m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers."); else if (!_function.isImplemented() && !_function.modifiers().empty()) m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers."); @@ -342,23 +433,6 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node) return true; } -bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) -{ - // Report if none of the variable components in the tuple have a name (only possible via deprecated "var") - if (std::all_of( - _statement.declarations().begin(), - _statement.declarations().end(), - [](ASTPointer const& declaration) { return declaration == nullptr; } - )) - m_errorReporter.syntaxError( - 3299_error, - _statement.location(), - "The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty." - ); - - return true; -} - bool SyntaxChecker::visit(StructDefinition const& _struct) { if (_struct.members().empty()) diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 1ace2f36a0b7..ca36ed992c83 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -39,6 +39,7 @@ namespace solidity::frontend * - issues deprecation warnings for unary '+' * - issues deprecation warning for throw * - whether the msize instruction is used and the Yul optimizer is enabled at the same time. + * - selection of the ABI coder through pragmas. */ class SyntaxChecker: private ASTConstVisitor { @@ -71,6 +72,9 @@ class SyntaxChecker: private ASTConstVisitor bool visit(ForStatement const& _forStatement) override; void endVisit(ForStatement const& _forStatement) override; + bool visit(Block const& _block) override; + void endVisit(Block const& _block) override; + bool visit(Continue const& _continueStatement) override; bool visit(Break const& _breakStatement) override; @@ -83,11 +87,10 @@ class SyntaxChecker: private ASTConstVisitor bool visit(PlaceholderStatement const& _placeholderStatement) override; bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; bool visit(FunctionDefinition const& _function) override; bool visit(FunctionTypeName const& _node) override; - bool visit(VariableDeclarationStatement const& _statement) override; - bool visit(StructDefinition const& _struct) override; bool visit(Literal const& _literal) override; @@ -101,8 +104,11 @@ class SyntaxChecker: private ASTConstVisitor /// Flag that indicates whether some version pragma was present. bool m_versionPragmaFound = false; + /// Flag that indicates whether we are inside an unchecked block. + bool m_uncheckedArithmetic = false; + int m_inLoopDepth = 0; - bool m_isInterface = false; + std::optional m_currentContractKind; SourceUnit const* m_sourceUnit = nullptr; }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 5e1b6c7d737f..79cc3b25fee0 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include @@ -226,16 +226,13 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio { vector> arguments = _functionCall.arguments(); if (arguments.size() != 1) - { - m_errorReporter.typeError( + m_errorReporter.fatalTypeError( 8885_error, _functionCall.location(), "This function takes one argument, but " + toString(arguments.size()) + " were provided." ); - return {}; - } TypePointer firstArgType = type(*arguments.front()); bool wrongType = false; @@ -243,26 +240,22 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio { TypeType const* typeTypePtr = dynamic_cast(firstArgType); Type::Category typeCategory = typeTypePtr->actualType()->category(); - if ( - typeCategory != Type::Category::Contract && - typeCategory != Type::Category::Integer - ) + if (auto const* contractType = dynamic_cast(typeTypePtr->actualType())) + wrongType = contractType->isSuper(); + else if (typeCategory != Type::Category::Integer) wrongType = true; } else wrongType = true; if (wrongType) - { - m_errorReporter.typeError( + m_errorReporter.fatalTypeError( 4259_error, arguments.front()->location(), "Invalid type for argument in the function call. " "A contract type or an integer type is required, but " + type(*arguments.front())->toString(true) + " provided." ); - return {}; - } return {TypeProvider::meta(dynamic_cast(*firstArgType).actualType())}; } @@ -271,13 +264,11 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast(&dereference(_inheritance.name())); solAssert(base, "Base contract not available."); + solAssert(m_currentContract, ""); if (m_currentContract->isInterface() && !base->isInterface()) m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); - if (base->isLibrary()) - m_errorReporter.typeError(2571_error, _inheritance.location(), "Libraries cannot be inherited from."); - auto const& arguments = _inheritance.arguments(); TypePointers parameterTypes; if (!base->isInterface()) @@ -319,6 +310,15 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) void TypeChecker::endVisit(ModifierDefinition const& _modifier) { + if (_modifier.virtualSemantics()) + if (auto const* contractDef = dynamic_cast(_modifier.scope())) + if (contractDef->isLibrary()) + m_errorReporter.typeError( + 3275_error, + _modifier.location(), + "Modifiers in a library cannot be virtual." + ); + if (!_modifier.isImplemented() && !_modifier.virtualSemantics()) m_errorReporter.typeError(8063_error, _modifier.location(), "Modifiers without implementation must be marked virtual."); } @@ -327,7 +327,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) { if (_function.markedVirtual()) { - if (_function.isConstructor()) + if (_function.isFree()) + m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual."); + else if (_function.isConstructor()) m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); else if (_function.annotation().contract->isInterface()) m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); @@ -336,12 +338,19 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); } + if (_function.overrides() && _function.isFree()) + m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override."); + + if (!_function.modifiers().empty() && _function.isFree()) + m_errorReporter.syntaxError(5811_error, _function.location(), "Free functions cannot have modifiers."); if (_function.isPayable()) { if (_function.libraryFunction()) m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); - if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) + else if (_function.isFree()) + m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable."); + else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); } @@ -382,13 +391,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function) m_errorReporter.typeError(4103_error, _var.location(), message); } else if ( - !experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) && + !useABICoderV2() && !typeSupportedByOldABIEncoder(*type(_var), _function.libraryFunction()) ) { string message = - "This type is only supported in ABIEncoderV2. " - "Use \"pragma experimental ABIEncoderV2;\" to enable the feature."; + "This type is only supported in ABI coder v2. " + "Use \"pragma abicoder v2;\" to enable the feature."; if (_function.isConstructor()) message += " Alternatively, make the contract abstract and supply the " @@ -415,15 +424,19 @@ bool TypeChecker::visit(FunctionDefinition const& _function) set modifiers; for (ASTPointer const& modifier: _function.modifiers()) { - auto baseContracts = dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts; - // Delete first base which is just the main contract itself - baseContracts.erase(baseContracts.begin()); + vector baseContracts; + if (auto contract = dynamic_cast(_function.scope())) + { + baseContracts = contract->annotation().linearizedBaseContracts; + // Delete first base which is just the main contract itself + baseContracts.erase(baseContracts.begin()); + } visitManually( *modifier, _function.isConstructor() ? baseContracts : vector() ); - Declaration const* decl = &dereference(*modifier->name()); + Declaration const* decl = &dereference(modifier->name()); if (modifiers.count(decl)) { if (dynamic_cast(decl)) @@ -432,7 +445,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else modifiers.insert(decl); } - if (m_currentContract->isInterface()) + + solAssert(_function.isFree() == !m_currentContract, ""); + if (!m_currentContract) + { + solAssert(!_function.isConstructor(), ""); + solAssert(!_function.isFallback(), ""); + solAssert(!_function.isReceive(), ""); + } + else if (m_currentContract->isInterface()) { if (_function.isImplemented()) m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); @@ -445,6 +466,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (m_currentContract->contractKind() == ContractKind::Library) if (_function.isConstructor()) m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); + if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) @@ -452,7 +474,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); else if (!_function.virtualSemantics()) - m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + { + if (_function.isFree()) + solAssert(m_errorReporter.hasErrors(), ""); + else + m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + } if (_function.isFallback()) @@ -474,9 +501,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) TypePointer varType = _variable.annotation().type; solAssert(!!varType, "Variable type not provided."); - if (auto contractType = dynamic_cast(varType)) - if (contractType->contractDefinition().isLibrary()) - m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library."); if (_variable.value()) { if (_variable.isStateVariable() && varType->containsNestedMapping()) @@ -504,7 +528,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!_variable.value()) m_errorReporter.typeError(4266_error, _variable.location(), "Uninitialized \"constant\" variable."); - else if (!_variable.value()->annotation().isPure) + else if (!*_variable.value()->annotation().isPure) m_errorReporter.typeError( 8349_error, _variable.value()->location(), @@ -539,7 +563,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) else if (_variable.visibility() >= Visibility::Public) { FunctionType getter(_variable); - if (!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + if (!useABICoderV2()) { vector unsupportedTypes; for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes()) @@ -549,9 +573,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) m_errorReporter.typeError( 2763_error, _variable.location(), - "The following types are only supported for getters in ABIEncoderV2: " + + "The following types are only supported for getters in ABI coder v2: " + joinHumanReadable(unsupportedTypes) + - ". Either remove \"public\" or use \"pragma experimental ABIEncoderV2;\" to enable the feature." + ". Either remove \"public\" or use \"pragma abicoder v2;\" to enable the feature." ); } if (!getter.interfaceFunctionType()) @@ -565,8 +589,13 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (auto referenceType = dynamic_cast(varType)) { auto result = referenceType->validForLocation(referenceType->location()); - if (result && (_variable.isConstructorParameter() || _variable.isPublicCallableParameter())) - result = referenceType->validForLocation(DataLocation::CallData); + if (result) + { + bool isLibraryStorageParameter = (_variable.isLibraryFunctionParameter() && referenceType->location() == DataLocation::Storage); + bool callDataCheckRequired = ((_variable.isConstructorParameter() || _variable.isPublicCallableParameter()) && !isLibraryStorageParameter); + if (callDataCheckRequired) + result = referenceType->validForLocation(DataLocation::CallData); + } if (!result) { solAssert(!result.message().empty(), "Expected detailed error message"); @@ -575,29 +604,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) } } - if (varType->dataStoredIn(DataLocation::Storage)) - { - auto collisionMessage = [&](string const& variableOrType, bool isVariable) -> string { - return - (isVariable ? "Variable " : "Type ") + - util::escapeAndQuoteString(variableOrType) + - " covers a large part of storage and thus makes collisions likely." - " Either use mappings or dynamic arrays and allow their size to be increased only" - " in small quantities per transaction."; - }; - - if (varType->storageSizeUpperBound() >= bigint(1) << 64) - { - if (_variable.isStateVariable()) - m_errorReporter.warning(3408_error, _variable.location(), collisionMessage(_variable.name(), true)); - else - m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->canonicalName(), false)); - } - vector oversizedSubtypes = frontend::oversizedSubtypes(*varType); - for (Type const* subtype: oversizedSubtypes) - m_errorReporter.warning(7325_error, _variable.typeName().location(), collisionMessage(subtype->canonicalName(), false)); - } - return false; } @@ -611,13 +617,25 @@ void TypeChecker::visitManually( for (ASTPointer const& argument: arguments) argument->accept(*this); - _modifier.name()->accept(*this); + _modifier.name().accept(*this); - auto const* declaration = &dereference(*_modifier.name()); + auto const* declaration = &dereference(_modifier.name()); vector> emptyParameterList; vector> const* parameters = nullptr; if (auto modifierDecl = dynamic_cast(declaration)) + { parameters = &modifierDecl->parameters(); + if (auto const* modifierContract = dynamic_cast(modifierDecl->scope())) + if (m_currentContract) + { + if (!contains(m_currentContract->annotation().linearizedBaseContracts, modifierContract)) + m_errorReporter.typeError( + 9428_error, + _modifier.location(), + "Can only use modifiers defined in the current contract or in base contracts." + ); + } + } else // check parameters for Base constructors for (ContractDefinition const* base: _bases) @@ -682,14 +700,14 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) if (!type(*var)->interfaceType(false)) m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type."); if ( - !experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) && + !useABICoderV2() && !typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */) ) m_errorReporter.typeError( 3061_error, var->location(), - "This type is only supported in ABIEncoderV2. " - "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." + "This type is only supported in ABI coder v2. " + "Use \"pragma abicoder v2;\" to enable the feature." ); } if (_eventDef.isAnonymous() && numIndexed > 4) @@ -730,7 +748,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second; Declaration const* declaration = identifierInfo.declaration; solAssert(!!declaration, ""); - bool requiresStorage = identifierInfo.isSlot || identifierInfo.isOffset; if (auto var = dynamic_cast(declaration)) { solAssert(var->type(), "Expected variable type!"); @@ -753,7 +770,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to."); return false; } - else if (requiresStorage) + else if (!identifierInfo.suffix.empty()) { m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes .offset and .slot can only be used on non-constant storage variables."); return false; @@ -779,51 +796,80 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!dynamic_cast(var->type()), "FixedPointType not implemented."); - if (requiresStorage) + if (!identifierInfo.suffix.empty()) { - if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) + string const& suffix = identifierInfo.suffix; + solAssert((set{"offset", "slot", "length"}).count(suffix), ""); + if (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables."); - return false; - } - else if (_context == yul::IdentifierContext::LValue) - { - if (var->isStateVariable()) + if (suffix != "slot" && suffix != "offset") { - m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); + m_errorReporter.typeError(4656_error, _identifier.location, "State variables only support \".slot\" and \".offset\"."); return false; } - else if (identifierInfo.isOffset) + else if (_context == yul::IdentifierContext::LValue) { - m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to."); + if (var->isStateVariable()) + { + m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); + return false; + } + else if (suffix != "slot") + { + m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to."); + return false; + } + } + } + else if ( + auto const* arrayType = dynamic_cast(var->type()); + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) + ) + { + if (suffix != "offset" && suffix != "length") + { + m_errorReporter.typeError(1536_error, _identifier.location, "Calldata variables only support \".offset\" and \".length\"."); return false; } - else - solAssert(identifierInfo.isSlot, ""); + } + else + { + m_errorReporter.typeError(3622_error, _identifier.location, "The suffix \"." + suffix + "\" is not supported by this variable or type."); + return false; } } else if (!var->isConstant() && var->isStateVariable()) { - m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the .slot and .offset suffixes."); + m_errorReporter.typeError( + 1408_error, + _identifier.location, + "Only local variables are supported. To access storage variables, use the \".slot\" and \".offset\" suffixes." + ); return false; } else if (var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the .slot or .offset suffix to access storage reference variables."); + m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables."); return false; } else if (var->type()->sizeOnStack() != 1) { - if (var->type()->dataStoredIn(DataLocation::CallData)) - m_errorReporter.typeError(2370_error, _identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes."); + if ( + auto const* arrayType = dynamic_cast(var->type()); + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) + ) + m_errorReporter.typeError(1397_error, _identifier.location, "Call data elements cannot be accessed directly. Use \".offset\" and \".length\" to access the calldata offset and length of this array and then use \"calldatacopy\"."); else + { + solAssert(!var->type()->dataStoredIn(DataLocation::CallData), ""); m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported."); + } return false; } } - else if (requiresStorage) + else if (!identifierInfo.suffix.empty()) { - m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes .offset and .slot can only be used on storage variables."); + m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."); return false; } else if (_context == yul::IdentifierContext::LValue) @@ -885,7 +931,7 @@ bool TypeChecker::visit(IfStatement const& _ifStatement) void TypeChecker::endVisit(TryStatement const& _tryStatement) { FunctionCall const* externalCall = dynamic_cast(&_tryStatement.externalCall()); - if (!externalCall || externalCall->annotation().kind != FunctionCallKind::FunctionCall) + if (!externalCall || *externalCall->annotation().kind != FunctionCallKind::FunctionCall) { m_errorReporter.typeError( 5347_error, @@ -1095,7 +1141,7 @@ void TypeChecker::endVisit(Return const& _return) void TypeChecker::endVisit(EmitStatement const& _emit) { if ( - _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || + *_emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || type(_emit.eventCall().expression())->category() != Type::Category::Function || dynamic_cast(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event ) @@ -1285,11 +1331,14 @@ bool TypeChecker::visit(Conditional const& _conditional) } } + _conditional.annotation().isConstant = false; _conditional.annotation().type = commonType; _conditional.annotation().isPure = - _conditional.condition().annotation().isPure && - _conditional.trueExpression().annotation().isPure && - _conditional.falseExpression().annotation().isPure; + *_conditional.condition().annotation().isPure && + *_conditional.trueExpression().annotation().isPure && + *_conditional.falseExpression().annotation().isPure; + + _conditional.annotation().isLValue = false; if (_conditional.annotation().willBeWrittenTo) m_errorReporter.typeError( @@ -1343,6 +1392,9 @@ bool TypeChecker::visit(Assignment const& _assignment) ); TypePointer t = type(_assignment.leftHandSide()); _assignment.annotation().type = t; + _assignment.annotation().isPure = false; + _assignment.annotation().isLValue = false; + _assignment.annotation().isConstant = false; checkExpressionAssignment(*t, _assignment.leftHandSide()); @@ -1390,6 +1442,7 @@ bool TypeChecker::visit(Assignment const& _assignment) bool TypeChecker::visit(TupleExpression const& _tuple) { + _tuple.annotation().isConstant = false; vector> const& components = _tuple.components(); TypePointers types; @@ -1414,6 +1467,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) _tuple.annotation().type = TypeProvider::tuple(move(types)); // If some of the components are not LValues, the error is reported above. _tuple.annotation().isLValue = true; + _tuple.annotation().isPure = false; } else { @@ -1453,7 +1507,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) else if (inlineArrayType) inlineArrayType = Type::commonType(inlineArrayType, types[i]); } - if (!components[i]->annotation().isPure) + if (!*components[i]->annotation().isPure) isPure = false; } _tuple.annotation().isPure = isPure; @@ -1484,6 +1538,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) _tuple.annotation().type = TypeProvider::tuple(move(types)); } + _tuple.annotation().isLValue = false; } return false; } @@ -1511,7 +1566,10 @@ bool TypeChecker::visit(UnaryOperation const& _operation) t = subExprType; } _operation.annotation().type = t; - _operation.annotation().isPure = !modifying && _operation.subExpression().annotation().isPure; + _operation.annotation().isConstant = false; + _operation.annotation().isPure = !modifying && *_operation.subExpression().annotation().isPure; + _operation.annotation().isLValue = false; + return false; } @@ -1542,8 +1600,10 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) TypeProvider::boolean() : commonType; _operation.annotation().isPure = - _operation.leftExpression().annotation().isPure && - _operation.rightExpression().annotation().isPure; + *_operation.leftExpression().annotation().isPure && + *_operation.rightExpression().annotation().isPure; + _operation.annotation().isLValue = false; + _operation.annotation().isConstant = false; if (_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL) { @@ -1591,7 +1651,7 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( FunctionCall const& _functionCall ) { - solAssert(_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + solAssert(*_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); TypePointer const& expressionType = type(_functionCall.expression()); vector> const& arguments = _functionCall.arguments(); @@ -1621,7 +1681,8 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( dataLoc = argRefType->location(); if (auto type = dynamic_cast(resultType)) resultType = TypeProvider::withLocation(type, dataLoc, type->isPointer()); - if (argType->isExplicitlyConvertibleTo(*resultType)) + BoolResult result = argType->isExplicitlyConvertibleTo(*resultType); + if (result) { if (auto argArrayType = dynamic_cast(argType)) { @@ -1692,24 +1753,17 @@ TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( "you can use the .address member of the function." ); else - m_errorReporter.typeError( + m_errorReporter.typeErrorConcatenateDescriptions( 9640_error, _functionCall.location(), "Explicit type conversion not allowed from \"" + argType->toString() + "\" to \"" + resultType->toString() + - "\"." + "\".", + result.message() ); } - if (auto addressType = dynamic_cast(resultType)) - if (addressType->stateMutability() != StateMutability::Payable) - { - bool payable = false; - if (argType->category() != Type::Category::Address) - payable = argType->isExplicitlyConvertibleTo(*TypeProvider::payableAddress()); - resultType = payable ? TypeProvider::payableAddress() : TypeProvider::address(); - } } return resultType; } @@ -1726,7 +1780,9 @@ void TypeChecker::typeCheckFunctionCall( if (_functionType->kind() == FunctionType::Kind::Declaration) { + solAssert(_functionType->declaration().annotation().contract, ""); if ( + m_currentContract && m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) && !dynamic_cast(_functionType->declaration()).isImplemented() ) @@ -1775,15 +1831,21 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function) ); if (_function.visibility() != Visibility::External) m_errorReporter.typeError(1159_error, _function.location(), "Fallback function must be defined as \"external\"."); - if (!_function.returnParameters().empty()) + + if (!_function.returnParameters().empty() || !_function.parameters().empty()) { - if (_function.returnParameters().size() > 1 || *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory()) - m_errorReporter.typeError(5570_error, _function.returnParameterList()->location(), "Fallback function can only have a single \"bytes memory\" return value."); - else - m_errorReporter.typeError(6151_error, _function.returnParameterList()->location(), "Return values for fallback functions are not yet implemented."); + if ( + _function.returnParameters().size() != 1 || + *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory() || + _function.parameters().size() != 1 || + *type(*_function.parameters().front()) != *TypeProvider::bytesCalldata() + ) + m_errorReporter.typeError( + 5570_error, + _function.returnParameterList()->location(), + "Fallback function either has to have the signature \"fallback()\" or \"fallback(bytes calldata) returns (bytes memory)\"." + ); } - if (!_function.parameters().empty()) - m_errorReporter.typeError(3978_error, _function.parameterList().location(), "Fallback function cannot take parameters."); } void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function) @@ -1869,7 +1931,7 @@ void TypeChecker::typeCheckABIEncodeFunctions( bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked; solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding"); - bool const abiEncoderV2 = experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2); + bool const abiEncoderV2 = useABICoderV2(); // Check for named arguments if (!_functionCall.names().empty()) @@ -1956,8 +2018,10 @@ void TypeChecker::typeCheckFunctionGeneralChecks( bool const isPositionalCall = _functionCall.names().empty(); bool const isVariadic = _functionType->takesArbitraryParameters(); + auto functionCallKind = *_functionCall.annotation().kind; + solAssert( - !isVariadic || _functionCall.annotation().kind == FunctionCallKind::FunctionCall, + !isVariadic || functionCallKind == FunctionCallKind::FunctionCall, "Struct constructor calls cannot be variadic." ); @@ -1972,7 +2036,7 @@ void TypeChecker::typeCheckFunctionGeneralChecks( ) { bool const isStructConstructorCall = - _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + functionCallKind == FunctionCallKind::StructConstructorCall; auto [errorId, description] = [&]() -> tuple { string msg = isVariadic ? @@ -2076,18 +2140,17 @@ void TypeChecker::typeCheckFunctionGeneralChecks( { bool not_all_mapped = false; - for (size_t i = 0; i < paramArgMap.size(); i++) + for (size_t i = 0; i < argumentNames.size(); i++) { size_t j; - for (j = 0; j < argumentNames.size(); j++) - if (parameterNames[i] == *argumentNames[j]) + for (j = 0; j < parameterNames.size(); j++) + if (parameterNames[j] == *argumentNames[i]) break; - if (j < argumentNames.size()) - paramArgMap[i] = arguments[j].get(); + if (j < parameterNames.size()) + paramArgMap[j] = arguments[i].get(); else { - paramArgMap[i] = nullptr; not_all_mapped = true; m_errorReporter.typeError( 4974_error, @@ -2108,7 +2171,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks( for (size_t i = 0; i < paramArgMap.size(); ++i) { solAssert(!!paramArgMap[i], "unmapped parameter"); - if (!type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) + BoolResult result = type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i]); + if (!result) { auto [errorId, description] = [&]() -> tuple { string msg = @@ -2118,6 +2182,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks( " to " + parameterTypes[i]->toString() + " requested."; + if (!result.message().empty()) + msg += " " + result.message(); if ( _functionType->kind() == FunctionType::Kind::BareCall || _functionType->kind() == FunctionType::Kind::BareCallCode || @@ -2149,6 +2215,52 @@ void TypeChecker::typeCheckFunctionGeneralChecks( m_errorReporter.typeError(errorId, paramArgMap[i]->location(), description); } } + + TypePointers const& returnParameterTypes = _functionType->returnParameterTypes(); + bool isLibraryCall = (_functionType->kind() == FunctionType::Kind::DelegateCall); + bool callRequiresABIEncoding = + // ABIEncode/ABIDecode calls not included because they should have been already validated + // at this point and they have variadic arguments so they need special handling. + _functionType->kind() == FunctionType::Kind::DelegateCall || + _functionType->kind() == FunctionType::Kind::External || + _functionType->kind() == FunctionType::Kind::Creation || + _functionType->kind() == FunctionType::Kind::Event; + + if (callRequiresABIEncoding && !useABICoderV2()) + { + solAssert(!isVariadic, ""); + solAssert(parameterTypes.size() == arguments.size(), ""); + solAssert(!_functionType->isBareCall(), ""); + solAssert(*_functionCall.annotation().kind == FunctionCallKind::FunctionCall, ""); + + for (size_t i = 0; i < parameterTypes.size(); ++i) + { + solAssert(parameterTypes[i], ""); + + if (!typeSupportedByOldABIEncoder(*parameterTypes[i], isLibraryCall)) + m_errorReporter.typeError( + 2443_error, + paramArgMap[i]->location(), + "The type of this parameter, " + parameterTypes[i]->toString(true) + ", " + "is only supported in ABI coder v2. " + "Use \"pragma abicoder v2;\" to enable the feature." + ); + } + + for (size_t i = 0; i < returnParameterTypes.size(); ++i) + { + solAssert(returnParameterTypes[i], ""); + + if (!typeSupportedByOldABIEncoder(*returnParameterTypes[i], isLibraryCall)) + m_errorReporter.typeError( + 2428_error, + _functionCall.location(), + "The type of return parameter " + toString(i + 1) + ", " + returnParameterTypes[i]->toString(true) + ", " + "is only supported in ABI coder v2. " + "Use \"pragma abicoder v2;\" to enable the feature." + ); + } + } } bool TypeChecker::visit(FunctionCall const& _functionCall) @@ -2160,7 +2272,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) for (ASTPointer const& argument: arguments) { argument->accept(*this); - if (!argument->annotation().isPure) + if (!*argument->annotation().isPure) argumentsArePure = false; } @@ -2183,6 +2295,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) // Determine function call kind and function type for this FunctionCall node FunctionCallAnnotation& funcCallAnno = _functionCall.annotation(); FunctionTypePointer functionType = nullptr; + funcCallAnno.isConstant = false; + + bool isLValue = false; // Determine and assign function call kind, lvalue, purity and function type for this FunctionCall node switch (expressionType->category()) @@ -2191,18 +2306,26 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) functionType = dynamic_cast(expressionType); funcCallAnno.kind = FunctionCallKind::FunctionCall; + if (auto memberAccess = dynamic_cast(&_functionCall.expression())) + { + if (dynamic_cast(memberAccess->annotation().referencedDeclaration)) + _functionCall.expression().annotation().calledDirectly = true; + } + else if (auto identifier = dynamic_cast(&_functionCall.expression())) + if (dynamic_cast(identifier->annotation().referencedDeclaration)) + _functionCall.expression().annotation().calledDirectly = true; + // Purity for function calls also depends upon the callee and its FunctionType funcCallAnno.isPure = argumentsArePure && - _functionCall.expression().annotation().isPure && - functionType && + *_functionCall.expression().annotation().isPure && functionType->isPure(); if ( functionType->kind() == FunctionType::Kind::ArrayPush || functionType->kind() == FunctionType::Kind::ByteArrayPush ) - funcCallAnno.isLValue = functionType->parameterTypes().empty(); + isLValue = functionType->parameterTypes().empty(); break; @@ -2222,26 +2345,36 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) ); functionType = dynamic_cast(*actualType).constructorType(); funcCallAnno.kind = FunctionCallKind::StructConstructorCall; - funcCallAnno.isPure = argumentsArePure; } else { + if (auto const* contractType = dynamic_cast(actualType)) + if (contractType->isSuper()) + m_errorReporter.fatalTypeError( + 1744_error, + _functionCall.location(), + "Cannot convert to the super type." + ); funcCallAnno.kind = FunctionCallKind::TypeConversion; - funcCallAnno.isPure = argumentsArePure; } + funcCallAnno.isPure = argumentsArePure; + break; } default: m_errorReporter.fatalTypeError(5704_error, _functionCall.location(), "Type is not callable"); - funcCallAnno.kind = FunctionCallKind::Unset; + // Unreachable, because fatalTypeError throws. We don't set kind, but that's okay because the switch below + // is never reached. And, even if it was, SetOnce would trigger an assertion violation and not UB. funcCallAnno.isPure = argumentsArePure; break; } + funcCallAnno.isLValue = isLValue; + // Determine return types - switch (funcCallAnno.kind) + switch (*funcCallAnno.kind) { case FunctionCallKind::TypeConversion: funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall); @@ -2258,7 +2391,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) { returnTypes = typeCheckABIDecodeAndRetrieveReturnType( _functionCall, - experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) + useABICoderV2() ); break; } @@ -2291,7 +2424,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) break; } - case FunctionCallKind::Unset: // fall-through default: // for non-callables, ensure error reported and annotate node to void function solAssert(m_errorReporter.hasErrors(), ""); @@ -2311,6 +2443,10 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) _functionCallOptions.expression().accept(*this); + _functionCallOptions.annotation().isPure = false; + _functionCallOptions.annotation().isConstant = false; + _functionCallOptions.annotation().isLValue = false; + auto expressionFunctionType = dynamic_cast(type(_functionCallOptions.expression())); if (!expressionFunctionType) { @@ -2341,14 +2477,24 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) return false; } - auto setCheckOption = [&](bool& _option, string const&& _name, bool _alreadySet = false) + if ( + expressionFunctionType->valueSet() || + expressionFunctionType->gasSet() || + expressionFunctionType->saltSet() + ) + m_errorReporter.typeError( + 1645_error, + _functionCallOptions.location(), + "Function call options have already been set, you have to combine them into a single " + "{...}-option." + ); + + auto setCheckOption = [&](bool& _option, string const& _name) { - if (_option || _alreadySet) + if (_option) m_errorReporter.typeError( 9886_error, _functionCallOptions.location(), - _alreadySet ? - "Option \"" + std::move(_name) + "\" has already been set." : "Duplicate option \"" + std::move(_name) + "\"." ); @@ -2362,7 +2508,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { if (kind == FunctionType::Kind::Creation) { - setCheckOption(setSalt, "salt", expressionFunctionType->saltSet()); + setCheckOption(setSalt, "salt"); expectType(*_functionCallOptions.options()[i], *TypeProvider::fixedBytes(32)); } else @@ -2400,7 +2546,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { expectType(*_functionCallOptions.options()[i], *TypeProvider::uint256()); - setCheckOption(setValue, "value", expressionFunctionType->valueSet()); + setCheckOption(setValue, "value"); } } else if (name == "gas") @@ -2415,7 +2561,7 @@ bool TypeChecker::visit(FunctionCallOptions const& _functionCallOptions) { expectType(*_functionCallOptions.options()[i], *TypeProvider::uint256()); - setCheckOption(setGas, "gas", expressionFunctionType->gasSet()); + setCheckOption(setGas, "gas"); } } else @@ -2442,9 +2588,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) TypePointer type = _newExpression.typeName().annotation().type; solAssert(!!type, "Type name not resolved."); + _newExpression.annotation().isConstant = false; + _newExpression.annotation().isLValue = false; + if (auto contractName = dynamic_cast(&_newExpression.typeName())) { - auto contract = dynamic_cast(&dereference(*contractName)); + auto contract = dynamic_cast(&dereference(contractName->pathNode())); if (!contract) m_errorReporter.fatalTypeError(5540_error, _newExpression.location(), "Identifier is not a contract."); @@ -2453,20 +2602,26 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (contract->abstract()) m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); - solAssert(!!m_currentContract, ""); - m_currentContract->annotation().contractDependencies.insert(contract); - solAssert( - !contract->annotation().linearizedBaseContracts.empty(), - "Linearized base contracts not yet available." - ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4579_error, - _newExpression.location(), - "Circular reference for contract creation (cannot create instance of derived or same contract)." + if (m_currentContract) + { + // TODO this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. + m_currentContract->annotation().contractDependencies.insert(contract); + solAssert( + !contract->annotation().linearizedBaseContracts.empty(), + "Linearized base contracts not yet available." ); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4579_error, + _newExpression.location(), + "Circular reference for contract creation (cannot create instance of derived or same contract)." + ); + } _newExpression.annotation().type = FunctionType::newExpressionType(*contract); + _newExpression.annotation().isPure = false; } else if (type->category() == Type::Category::Array) { @@ -2495,7 +2650,10 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) _newExpression.annotation().isPure = true; } else + { + _newExpression.annotation().isPure = false; m_errorReporter.fatalTypeError(8807_error, _newExpression.location(), "Contract or array type expected."); + } } bool TypeChecker::visit(MemberAccess const& _memberAccess) @@ -2504,9 +2662,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) TypePointer exprType = type(_memberAccess.expression()); ASTString const& memberName = _memberAccess.memberName(); + auto& annotation = _memberAccess.annotation(); + // Retrieve the types of the arguments if this is used to call a function. - auto const& arguments = _memberAccess.annotation().arguments; - MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName); + auto const& arguments = annotation.arguments; + MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName); size_t const initialMemberCount = possibleMembers.size(); if (initialMemberCount > 1 && arguments) { @@ -2521,7 +2681,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ++it; } - auto& annotation = _memberAccess.annotation(); + annotation.isConstant = false; if (possibleMembers.empty()) { @@ -2532,7 +2692,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) DataLocation::Storage, exprType ); - if (!storageType->members(m_currentContract).membersByName(memberName).empty()) + if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty()) m_errorReporter.fatalTypeError( 4994_error, _memberAccess.location(), @@ -2619,6 +2779,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) annotation.referencedDeclaration = possibleMembers.front().declaration; annotation.type = possibleMembers.front().type; + VirtualLookup requiredLookup = VirtualLookup::Static; + if (auto funType = dynamic_cast(annotation.type)) { solAssert( @@ -2637,8 +2799,29 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "Using \"." + memberName + "(...)\" is deprecated. Use \"{" + memberName + ": ...}\" instead." ); + + if ( + funType->kind() == FunctionType::Kind::ArrayPush && + arguments.value().numArguments() != 0 && + exprType->containsNestedMapping() + ) + m_errorReporter.typeError( + 8871_error, + _memberAccess.location(), + "Storage arrays with nested mappings do not support .push()." + ); + + if (!funType->bound()) + if (auto typeType = dynamic_cast(exprType)) + { + auto contractType = dynamic_cast(typeType->actualType()); + if (contractType && contractType->isSuper()) + requiredLookup = VirtualLookup::Super; + } } + annotation.requiredLookup = requiredLookup; + if (auto const* structType = dynamic_cast(exprType)) annotation.isLValue = !structType->dataStoredIn(DataLocation::CallData); else if (exprType->category() == Type::Category::Array) @@ -2655,11 +2838,18 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) functionType && functionType->kind() == FunctionType::Kind::Declaration ) - annotation.isPure = _memberAccess.expression().annotation().isPure; + annotation.isPure = *_memberAccess.expression().annotation().isPure; } + else + annotation.isLValue = false; } else if (exprType->category() == Type::Category::Module) - annotation.isPure = _memberAccess.expression().annotation().isPure; + { + annotation.isPure = *_memberAccess.expression().annotation().isPure; + annotation.isLValue = false; + } + else + annotation.isLValue = false; // TODO some members might be pure, but for example `address(0x123).balance` is not pure // although every subexpression is, so leaving this limited for now. @@ -2675,11 +2865,14 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ) if (auto const* parentAccess = dynamic_cast(&_memberAccess.expression())) { - annotation.isPure = parentAccess->expression().annotation().isPure; + bool isPure = *parentAccess->expression().annotation().isPure; if (auto const* exprInt = dynamic_cast(&parentAccess->expression())) if (exprInt->name() == "this" || exprInt->name() == "super") - annotation.isPure = true; + isPure = true; + + annotation.isPure = isPure; } + if (auto magicType = dynamic_cast(exprType)) { if (magicType->kind() == MagicType::Kind::ABI) @@ -2690,8 +2883,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) { annotation.isPure = true; ContractType const& accessedContractType = dynamic_cast(*magicType->typeArgument()); - m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); - + solAssert(!accessedContractType.isSuper(), ""); if ( memberName == "runtimeCode" && !accessedContractType.immutableVariables().empty() @@ -2701,13 +2893,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "\"runtimeCode\" is not available for contracts containing immutable variables." ); + if (m_currentContract) + { + // TODO in the same way as with ``new``, + // this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4224_error, - _memberAccess.location(), - "Circular reference for contract code access." - ); + m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4224_error, + _memberAccess.location(), + "Circular reference for contract code access." + ); + } } else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name") annotation.isPure = true; @@ -2718,18 +2918,39 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) (memberName == "min" || memberName == "max") ) annotation.isPure = true; + else if (magicType->kind() == MagicType::Kind::Block && memberName == "chainid" && !m_evmVersion.hasChainID()) + m_errorReporter.typeError( + 3081_error, + _memberAccess.location(), + "\"chainid\" is not supported by the VM version." + ); } + if ( + _memberAccess.expression().annotation().type->category() == Type::Category::Address && + memberName == "codehash" && + !m_evmVersion.hasExtCodeHash() + ) + m_errorReporter.typeError( + 7598_error, + _memberAccess.location(), + "\"codehash\" is not supported by the VM version." + ); + + if (!annotation.isPure.set()) + annotation.isPure = false; + return false; } bool TypeChecker::visit(IndexAccess const& _access) { + _access.annotation().isConstant = false; _access.baseExpression().accept(*this); TypePointer baseType = type(_access.baseExpression()); TypePointer resultType = nullptr; bool isLValue = false; - bool isPure = _access.baseExpression().annotation().isPure; + bool isPure = *_access.baseExpression().annotation().isPure; Expression const* index = _access.indexExpression(); switch (baseType->category()) { @@ -2780,8 +3001,9 @@ bool TypeChecker::visit(IndexAccess const& _access) case Type::Category::TypeType: { TypeType const& typeType = dynamic_cast(*baseType); - if (dynamic_cast(typeType.actualType())) - m_errorReporter.typeError(2876_error, _access.location(), "Index access for contracts or libraries is not possible."); + if (auto const* contractType = dynamic_cast(typeType.actualType())) + if (contractType->contractDefinition().isLibrary()) + m_errorReporter.typeError(2876_error, _access.location(), "Index access for library types and arrays of libraries are not possible."); if (!index) resultType = TypeProvider::typeType(TypeProvider::array(DataLocation::Memory, typeType.actualType())); else @@ -2831,7 +3053,7 @@ bool TypeChecker::visit(IndexAccess const& _access) } _access.annotation().type = resultType; _access.annotation().isLValue = isLValue; - if (index && !index->annotation().isPure) + if (index && !*index->annotation().isPure) isPure = false; _access.annotation().isPure = isPure; @@ -2840,24 +3062,28 @@ bool TypeChecker::visit(IndexAccess const& _access) bool TypeChecker::visit(IndexRangeAccess const& _access) { + _access.annotation().isConstant = false; _access.baseExpression().accept(*this); bool isLValue = false; // TODO: set this correctly when implementing slices for memory and storage arrays - bool isPure = _access.baseExpression().annotation().isPure; + bool isPure = *_access.baseExpression().annotation().isPure; if (Expression const* start = _access.startExpression()) { expectType(*start, *TypeProvider::uint256()); - if (!start->annotation().isPure) + if (!*start->annotation().isPure) isPure = false; } if (Expression const* end = _access.endExpression()) { expectType(*end, *TypeProvider::uint256()); - if (!end->annotation().isPure) + if (!*end->annotation().isPure) isPure = false; } + _access.annotation().isLValue = isLValue; + _access.annotation().isPure = isPure; + TypePointer exprType = type(_access.baseExpression()); if (exprType->category() == Type::Category::TypeType) { @@ -2872,14 +3098,11 @@ bool TypeChecker::visit(IndexRangeAccess const& _access) else if (!(arrayType = dynamic_cast(exprType))) m_errorReporter.fatalTypeError(4781_error, _access.location(), "Index range access is only possible for arrays and array slices."); - if (arrayType->location() != DataLocation::CallData || !arrayType->isDynamicallySized()) m_errorReporter.typeError(1227_error, _access.location(), "Index range access is only supported for dynamic calldata arrays."); else if (arrayType->baseType()->isDynamicallyEncoded()) m_errorReporter.typeError(2148_error, _access.location(), "Index range access is not supported for arrays with dynamically encoded base types."); _access.annotation().type = TypeProvider::arraySlice(*arrayType); - _access.annotation().isLValue = isLValue; - _access.annotation().isPure = isPure; return false; } @@ -2932,6 +3155,7 @@ vector TypeChecker::cleanOverloadedDeclarations( bool TypeChecker::visit(Identifier const& _identifier) { IdentifierAnnotation& annotation = _identifier.annotation(); + if (!annotation.referencedDeclaration) { annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations); @@ -2997,20 +3221,26 @@ bool TypeChecker::visit(Identifier const& _identifier) !!annotation.referencedDeclaration, "Referenced declaration is null after overload resolution." ); + bool isConstant = false; annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.type = annotation.referencedDeclaration->type(); solAssert(annotation.type, "Declaration referenced before type could be determined."); if (auto variableDeclaration = dynamic_cast(annotation.referencedDeclaration)) - annotation.isPure = annotation.isConstant = variableDeclaration->isConstant(); + annotation.isPure = isConstant = variableDeclaration->isConstant(); else if (dynamic_cast(annotation.referencedDeclaration)) - { - if (dynamic_cast(annotation.type)) - annotation.isPure = true; - } + annotation.isPure = dynamic_cast(annotation.type); else if (dynamic_cast(annotation.type)) annotation.isPure = true; else if (dynamic_cast(annotation.type)) annotation.isPure = true; + else + annotation.isPure = false; + + annotation.isConstant = isConstant; + + annotation.requiredLookup = + dynamic_cast(annotation.referencedDeclaration) ? + VirtualLookup::Virtual : VirtualLookup::Static; // Check for deprecated function names. // The check is done here for the case without an actual function call. @@ -3047,10 +3277,29 @@ bool TypeChecker::visit(Identifier const& _identifier) return false; } +void TypeChecker::endVisit(IdentifierPath const& _identifierPath) +{ + if ( + dynamic_cast(_identifierPath.annotation().referencedDeclaration) && + _identifierPath.path().size() == 1 + ) + _identifierPath.annotation().requiredLookup = VirtualLookup::Virtual; + else + _identifierPath.annotation().requiredLookup = VirtualLookup::Static; +} + +void TypeChecker::endVisit(UserDefinedTypeName const& _userDefinedTypeName) +{ + if (!_userDefinedTypeName.annotation().type) + _userDefinedTypeName.annotation().type = _userDefinedTypeName.pathNode().annotation().referencedDeclaration->type(); +} + void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) { _expr.annotation().type = TypeProvider::typeType(TypeProvider::fromElementaryTypeName(_expr.type().typeName(), _expr.type().stateMutability())); _expr.annotation().isPure = true; + _expr.annotation().isLValue = false; + _expr.annotation().isConstant = false; } void TypeChecker::endVisit(Literal const& _literal) @@ -3058,7 +3307,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (_literal.looksLikeAddress()) { // Assign type here if it even looks like an address. This prevents double errors for invalid addresses - _literal.annotation().type = TypeProvider::payableAddress(); + _literal.annotation().type = TypeProvider::address(); string msg; if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits @@ -3080,7 +3329,7 @@ void TypeChecker::endVisit(Literal const& _literal) _literal.location(), msg + " If this is not used as an address, please prepend '00'. " + - "For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals" + "For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals" ); } @@ -3106,6 +3355,18 @@ void TypeChecker::endVisit(Literal const& _literal) m_errorReporter.fatalTypeError(2826_error, _literal.location(), "Invalid literal value."); _literal.annotation().isPure = true; + _literal.annotation().isLValue = false; + _literal.annotation().isConstant = false; +} + +void TypeChecker::endVisit(UsingForDirective const& _usingFor) +{ + if (m_currentContract->isInterface()) + m_errorReporter.typeError( + 9088_error, + _usingFor.location(), + "The \"using for\" directive is not allowed inside interfaces." + ); } bool TypeChecker::contractDependenciesAreCyclic( @@ -3130,16 +3391,17 @@ Declaration const& TypeChecker::dereference(Identifier const& _identifier) const return *_identifier.annotation().referencedDeclaration; } -Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName) const +Declaration const& TypeChecker::dereference(IdentifierPath const& _path) const { - solAssert(!!_typeName.annotation().referencedDeclaration, "Declaration not stored."); - return *_typeName.annotation().referencedDeclaration; + solAssert(!!_path.annotation().referencedDeclaration, "Declaration not stored."); + return *_path.annotation().referencedDeclaration; } bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) { _expression.accept(*this); - if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) + BoolResult result = type(_expression)->isImplicitlyConvertibleTo(_expectedType); + if (!result) { auto errorMsg = "Type " + type(_expression)->toString() + @@ -3158,17 +3420,23 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte errorMsg + ", but it can be explicitly converted." ); else - m_errorReporter.typeError( + m_errorReporter.typeErrorConcatenateDescriptions( 2326_error, _expression.location(), errorMsg + ". Try converting to type " + type(_expression)->mobileType()->toString() + - " or use an explicit conversion." + " or use an explicit conversion.", + result.message() ); } else - m_errorReporter.typeError(7407_error, _expression.location(), errorMsg + "."); + m_errorReporter.typeErrorConcatenateDescriptions( + 7407_error, + _expression.location(), + errorMsg + ".", + result.message() + ); return false; } return true; @@ -3180,11 +3448,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment; _expression.accept(*this); - if (_expression.annotation().isLValue) + if (*_expression.annotation().isLValue) return; auto [errorId, description] = [&]() -> tuple { - if (_expression.annotation().isConstant) + if (*_expression.annotation().isConstant) return { 6520_error, "Cannot assign to a constant variable." }; if (auto indexAccess = dynamic_cast(&_expression)) @@ -3219,10 +3487,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss m_errorReporter.typeError(errorId, _expression.location(), description); } -bool TypeChecker::experimentalFeatureActive(ExperimentalFeature _feature) const +bool TypeChecker::useABICoderV2() const { solAssert(m_currentSourceUnit, ""); if (m_currentContract) solAssert(m_currentSourceUnit == &m_currentContract->sourceUnit(), ""); - return m_currentSourceUnit->annotation().experimentalFeatures.count(_feature); + return *m_currentSourceUnit->annotation().useABICoderV2; + } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index e34c9e9d06a0..d75bb8a57cbf 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -141,8 +141,11 @@ class TypeChecker: private ASTConstVisitor bool visit(IndexAccess const& _indexAccess) override; bool visit(IndexRangeAccess const& _indexRangeAccess) override; bool visit(Identifier const& _identifier) override; + void endVisit(IdentifierPath const& _identifierPath) override; + void endVisit(UserDefinedTypeName const& _userDefinedTypeName) override; void endVisit(ElementaryTypeNameExpression const& _expr) override; void endVisit(Literal const& _literal) override; + void endVisit(UsingForDirective const& _usingForDirective) override; bool contractDependenciesAreCyclic( ContractDefinition const& _contract, @@ -152,7 +155,7 @@ class TypeChecker: private ASTConstVisitor /// @returns the referenced declaration and throws on error. Declaration const& dereference(Identifier const& _identifier) const; /// @returns the referenced declaration and throws on error. - Declaration const& dereference(UserDefinedTypeName const& _typeName) const; + Declaration const& dereference(IdentifierPath const& _path) const; std::vector cleanOverloadedDeclarations( Identifier const& _reference, @@ -165,7 +168,17 @@ class TypeChecker: private ASTConstVisitor /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. void requireLValue(Expression const& _expression, bool _ordinaryAssignment); - bool experimentalFeatureActive(ExperimentalFeature _feature) const; + bool useABICoderV2() const; + + /// @returns the current scope that can have function or type definitions. + /// This is either a contract or a source unit. + ASTNode const* currentDefinitionScope() const + { + if (m_currentContract) + return m_currentContract; + else + return m_currentSourceUnit; + } SourceUnit const* m_currentSourceUnit = nullptr; ContractDefinition const* m_currentContract = nullptr; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 9d5b2ef6298b..ed1bb0be9704 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -128,28 +128,12 @@ class AssemblyViewPureChecker bool ViewPureChecker::check() { - vector contracts; - - for (auto const& node: m_ast) - { - SourceUnit const* source = dynamic_cast(node.get()); - solAssert(source, ""); - contracts += source->filteredNodes(source->nodes()); - } - - // Check modifiers first to infer their state mutability. - for (auto const& contract: contracts) - for (ModifierDefinition const* mod: contract->functionModifiers()) - mod->accept(*this); - - for (auto const& contract: contracts) - contract->accept(*this); + for (auto const& source: m_ast) + source->accept(*this); return !m_errors; } - - bool ViewPureChecker::visit(FunctionDefinition const& _funDef) { solAssert(!m_currentFunction, ""); @@ -202,7 +186,13 @@ void ViewPureChecker::endVisit(Identifier const& _identifier) bool writes = _identifier.annotation().willBeWrittenTo; if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) { - if (varDecl->isStateVariable() && !varDecl->isConstant()) + if (varDecl->immutable()) + { + // Immutables that are assigned literals are pure. + if (!(varDecl->value() && varDecl->value()->annotation().type->category() == Type::Category::RationalNumber)) + mutability = StateMutability::View; + } + else if (varDecl->isStateVariable() && !varDecl->isConstant()) mutability = writes ? StateMutability::NonPayable : StateMutability::View; } else if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) @@ -210,8 +200,8 @@ void ViewPureChecker::endVisit(Identifier const& _identifier) switch (magicVar->type()->category()) { case Type::Category::Contract: - solAssert(_identifier.name() == "this" || _identifier.name() == "super", ""); - if (!dynamic_cast(*magicVar->type()).isSuper()) + solAssert(_identifier.name() == "this", ""); + if (dynamic_cast(magicVar->type())) // reads the address mutability = StateMutability::View; break; @@ -277,21 +267,24 @@ void ViewPureChecker::reportMutability( { // We do not warn for library functions because they cannot be payable anyway. // Also internal functions should be allowed to use `msg.value`. - if (m_currentFunction->isPublic() && !m_currentFunction->libraryFunction()) + if ((m_currentFunction->isConstructor() || m_currentFunction->isPublic()) && !m_currentFunction->libraryFunction()) { if (_nestedLocation) m_errorReporter.typeError( 4006_error, _location, SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation), - "This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal." + m_currentFunction->isConstructor() ? + "This modifier uses \"msg.value\" or \"callvalue()\" and thus the constructor has to be payable." + : "This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal." ); else m_errorReporter.typeError( 5887_error, _location, - "\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function " - "\"payable\" or use an internal function to avoid this error." + m_currentFunction->isConstructor() ? + "\"msg.value\" and \"callvalue()\" can only be used in payable constructors. Make the constructor \"payable\" to avoid this error." + : "\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function \"payable\" or use an internal function to avoid this error." ); m_errors = true; } @@ -307,9 +300,28 @@ void ViewPureChecker::reportMutability( ); } +ViewPureChecker::MutabilityAndLocation const& ViewPureChecker::modifierMutability( + ModifierDefinition const& _modifier +) +{ + if (!m_inferredMutability.count(&_modifier)) + { + MutabilityAndLocation bestMutabilityAndLocation{}; + FunctionDefinition const* currentFunction = nullptr; + swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation); + swap(currentFunction, m_currentFunction); + + _modifier.accept(*this); + + swap(bestMutabilityAndLocation, m_bestMutabilityAndLocation); + swap(currentFunction, m_currentFunction); + } + return m_inferredMutability.at(&_modifier); +} + void ViewPureChecker::endVisit(FunctionCall const& _functionCall) { - if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_functionCall.annotation().kind != FunctionCallKind::FunctionCall) return; StateMutability mutability = dynamic_cast(*_functionCall.expression().annotation().type).stateMutability(); @@ -344,7 +356,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) switch (_memberAccess.expression().annotation().type->category()) { case Type::Category::Address: - if (member == "balance") + if (member == "balance" || member == "code" || member == "codehash") mutability = StateMutability::View; else if (member == "isContract") mutability = StateMutability::View; @@ -358,7 +370,6 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::ABI, "encodePacked"}, {MagicType::Kind::ABI, "encodeWithSelector"}, {MagicType::Kind::ABI, "encodeWithSignature"}, - {MagicType::Kind::Block, "blockhash"}, {MagicType::Kind::Message, "data"}, {MagicType::Kind::Message, "sig"}, {MagicType::Kind::MetaType, "creationCode"}, @@ -430,14 +441,11 @@ void ViewPureChecker::endVisit(IndexRangeAccess const& _indexRangeAccess) void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) { - solAssert(_modifier.name(), ""); - if (ModifierDefinition const* mod = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) + if (ModifierDefinition const* mod = dynamic_cast(_modifier.name().annotation().referencedDeclaration)) { - solAssert(m_inferredMutability.count(mod), ""); - auto const& mutAndLocation = m_inferredMutability.at(mod); + MutabilityAndLocation const& mutAndLocation = modifierMutability(*mod); reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location); } else - solAssert(dynamic_cast(_modifier.name()->annotation().referencedDeclaration), ""); + solAssert(dynamic_cast(_modifier.name().annotation().referencedDeclaration), ""); } - diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 87135b4f762c..dfbb2f7478ba 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -71,6 +71,9 @@ class ViewPureChecker: private ASTConstVisitor std::optional const& _nestedLocation = {} ); + /// Determines the mutability of modifier if not already cached. + MutabilityAndLocation const& modifierMutability(ModifierDefinition const& _modifier); + std::vector> const& m_ast; langutil::ErrorReporter& m_errorReporter; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 662ea81d3184..8081b2c8beb6 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -84,16 +84,6 @@ TypePointer ImportDirective::type() const return TypeProvider::module(*annotation().sourceUnit); } -vector ContractDefinition::stateVariablesIncludingInherited() const -{ - vector stateVars; - for (auto const& contract: annotation().linearizedBaseContracts) - for (auto var: contract->stateVariables()) - if (*contract == *this || var->isVisibleInDerivedContracts()) - stateVars.push_back(var); - return stateVars; -} - bool ContractDefinition::derivesFrom(ContractDefinition const& _base) const { return util::contains(annotation().linearizedBaseContracts, &_base); @@ -208,6 +198,14 @@ vector, FunctionTypePointer>> const& ContractDefinition: }); } +uint32_t ContractDefinition::interfaceId() const +{ + uint32_t result{0}; + for (auto const& function: interfaceFunctionList(false)) + result ^= util::fromBigEndian(function.first.ref()); + return result; +} + TypePointer ContractDefinition::type() const { return TypeProvider::typeType(TypeProvider::contract(*this)); @@ -292,7 +290,7 @@ bool FunctionDefinition::libraryFunction() const Visibility FunctionDefinition::defaultVisibility() const { solAssert(!isConstructor(), ""); - return Declaration::defaultVisibility(); + return isFree() ? Visibility::Internal : Declaration::defaultVisibility(); } FunctionTypePointer FunctionDefinition::functionType(bool _internal) const @@ -338,7 +336,7 @@ TypePointer FunctionDefinition::type() const TypePointer FunctionDefinition::typeViaContractName() const { - if (annotation().contract->isLibrary()) + if (libraryFunction()) { if (isPublic()) return FunctionType(*this).asExternallyCallableFunction(true); @@ -374,7 +372,9 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual( if (_searchStart == nullptr && !virtualSemantics()) return *this; - solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + solAssert(!isFree(), ""); + solAssert(isOrdinary(), ""); + solAssert(!libraryFunction(), ""); FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false); @@ -450,11 +450,6 @@ EventDefinitionAnnotation& EventDefinition::annotation() const return initAnnotation(); } -UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const -{ - return initAnnotation(); -} - SourceUnit const& Scopable::sourceUnit() const { ASTNode const* s = scope(); @@ -482,7 +477,25 @@ CallableDeclaration const* Scopable::functionOrModifierDefinition() const string Scopable::sourceUnitName() const { - return sourceUnit().annotation().path; + return *sourceUnit().annotation().path; +} + +bool Declaration::isEnumValue() const +{ + solAssert(scope(), ""); + return dynamic_cast(scope()); +} + +bool Declaration::isStructMember() const +{ + solAssert(scope(), ""); + return dynamic_cast(scope()); +} + +bool Declaration::isEventParameter() const +{ + solAssert(scope(), ""); + return dynamic_cast(scope()); } DeclarationAnnotation& Declaration::annotation() const @@ -603,14 +616,8 @@ bool VariableDeclaration::isLibraryFunctionParameter() const if (!isCallableOrCatchParameter()) return false; if (auto const* funDef = dynamic_cast(scope())) - return dynamic_cast(*funDef->scope()).isLibrary(); - else - return false; -} - -bool VariableDeclaration::isEventParameter() const -{ - return dynamic_cast(scope()) != nullptr; + return funDef->libraryFunction(); + return false; } bool VariableDeclaration::hasReferenceOrMappingType() const @@ -620,6 +627,16 @@ bool VariableDeclaration::hasReferenceOrMappingType() const return type->category() == Type::Category::Mapping || dynamic_cast(type); } +bool VariableDeclaration::isStateVariable() const +{ + return dynamic_cast(scope()); +} + +bool VariableDeclaration::isFileLevelVariable() const +{ + return dynamic_cast(scope()); +} + set VariableDeclaration::allowedDataLocations() const { using Location = VariableDeclaration::Location; @@ -735,6 +752,44 @@ FunctionCallAnnotation& FunctionCall::annotation() const return initAnnotation(); } +vector> FunctionCall::sortedArguments() const +{ + // normal arguments + if (m_names.empty()) + return arguments(); + + // named arguments + FunctionTypePointer functionType; + if (*annotation().kind == FunctionCallKind::StructConstructorCall) + { + auto const& type = dynamic_cast(*m_expression->annotation().type); + auto const& structType = dynamic_cast(*type.actualType()); + functionType = structType.constructorType(); + } + else + functionType = dynamic_cast(m_expression->annotation().type); + + vector> sorted; + for (auto const& parameterName: functionType->parameterNames()) + { + bool found = false; + for (size_t j = 0; j < m_names.size() && !found; j++) + if ((found = (parameterName == *m_names.at(j)))) + // we found the actual parameter position + sorted.push_back(m_arguments.at(j)); + solAssert(found, ""); + } + + if (!functionType->takesArbitraryParameters()) + { + solAssert(m_arguments.size() == functionType->parameterTypes().size(), ""); + solAssert(m_arguments.size() == m_names.size(), ""); + solAssert(m_arguments.size() == sorted.size(), ""); + } + + return sorted; +} + IdentifierAnnotation& Identifier::annotation() const { return initAnnotation(); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 0dd24fb4186f..819412a63e6d 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -45,7 +45,7 @@ namespace solidity::yul { -// Forward-declaration to +// Forward-declaration to struct Block; struct Dialect; } @@ -152,10 +152,19 @@ std::vector ASTNode::filteredNodes(std::vector> co return ret; } +/** + * Abstract marker class that specifies that this AST node opens a scope. + */ +class ScopeOpener +{ +public: + virtual ~ScopeOpener() = default; +}; + /** * Source unit containing import directives and contract definitions. */ -class SourceUnit: public ASTNode +class SourceUnit: public ASTNode, public ScopeOpener { public: SourceUnit( @@ -249,10 +258,16 @@ class Declaration: public ASTNode, public Scopable bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; } virtual bool isVisibleViaContractTypeAccess() const { return false; } - virtual bool isLValue() const { return false; } virtual bool isPartOfExternalInterface() const { return false; } + /// @returns true if this is a declaration of an enum member. + bool isEnumValue() const; + /// @returns true if this is a declaration of a struct member. + bool isStructMember() const; + /// @returns true if this is a declaration of a parameter of an event. + bool isEventParameter() const; + /// @returns the type of expressions referencing this declaration. /// This can only be called once types of variable declarations have already been resolved. virtual TypePointer type() const = 0; @@ -455,7 +470,7 @@ class ImplementationOptional * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration, public StructurallyDocumented +class ContractDefinition: public Declaration, public StructurallyDocumented, public ScopeOpener { public: ContractDefinition( @@ -485,7 +500,6 @@ class ContractDefinition: public Declaration, public StructurallyDocumented std::vector definedStructs() const { return filteredNodes(m_subNodes); } std::vector definedEnums() const { return filteredNodes(m_subNodes); } std::vector stateVariables() const { return filteredNodes(m_subNodes); } - std::vector stateVariablesIncludingInherited() const; std::vector functionModifiers() const { return filteredNodes(m_subNodes); } std::vector definedFunctions() const { return filteredNodes(m_subNodes); } std::vector events() const { return filteredNodes(m_subNodes); } @@ -500,6 +514,8 @@ class ContractDefinition: public Declaration, public StructurallyDocumented /// as intended for use by the ABI. std::map, FunctionTypePointer> interfaceFunctions(bool _includeInheritedFunctions = true) const; std::vector, FunctionTypePointer>> const& interfaceFunctionList(bool _includeInheritedFunctions = true) const; + /// @returns the EIP-165 compatible interface identifier. This will exclude inherited functions. + uint32_t interfaceId() const; /// @returns a list of all declarations in this contract std::vector declarations() const { return filteredNodes(m_subNodes); } @@ -540,28 +556,52 @@ class ContractDefinition: public Declaration, public StructurallyDocumented util::LazyInit> m_interfaceEvents; }; +/** + * A sequence of identifiers separated by dots used outside the expression context. Inside the expression context, this is a sequence of Identifier and MemberAccess. + */ +class IdentifierPath: public ASTNode +{ +public: + IdentifierPath(int64_t _id, SourceLocation const& _location, std::vector _path): + ASTNode(_id, _location), m_path(std::move(_path)) {} + + std::vector const& path() const { return m_path; } + IdentifierPathAnnotation& annotation() const override + { + return initAnnotation(); + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; +private: + std::vector m_path; +}; + class InheritanceSpecifier: public ASTNode { public: InheritanceSpecifier( int64_t _id, SourceLocation const& _location, - ASTPointer _baseName, + ASTPointer _baseName, std::unique_ptr>> _arguments ): - ASTNode(_id, _location), m_baseName(std::move(_baseName)), m_arguments(std::move(_arguments)) {} + ASTNode(_id, _location), m_baseName(std::move(_baseName)), m_arguments(std::move(_arguments)) + { + solAssert(m_baseName != nullptr, "Name cannot be null."); + } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - UserDefinedTypeName const& name() const { return *m_baseName; } + IdentifierPath const& name() const { return *m_baseName; } // Returns nullptr if no argument list was given (``C``). // If an argument list is given (``C(...)``), the arguments are returned // as a vector of expressions. Note that this vector can be empty (``C()``). std::vector> const* arguments() const { return m_arguments.get(); } private: - ASTPointer m_baseName; + ASTPointer m_baseName; std::unique_ptr>> m_arguments; }; @@ -576,24 +616,27 @@ class UsingForDirective: public ASTNode UsingForDirective( int64_t _id, SourceLocation const& _location, - ASTPointer _libraryName, + ASTPointer _libraryName, ASTPointer _typeName ): - ASTNode(_id, _location), m_libraryName(std::move(_libraryName)), m_typeName(std::move(_typeName)) {} + ASTNode(_id, _location), m_libraryName(std::move(_libraryName)), m_typeName(std::move(_typeName)) + { + solAssert(m_libraryName != nullptr, "Name cannot be null."); + } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - UserDefinedTypeName const& libraryName() const { return *m_libraryName; } + IdentifierPath const& libraryName() const { return *m_libraryName; } /// @returns the type name the library is attached to, null for `*`. TypeName const* typeName() const { return m_typeName.get(); } private: - ASTPointer m_libraryName; + ASTPointer m_libraryName; ASTPointer m_typeName; }; -class StructDefinition: public Declaration +class StructDefinition: public Declaration, public ScopeOpener { public: StructDefinition( @@ -620,7 +663,7 @@ class StructDefinition: public Declaration std::vector> m_members; }; -class EnumDefinition: public Declaration +class EnumDefinition: public Declaration, public ScopeOpener { public: EnumDefinition( @@ -748,7 +791,7 @@ class OverrideSpecifier: public ASTNode OverrideSpecifier( int64_t _id, SourceLocation const& _location, - std::vector> _overrides + std::vector> _overrides ): ASTNode(_id, _location), m_overrides(std::move(_overrides)) @@ -759,13 +802,13 @@ class OverrideSpecifier: public ASTNode void accept(ASTConstVisitor& _visitor) const override; /// @returns the list of specific overrides, if any - std::vector> const& overrides() const { return m_overrides; } + std::vector> const& overrides() const { return m_overrides; } protected: - std::vector> m_overrides; + std::vector> m_overrides; }; -class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional +class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional, public ScopeOpener { public: FunctionDefinition( @@ -774,6 +817,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ASTPointer const& _name, Visibility _visibility, StateMutability _stateMutability, + bool _free, Token _kind, bool _isVirtual, ASTPointer const& _overrides, @@ -787,11 +831,13 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), + m_free(_free), m_kind(_kind), m_functionModifiers(std::move(_modifiers)), m_body(_body) { solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, ""); + solAssert(isOrdinary() == !name().empty(), ""); } void accept(ASTVisitor& _visitor) override; @@ -803,6 +849,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen bool isConstructor() const { return m_kind == Token::Constructor; } bool isFallback() const { return m_kind == Token::Fallback; } bool isReceive() const { return m_kind == Token::Receive; } + bool isFree() const { return m_free; } Token kind() const { return m_kind; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector> const& modifiers() const { return m_functionModifiers; } @@ -814,6 +861,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen } bool isVisibleViaContractTypeAccess() const override { + solAssert(!isFree(), ""); return isOrdinary() && visibility() >= Visibility::Public; } bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); } @@ -849,6 +897,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen private: StateMutability m_stateMutability; + bool m_free; Token const m_kind; std::vector> m_functionModifiers; ASTPointer m_body; @@ -882,7 +931,6 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented ASTPointer _value, Visibility _visibility, ASTPointer const _documentation = nullptr, - bool _isStateVar = false, bool _isIndexed = false, Mutability _mutability = Mutability::Mutable, ASTPointer _overrides = nullptr, @@ -892,7 +940,6 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented StructurallyDocumented(std::move(_documentation)), m_typeName(std::move(_type)), m_value(std::move(_value)), - m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed), m_mutability(_mutability), m_overrides(std::move(_overrides)), @@ -935,15 +982,12 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented bool isConstructorParameter() const; /// @returns true iff this variable is a parameter(or return parameter of a library function bool isLibraryFunctionParameter() const; - /// @returns true if the type of the variable does not need to be specified, i.e. it is declared - /// in the body of a function or modifier. - /// @returns true if this variable is a parameter of an event. - bool isEventParameter() const; /// @returns true if the type of the variable is a reference or mapping type, i.e. /// array, struct or mapping. These types can take a data location (and often require it). /// Can only be called after reference resolution. bool hasReferenceOrMappingType() const; - bool isStateVariable() const { return m_isStateVariable; } + bool isStateVariable() const; + bool isFileLevelVariable() const; bool isIndexed() const { return m_isIndexed; } Mutability mutability() const { return m_mutability; } bool isConstant() const { return m_mutability == Mutability::Constant; } @@ -972,7 +1016,6 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented /// Initially assigned value, can be missing. For local variables, this is stored inside /// VariableDeclarationStatement and not here. ASTPointer m_value; - bool m_isStateVariable = false; ///< Whether or not this is a contract state variable bool m_isIndexed = false; ///< Whether this is an indexed variable (used by events). /// Whether the variable is "constant", "immutable" or non-marked (mutable). Mutability m_mutability = Mutability::Mutable; @@ -983,7 +1026,7 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented /** * Definition of a function modifier. */ -class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional +class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional, public ScopeOpener { public: ModifierDefinition( @@ -1033,29 +1076,32 @@ class ModifierInvocation: public ASTNode ModifierInvocation( int64_t _id, SourceLocation const& _location, - ASTPointer _name, + ASTPointer _name, std::unique_ptr>> _arguments ): - ASTNode(_id, _location), m_modifierName(std::move(_name)), m_arguments(std::move(_arguments)) {} + ASTNode(_id, _location), m_modifierName(std::move(_name)), m_arguments(std::move(_arguments)) + { + solAssert(m_modifierName != nullptr, "Name cannot be null."); + } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - ASTPointer const& name() const { return m_modifierName; } + IdentifierPath& name() const { return *m_modifierName; } // Returns nullptr if no argument list was given (``mod``). // If an argument list is given (``mod(...)``), the arguments are returned // as a vector of expressions. Note that this vector can be empty (``mod()``). std::vector> const* arguments() const { return m_arguments.get(); } private: - ASTPointer m_modifierName; + ASTPointer m_modifierName; std::unique_ptr>> m_arguments; }; /** * Definition of a (loggable) event. */ -class EventDefinition: public CallableDeclaration, public StructurallyDocumented +class EventDefinition: public CallableDeclaration, public StructurallyDocumented, public ScopeOpener { public: EventDefinition( @@ -1177,23 +1223,25 @@ class ElementaryTypeName: public TypeName class UserDefinedTypeName: public TypeName { public: - UserDefinedTypeName(int64_t _id, SourceLocation const& _location, std::vector _namePath): - TypeName(_id, _location), m_namePath(std::move(_namePath)) {} + UserDefinedTypeName(int64_t _id, SourceLocation const& _location, ASTPointer _namePath): + TypeName(_id, _location), m_namePath(std::move(_namePath)) + { + solAssert(m_namePath != nullptr, "Name cannot be null."); + } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; - std::vector const& namePath() const { return m_namePath; } - - UserDefinedTypeNameAnnotation& annotation() const override; + std::vector const& namePath() const { return m_namePath->path(); } + IdentifierPath& pathNode() const { return *m_namePath; } private: - std::vector m_namePath; + ASTPointer m_namePath; }; /** * A literal function type. Its source form is "function (paramType1, paramType2) internal / external returns (retType1, retType2)" */ -class FunctionTypeName: public TypeName +class FunctionTypeName: public TypeName, public ScopeOpener { public: FunctionTypeName( @@ -1328,25 +1376,31 @@ class InlineAssembly: public Statement /** * Brace-enclosed block containing zero or more statements. */ -class Block: public Statement, public Scopable +class Block: public Statement, public Scopable, public ScopeOpener { public: Block( int64_t _id, SourceLocation const& _location, ASTPointer const& _docString, + bool _unchecked, std::vector> _statements ): - Statement(_id, _location, _docString), m_statements(std::move(_statements)) {} + Statement(_id, _location, _docString), + m_statements(std::move(_statements)), + m_unchecked(_unchecked) + {} void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; std::vector> const& statements() const { return m_statements; } + bool unchecked() const { return m_unchecked; } BlockAnnotation& annotation() const override; private: std::vector> m_statements; + bool m_unchecked; }; /** @@ -1405,7 +1459,7 @@ class IfStatement: public Statement * unsuccessful cases. * Names are only allowed for the unsuccessful cases. */ -class TryCatchClause: public ASTNode, public Scopable +class TryCatchClause: public ASTNode, public Scopable, public ScopeOpener { public: TryCatchClause( @@ -1520,7 +1574,7 @@ class WhileStatement: public BreakableStatement /** * For loop statement */ -class ForStatement: public BreakableStatement, public Scopable +class ForStatement: public BreakableStatement, public Scopable, public ScopeOpener { public: ForStatement( @@ -1882,7 +1936,13 @@ class FunctionCall: public Expression void accept(ASTConstVisitor& _visitor) const override; Expression const& expression() const { return *m_expression; } + /// @returns the given arguments in the order they were written. std::vector> arguments() const { return {m_arguments.begin(), m_arguments.end()}; } + /// @returns the given arguments sorted by how the called function takes them. + std::vector> sortedArguments() const; + /// @returns the list of given argument names if this is a named call, + /// in the order they were written. + /// If this is not a named call, this is empty. std::vector> const& names() const { return m_names; } FunctionCallAnnotation& annotation() const override; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 04f661020ccb..e6ef18c3a377 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -27,6 +27,8 @@ #include #include +#include + #include #include #include @@ -45,6 +47,7 @@ namespace solidity::frontend class Type; using TypePointer = Type const*; +using namespace util; struct ASTAnnotation { @@ -86,11 +89,13 @@ struct StructurallyDocumentedAnnotation struct SourceUnitAnnotation: ASTAnnotation { /// The "absolute" (in the compiler sense) path of this source unit. - std::string path; + SetOnce path; /// The exported symbols (all global symbols). - std::map> exportedSymbols; + SetOnce>> exportedSymbols; /// Experimental features. std::set experimentalFeatures; + /// Using the new ABI coder. Set to `false` if using ABI coder v1. + SetOnce useABICoderV2; }; struct ScopableAnnotation @@ -106,10 +111,10 @@ struct ScopableAnnotation virtual ~ScopableAnnotation() = default; /// The scope this declaration resides in. Can be nullptr if it is the global scope. - /// Available only after name and type resolution step. + /// Filled by the Scoper. ASTNode const* scope = nullptr; /// Pointer to the contract this declaration resides in. Can be nullptr if the current scope - /// is not part of a contract. Available only after name and type resolution step. + /// is not part of a contract. Filled by the Scoper. ContractDefinition const* contract = nullptr; }; @@ -120,7 +125,7 @@ struct DeclarationAnnotation: ASTAnnotation, ScopableAnnotation struct ImportAnnotation: DeclarationAnnotation { /// The absolute path of the source unit to import. - std::string absolutePath; + SetOnce absolutePath; /// The actual source unit. SourceUnit const* sourceUnit = nullptr; }; @@ -128,7 +133,7 @@ struct ImportAnnotation: DeclarationAnnotation struct TypeDeclarationAnnotation: DeclarationAnnotation { /// The name of this type, prefixed by proper namespaces if globally accessible. - std::string canonicalName; + SetOnce canonicalName; }; struct StructDeclarationAnnotation: TypeDeclarationAnnotation @@ -147,7 +152,7 @@ struct StructDeclarationAnnotation: TypeDeclarationAnnotation struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation { /// List of functions and modifiers without a body. Can also contain functions from base classes. - std::vector unimplementedDeclarations; + std::optional> unimplementedDeclarations; /// List of all (direct and indirect) base contracts in order from derived to /// base, including the contract itself. std::vector linearizedBaseContracts; @@ -194,8 +199,8 @@ struct InlineAssemblyAnnotation: StatementAnnotation struct ExternalIdentifierInfo { Declaration const* declaration = nullptr; - bool isSlot = false; ///< Whether the storage slot of a variable is queried. - bool isOffset = false; ///< Whether the intra-slot offset of a storage variable is queried. + /// Suffix used, one of "slot", "offset", "length" or empty. + std::string suffix; size_t valueSize = size_t(-1); }; @@ -230,13 +235,12 @@ struct TypeNameAnnotation: ASTAnnotation TypePointer type = nullptr; }; -struct UserDefinedTypeNameAnnotation: TypeNameAnnotation +struct IdentifierPathAnnotation: ASTAnnotation { /// Referenced declaration, set during reference resolution stage. Declaration const* referencedDeclaration = nullptr; - /// Stores a reference to the current contract. - /// This is needed because types of base contracts change depending on the context. - ContractDefinition const* contractScope = nullptr; + /// What kind of lookup needs to be done (static, virtual, super) find the declaration. + SetOnce requiredLookup; }; struct ExpressionAnnotation: ASTAnnotation @@ -244,26 +248,38 @@ struct ExpressionAnnotation: ASTAnnotation /// Inferred type of the expression. TypePointer type = nullptr; /// Whether the expression is a constant variable - bool isConstant = false; + SetOnce isConstant; /// Whether the expression is pure, i.e. compile-time constant. - bool isPure = false; + SetOnce isPure; /// Whether it is an LValue (i.e. something that can be assigned to). - bool isLValue = false; + SetOnce isLValue; /// Whether the expression is used in a context where the LValue is actually required. bool willBeWrittenTo = false; /// Whether the expression is an lvalue that is only assigned. /// Would be false for --, ++, delete, +=, -=, .... + /// Only relevant if isLvalue == true bool lValueOfOrdinaryAssignment = false; /// Types and - if given - names of arguments if the expr. is a function /// that is called, used for overload resolution std::optional arguments; + + /// True if the expression consists solely of the name of the function and the function is called immediately + /// instead of being stored or processed. The name may be qualified with the name of a contract, library + /// module, etc., that clarifies the scope. For example: `m.L.f()`, where `m` is a module, `L` is a library + /// and `f` is a function is a direct call. This means that the function to be called is known at compilation + /// time and it's not necessary to rely on any runtime dispatch mechanism to resolve it. + /// Note that even the simplest expressions, like `(f)()`, result in an indirect call even if they consist of + /// values known at compilation time. + bool calledDirectly = false; }; struct IdentifierAnnotation: ExpressionAnnotation { /// Referenced declaration, set at latest during overload resolution stage. Declaration const* referencedDeclaration = nullptr; + /// What kind of lookup needs to be done (static, virtual, super) find the declaration. + SetOnce requiredLookup; /// List of possible declarations it could refer to (can contain duplicates). std::vector candidateDeclarations; /// List of possible declarations it could refer to. @@ -274,6 +290,8 @@ struct MemberAccessAnnotation: ExpressionAnnotation { /// Referenced declaration, set at latest during overload resolution stage. Declaration const* referencedDeclaration = nullptr; + /// What kind of lookup needs to be done (static, virtual, super) find the declaration. + SetOnce requiredLookup; }; struct BinaryOperationAnnotation: ExpressionAnnotation @@ -285,7 +303,6 @@ struct BinaryOperationAnnotation: ExpressionAnnotation enum class FunctionCallKind { - Unset, FunctionCall, TypeConversion, StructConstructorCall @@ -293,7 +310,7 @@ enum class FunctionCallKind struct FunctionCallAnnotation: ExpressionAnnotation { - FunctionCallKind kind = FunctionCallKind::Unset; + util::SetOnce kind; /// If true, this is the external call of a try statement. bool tryCall = false; }; diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h index e167e7d49069..87f9817c3c5b 100644 --- a/libsolidity/ast/ASTEnums.h +++ b/libsolidity/ast/ASTEnums.h @@ -30,12 +30,17 @@ namespace solidity::frontend { +/// Possible lookups for function resolving +enum class VirtualLookup { Static, Virtual, Super }; + // How a function can mutate the EVM state. enum class StateMutability { Pure, View, NonPayable, Payable }; /// Visibility ordered from restricted to unrestricted. enum class Visibility { Default, Private, Internal, Public, External }; +enum class Arithmetic { Checked, Wrapping }; + inline std::string stateMutabilityToString(StateMutability const& _stateMutability) { switch (_stateMutability) diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 3f082d975bf0..80d37d3994d5 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -38,6 +38,7 @@ namespace solidity::frontend { class ASTNode; +class ScopeOpener; class SourceUnit; class PragmaDirective; class ImportDirective; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 5403b05c575e..68bc5367fc86 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -23,10 +23,11 @@ #include #include +#include #include -#include #include +#include #include #include @@ -39,15 +40,38 @@ #include #include #include +#include using namespace std; using namespace solidity::langutil; +namespace +{ + +template typename C> +void addIfSet(std::vector>& _attributes, string const& _name, C const& _value) +{ + if constexpr (std::is_same_v, solidity::util::SetOnce>) + { + if (!_value.set()) + return; + } + else if constexpr (std::is_same_v, optional>) + { + if (!_value.has_value()) + return; + } + + _attributes.emplace_back(_name, *_value); +} + +} + namespace solidity::frontend { -ASTJsonConverter::ASTJsonConverter(bool _legacy, map _sourceIndices): - m_legacy(_legacy), +ASTJsonConverter::ASTJsonConverter(CompilerStack::State _stackState, map _sourceIndices): + m_stackState(_stackState), m_sourceIndices(std::move(_sourceIndices)) { } @@ -75,60 +99,9 @@ void ASTJsonConverter::setJsonNode( m_currentValue = Json::objectValue; m_currentValue["id"] = nodeId(_node); m_currentValue["src"] = sourceLocationToString(_node.location()); - if (!m_legacy) - { - m_currentValue["nodeType"] = _nodeType; - for (auto& e: _attributes) - m_currentValue[e.first] = std::move(e.second); - } - else - { - m_currentValue["name"] = _nodeType; - Json::Value attrs(Json::objectValue); - if ( - //these nodeTypes need to have a children-node even if it is empty - (_nodeType == "VariableDeclaration") || - (_nodeType == "ParameterList") || - (_nodeType == "Block") || - (_nodeType == "InlineAssembly") || - (_nodeType == "Throw") - ) - m_currentValue["children"] = Json::arrayValue; - - for (auto& e: _attributes) - { - if ((!e.second.isNull()) && ( - (e.second.isObject() && e.second.isMember("name")) || - (e.second.isArray() && e.second[0].isObject() && e.second[0].isMember("name")) || - (e.first == "declarations") // (in the case (_,x)= ... there's a nullpointer at [0] - )) - { - if (e.second.isObject()) - { - if (!m_currentValue["children"].isArray()) - m_currentValue["children"] = Json::arrayValue; - appendMove(m_currentValue["children"], std::move(e.second)); - } - if (e.second.isArray()) - for (auto& child: e.second) - if (!child.isNull()) - { - if (!m_currentValue["children"].isArray()) - m_currentValue["children"] = Json::arrayValue; - appendMove(m_currentValue["children"], std::move(child)); - } - } - else - { - if (e.first == "typeDescriptions") - attrs["type"] = Json::Value(e.second["typeString"]); - else - attrs[e.first] = std::move(e.second); - } - } - if (!attrs.empty()) - m_currentValue["attributes"] = std::move(attrs); - } + m_currentValue["nodeType"] = _nodeType; + for (auto& e: _attributes) + m_currentValue[e.first] = std::move(e.second); } size_t ASTJsonConverter::sourceIndexFromLocation(SourceLocation const& _location) const @@ -181,12 +154,16 @@ void ASTJsonConverter::appendExpressionAttributes( { std::vector> exprAttributes = { make_pair("typeDescriptions", typePointerToJson(_annotation.type)), - make_pair("isConstant", _annotation.isConstant), - make_pair("isPure", _annotation.isPure), - make_pair("isLValue", _annotation.isLValue), - make_pair("lValueRequested", _annotation.willBeWrittenTo), make_pair("argumentTypes", typePointerToJson(_annotation.arguments)) }; + + addIfSet(exprAttributes, "isLValue", _annotation.isLValue); + addIfSet(exprAttributes, "isPure", _annotation.isPure); + addIfSet(exprAttributes, "isConstant", _annotation.isConstant); + + if (m_stackState > CompilerStack::State::ParsedAndImported) + exprAttributes.emplace_back("lValueRequested", _annotation.willBeWrittenTo); + _attributes += exprAttributes; } @@ -195,42 +172,49 @@ Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pairlocation); tuple["declaration"] = idOrNull(_info.second.declaration); - tuple["isSlot"] = Json::Value(_info.second.isSlot); - tuple["isOffset"] = Json::Value(_info.second.isOffset); + tuple["isSlot"] = Json::Value(_info.second.suffix == "slot"); + tuple["isOffset"] = Json::Value(_info.second.suffix == "offset"); + if (!_info.second.suffix.empty()) + tuple["suffix"] = Json::Value(_info.second.suffix); tuple["valueSize"] = Json::Value(Json::LargestUInt(_info.second.valueSize)); return tuple; } void ASTJsonConverter::print(ostream& _stream, ASTNode const& _node) { - _stream << util::jsonPrettyPrint(util::removeNullMembers(toJson(_node))); + _stream << util::jsonPrettyPrint(toJson(_node)); } -Json::Value&& ASTJsonConverter::toJson(ASTNode const& _node) +Json::Value ASTJsonConverter::toJson(ASTNode const& _node) { _node.accept(*this); - return std::move(m_currentValue); + return util::removeNullMembers(std::move(m_currentValue)); } bool ASTJsonConverter::visit(SourceUnit const& _node) { - Json::Value exportedSymbols = Json::objectValue; - for (auto const& sym: _node.annotation().exportedSymbols) + std::vector> attributes = { + make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue), + make_pair("nodes", toJson(_node.nodes())) + }; + + if (_node.annotation().exportedSymbols.set()) { - exportedSymbols[sym.first] = Json::arrayValue; - for (Declaration const* overload: sym.second) - exportedSymbols[sym.first].append(nodeId(*overload)); - } - setJsonNode( - _node, - "SourceUnit", + Json::Value exportedSymbols = Json::objectValue; + for (auto const& sym: *_node.annotation().exportedSymbols) { - make_pair("absolutePath", _node.annotation().path), - make_pair("exportedSymbols", move(exportedSymbols)), - make_pair("license", _node.licenseString() ? Json::Value(*_node.licenseString()) : Json::nullValue), - make_pair("nodes", toJson(_node.nodes())) + exportedSymbols[sym.first] = Json::arrayValue; + for (Declaration const* overload: sym.second) + exportedSymbols[sym.first].append(nodeId(*overload)); } - ); + + attributes.emplace_back("exportedSymbols", exportedSymbols); + }; + + addIfSet(attributes, "absolutePath", _node.annotation().path); + + setJsonNode(_node, "SourceUnit", std::move(attributes)); + return false; } @@ -239,7 +223,7 @@ bool ASTJsonConverter::visit(PragmaDirective const& _node) Json::Value literals(Json::arrayValue); for (auto const& literal: _node.literals()) literals.append(literal); - setJsonNode( _node, "PragmaDirective", { + setJsonNode(_node, "PragmaDirective", { make_pair("literals", std::move(literals)) }); return false; @@ -249,10 +233,12 @@ bool ASTJsonConverter::visit(ImportDirective const& _node) { std::vector> attributes = { make_pair("file", _node.path()), - make_pair("absolutePath", _node.annotation().absolutePath), - make_pair(m_legacy ? "SourceUnit" : "sourceUnit", nodeId(*_node.annotation().sourceUnit)), + make_pair("sourceUnit", idOrNull(_node.annotation().sourceUnit)), make_pair("scope", idOrNull(_node.scope())) }; + + addIfSet(attributes, "absolutePath", _node.annotation().absolutePath); + attributes.emplace_back("unitAlias", _node.name()); Json::Value symbolAliases(Json::arrayValue); for (auto const& symbolAlias: _node.symbolAliases()) @@ -270,17 +256,31 @@ bool ASTJsonConverter::visit(ImportDirective const& _node) bool ASTJsonConverter::visit(ContractDefinition const& _node) { - setJsonNode(_node, "ContractDefinition", { + std::vector> attributes = { make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("contractKind", contractKind(_node.contractKind())), make_pair("abstract", _node.abstract()), - make_pair("fullyImplemented", _node.annotation().unimplementedDeclarations.empty()), - make_pair("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)), make_pair("baseContracts", toJson(_node.baseContracts())), make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies, true)), make_pair("nodes", toJson(_node.subNodes())), make_pair("scope", idOrNull(_node.scope())) + }; + + if (_node.annotation().unimplementedDeclarations.has_value()) + attributes.emplace_back("fullyImplemented", _node.annotation().unimplementedDeclarations->empty()); + if (!_node.annotation().linearizedBaseContracts.empty()) + attributes.emplace_back("linearizedBaseContracts", getContainerIds(_node.annotation().linearizedBaseContracts)); + + setJsonNode(_node, "ContractDefinition", std::move(attributes)); + return false; +} + +bool ASTJsonConverter::visit(IdentifierPath const& _node) +{ + setJsonNode(_node, "IdentifierPath", { + make_pair("name", namePathToString(_node.path())), + make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)) }); return false; } @@ -305,23 +305,31 @@ bool ASTJsonConverter::visit(UsingForDirective const& _node) bool ASTJsonConverter::visit(StructDefinition const& _node) { - setJsonNode(_node, "StructDefinition", { + std::vector> attributes = { make_pair("name", _node.name()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), - make_pair("canonicalName", _node.annotation().canonicalName), make_pair("members", toJson(_node.members())), make_pair("scope", idOrNull(_node.scope())) - }); + }; + + addIfSet(attributes,"canonicalName", _node.annotation().canonicalName); + + setJsonNode(_node, "StructDefinition", std::move(attributes)); + return false; } bool ASTJsonConverter::visit(EnumDefinition const& _node) { - setJsonNode(_node, "EnumDefinition", { + std::vector> attributes = { make_pair("name", _node.name()), - make_pair("canonicalName", _node.annotation().canonicalName), make_pair("members", toJson(_node.members())) - }); + }; + + addIfSet(attributes,"canonicalName", _node.annotation().canonicalName); + + setJsonNode(_node, "EnumDefinition", std::move(attributes)); + return false; } @@ -351,18 +359,11 @@ bool ASTJsonConverter::visit(OverrideSpecifier const& _node) bool ASTJsonConverter::visit(FunctionDefinition const& _node) { - Visibility visibility; - if (_node.isConstructor()) - visibility = _node.annotation().contract->abstract() ? Visibility::Internal : Visibility::Public; - else - visibility = _node.visibility(); - std::vector> attributes = { make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), - make_pair("kind", TokenTraits::toString(_node.kind())), + make_pair("kind", _node.isFree() ? "freeFunction" : TokenTraits::toString(_node.kind())), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), - make_pair("visibility", Declaration::visibilityToString(visibility)), make_pair("virtual", _node.markedVirtual()), make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue), make_pair("parameters", toJson(_node.parameterList())), @@ -373,12 +374,22 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) make_pair("scope", idOrNull(_node.scope())) }; - if (_node.isPartOfExternalInterface()) + optional visibility; + if (_node.isConstructor()) + { + if (_node.annotation().contract) + visibility = _node.annotation().contract->abstract() ? Visibility::Internal : Visibility::Public; + } + else + visibility = _node.visibility(); + + if (visibility) + attributes.emplace_back("visibility", Declaration::visibilityToString(*visibility)); + + if (_node.isPartOfExternalInterface() && m_stackState > CompilerStack::State::ParsedAndImported) attributes.emplace_back("functionSelector", _node.externalIdentifierHex()); if (!_node.annotation().baseFunctions.empty()) attributes.emplace_back(make_pair("baseFunctions", getContainerIds(_node.annotation().baseFunctions, true))); - if (m_legacy) - attributes.emplace_back("isConstructor", _node.isConstructor()); setJsonNode(_node, "FunctionDefinition", std::move(attributes)); return false; } @@ -430,7 +441,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node) bool ASTJsonConverter::visit(ModifierInvocation const& _node) { setJsonNode(_node, "ModifierInvocation", { - make_pair("modifierName", toJson(*_node.name())), + make_pair("modifierName", toJson(_node.name())), make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue) }); return false; @@ -465,9 +476,8 @@ bool ASTJsonConverter::visit(ElementaryTypeName const& _node) bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) { setJsonNode(_node, "UserDefinedTypeName", { - make_pair("name", namePathToString(_node.namePath())), - make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), - make_pair("contractScope", idOrNull(_node.annotation().contractScope)), + make_pair("pathNode", toJson(_node.pathNode())), + make_pair("referencedDeclaration", idOrNull(_node.pathNode().annotation().referencedDeclaration)), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; @@ -522,9 +532,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node) externalReferencesJson.append(std::move(it.second)); setJsonNode(_node, "InlineAssembly", { - m_legacy ? - make_pair("operations", Json::Value(yul::AsmPrinter()(_node.operations()))) : - make_pair("AST", Json::Value(yul::AsmJsonConverter(sourceIndexFromLocation(_node.location()))(_node.operations()))), + make_pair("AST", Json::Value(yul::AsmJsonConverter(sourceIndexFromLocation(_node.location()))(_node.operations()))), make_pair("externalReferences", std::move(externalReferencesJson)), make_pair("evmVersion", dynamic_cast(_node.dialect()).evmVersion().name()) }); @@ -534,7 +542,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node) bool ASTJsonConverter::visit(Block const& _node) { - setJsonNode(_node, "Block", { + setJsonNode(_node, _node.unchecked() ? "UncheckedBlock" : "Block", { make_pair("statements", toJson(_node.statements())) }); return false; @@ -675,7 +683,7 @@ bool ASTJsonConverter::visit(Assignment const& _node) make_pair("rightHandSide", toJson(_node.rightHandSide())) }; appendExpressionAttributes(attributes, _node.annotation()); - setJsonNode( _node, "Assignment", std::move(attributes)); + setJsonNode(_node, "Assignment", std::move(attributes)); return false; } @@ -726,13 +734,13 @@ bool ASTJsonConverter::visit(FunctionCall const& _node) make_pair("arguments", toJson(_node.arguments())), make_pair("tryCall", _node.annotation().tryCall) }; - if (m_legacy) + + if (_node.annotation().kind.set()) { - attributes.emplace_back("isStructConstructorCall", _node.annotation().kind == FunctionCallKind::StructConstructorCall); - attributes.emplace_back("type_conversion", _node.annotation().kind == FunctionCallKind::TypeConversion); + FunctionCallKind nodeKind = *_node.annotation().kind; + attributes.emplace_back("kind", functionCallKind(nodeKind)); } - else - attributes.emplace_back("kind", functionCallKind(_node.annotation().kind)); + appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "FunctionCall", std::move(attributes)); return false; @@ -768,7 +776,7 @@ bool ASTJsonConverter::visit(NewExpression const& _node) bool ASTJsonConverter::visit(MemberAccess const& _node) { std::vector> attributes = { - make_pair(m_legacy ? "member_name" : "memberName", _node.memberName()), + make_pair("memberName", _node.memberName()), make_pair("expression", toJson(_node.expression())), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), }; @@ -806,7 +814,7 @@ bool ASTJsonConverter::visit(Identifier const& _node) for (auto const& dec: _node.annotation().overloadedDeclarations) overloads.append(nodeId(*dec)); setJsonNode(_node, "Identifier", { - make_pair(m_legacy ? "value" : "name", _node.name()), + make_pair("name", _node.name()), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), make_pair("overloadedDeclarations", overloads), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)), @@ -818,7 +826,7 @@ bool ASTJsonConverter::visit(Identifier const& _node) bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node) { std::vector> attributes = { - make_pair(m_legacy ? "value" : "typeName", toJson(_node.type())) + make_pair("typeName", toJson(_node.type())) }; appendExpressionAttributes(attributes, _node.annotation()); setJsonNode(_node, "ElementaryTypeNameExpression", std::move(attributes)); @@ -832,9 +840,9 @@ bool ASTJsonConverter::visit(Literal const& _node) value = Json::nullValue; Token subdenomination = Token(_node.subDenomination()); std::vector> attributes = { - make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())), + make_pair("kind", literalTokenKind(_node.token())), make_pair("value", value), - make_pair(m_legacy ? "hexvalue" : "hexValue", util::toHex(util::asBytes(_node.value()))), + make_pair("hexValue", util::toHex(util::asBytes(_node.value()))), make_pair( "subdenomination", subdenomination == Token::Illegal ? diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index bb5d9651c199..8e01f2390f12 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -50,15 +51,15 @@ class ASTJsonConverter: public ASTConstVisitor { public: /// Create a converter to JSON for the given abstract syntax tree. - /// @a _legacy if true, use legacy format + /// @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( - bool _legacy, + CompilerStack::State _stackState, std::map _sourceIndices = std::map() ); /// Output the json representation of the AST to _stream. void print(std::ostream& _stream, ASTNode const& _node); - Json::Value&& toJson(ASTNode const& _node); + Json::Value toJson(ASTNode const& _node); template Json::Value toJson(std::vector> const& _nodes) { @@ -74,6 +75,7 @@ class ASTJsonConverter: public ASTConstVisitor bool visit(PragmaDirective const& _node) override; bool visit(ImportDirective const& _node) override; bool visit(ContractDefinition const& _node) override; + bool visit(IdentifierPath const& _node) override; bool visit(InheritanceSpecifier const& _node) override; bool visit(UsingForDirective const& _node) override; bool visit(StructDefinition const& _node) override; @@ -153,14 +155,14 @@ class ASTJsonConverter: public ASTConstVisitor static std::string literalTokenKind(Token _token); static std::string type(Expression const& _expression); static std::string type(VariableDeclaration const& _varDecl); - static int nodeId(ASTNode const& _node) + static int64_t nodeId(ASTNode const& _node) { return _node.id(); } template static Json::Value getContainerIds(Container const& _container, bool _order = false) { - std::vector tmp; + std::vector tmp; for (auto const& element: _container) { @@ -171,7 +173,7 @@ class ASTJsonConverter: public ASTConstVisitor std::sort(tmp.begin(), tmp.end()); Json::Value json(Json::arrayValue); - for (int val: tmp) + for (int64_t val: tmp) json.append(val); return json; @@ -188,7 +190,7 @@ class ASTJsonConverter: public ASTConstVisitor _array.append(std::move(_value)); } - bool m_legacy = false; ///< if true, use legacy format + CompilerStack::State m_stackState = CompilerStack::State::Empty; ///< Used to only access information that already exists bool m_inEvent = false; ///< whether we are currently inside an event or not Json::Value m_currentValue; std::map m_sourceIndices; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 2d1fda322b64..29b8961e547b 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -22,18 +22,21 @@ */ #include -#include -#include -#include -#include -#include -#include + +#include #include +#include +#include #include -#include -#include + #include +#include +#include +#include +#include +#include +#include using namespace std; @@ -115,6 +118,8 @@ ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js return createImportDirective(_json); if (nodeType == "ContractDefinition") return createContractDefinition(_json); + if (nodeType == "IdentifierPath") + return createIdentifierPath(_json); if (nodeType == "InheritanceSpecifier") return createInheritanceSpecifier(_json); if (nodeType == "UsingForDirective") @@ -152,7 +157,9 @@ ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js if (nodeType == "InlineAssembly") return createInlineAssembly(_json); if (nodeType == "Block") - return createBlock(_json); + return createBlock(_json, false); + if (nodeType == "UncheckedBlock") + return createBlock(_json, true); if (nodeType == "PlaceholderStatement") return createPlaceholderStatement(_json); if (nodeType == "IfStatement") @@ -299,6 +306,23 @@ ASTPointer ASTJsonImporter::createContractDefinition(Json::V ); } +ASTPointer ASTJsonImporter::createIdentifierPath(Json::Value const& _node) +{ + astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); + + vector namePath; + vector strs; + string nameString = member(_node, "name").asString(); + boost::algorithm::split(strs, nameString, boost::is_any_of(".")); + astAssert(!strs.empty(), "Expected at least one element in IdentifierPath."); + for (string s: strs) + { + astAssert(!s.empty(), "Expected non-empty string for IdentifierPath element."); + namePath.emplace_back(s); + } + return createASTNode(_node, namePath); +} + ASTPointer ASTJsonImporter::createInheritanceSpecifier(Json::Value const& _node) { std::vector> arguments; @@ -306,7 +330,7 @@ ASTPointer ASTJsonImporter::createInheritanceSpecifier(Jso arguments.push_back(convertJsonToASTNode(arg)); return createASTNode( _node, - createUserDefinedTypeName(member(_node, "baseName")), + createIdentifierPath(member(_node, "baseName")), member(_node, "arguments").isNull() ? nullptr : make_unique>>(arguments) ); } @@ -315,7 +339,7 @@ ASTPointer ASTJsonImporter::createUsingForDirective(Json::Val { return createASTNode( _node, - createUserDefinedTypeName(member(_node, "libraryName")), + createIdentifierPath(member(_node, "libraryName")), _node["typeName"].isNull() ? nullptr : convertJsonToASTNode(_node["typeName"]) ); } @@ -365,10 +389,10 @@ ASTPointer ASTJsonImporter::createParameterList(Json::Value const ASTPointer ASTJsonImporter::createOverrideSpecifier(Json::Value const& _node) { - std::vector> overrides; + std::vector> overrides; for (auto& param: _node["overrides"]) - overrides.push_back(createUserDefinedTypeName(param)); + overrides.push_back(createIdentifierPath(param)); return createASTNode( _node, @@ -381,6 +405,7 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V astAssert(_node["kind"].isString(), "Expected 'kind' to be a string!"); Token kind; + bool freeFunction = false; string kindStr = member(_node, "kind").asString(); if (kindStr == "constructor") @@ -391,17 +416,27 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V kind = Token::Fallback; else if (kindStr == "receive") kind = Token::Receive; + else if (kindStr == "freeFunction") + { + kind = Token::Function; + freeFunction = true; + } else astAssert(false, "Expected 'kind' to be one of [constructor, function, fallback, receive]"); std::vector> modifiers; for (auto& mod: member(_node, "modifiers")) modifiers.push_back(createModifierInvocation(mod)); + + Visibility vis = Visibility::Default; + if (!freeFunction) + vis = visibility(_node); return createASTNode( _node, memberAsASTString(_node, "name"), - kind == Token::Constructor ? Visibility::Default : visibility(_node), + vis, stateMutability(_node), + freeFunction, kind, memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), @@ -409,7 +444,7 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V createParameterList(member(_node, "parameters")), modifiers, createParameterList(member(_node, "returnParameters")), - memberAsBool(_node, "implemented") ? createBlock(member(_node, "body")) : nullptr + memberAsBool(_node, "implemented") ? createBlock(member(_node, "body"), false) : nullptr ); } @@ -443,7 +478,6 @@ ASTPointer ASTJsonImporter::createVariableDeclaration(Json: nullOrCast(member(_node, "value")), visibility(_node), _node["documentation"].isNull() ? nullptr : createDocumentation(member(_node, "documentation")), - memberAsBool(_node, "stateVariable"), _node.isMember("indexed") ? memberAsBool(_node, "indexed") : false, mutability, _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), @@ -460,7 +494,7 @@ ASTPointer ASTJsonImporter::createModifierDefinition(Json::V createParameterList(member(_node, "parameters")), memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), - _node["body"].isNull() ? nullptr: createBlock(member(_node, "body")) + _node["body"].isNull() ? nullptr: createBlock(member(_node, "body"), false) ); } @@ -471,7 +505,7 @@ ASTPointer ASTJsonImporter::createModifierInvocation(Json::V arguments.push_back(convertJsonToASTNode(arg)); return createASTNode( _node, - createIdentifier(member(_node, "modifierName")), + createIdentifierPath(member(_node, "modifierName")), member(_node, "arguments").isNull() ? nullptr : make_unique>>(arguments) ); } @@ -508,17 +542,9 @@ ASTPointer ASTJsonImporter::createElementaryTypeName(Json::V ASTPointer ASTJsonImporter::createUserDefinedTypeName(Json::Value const& _node) { - astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); - - vector namePath; - vector strs; - string nameString = member(_node, "name").asString(); - boost::algorithm::split(strs, nameString, boost::is_any_of(".")); - for (string s: strs) - namePath.emplace_back(s); return createASTNode( _node, - namePath + createIdentifierPath(member(_node, "pathNode")) ); } @@ -559,7 +585,7 @@ ASTPointer ASTJsonImporter::createInlineAssembly(Json::Value con astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!"); yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value()); - shared_ptr operations = make_shared(AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST"))); + shared_ptr operations = make_shared(yul::AsmJsonImporter(m_currentSourceName).createBlock(member(_node, "AST"))); return createASTNode( _node, nullOrASTString(_node, "documentation"), @@ -568,7 +594,7 @@ ASTPointer ASTJsonImporter::createInlineAssembly(Json::Value con ); } -ASTPointer ASTJsonImporter::createBlock(Json::Value const& _node) +ASTPointer ASTJsonImporter::createBlock(Json::Value const& _node, bool _unchecked) { std::vector> statements; for (auto& stat: member(_node, "statements")) @@ -576,6 +602,7 @@ ASTPointer ASTJsonImporter::createBlock(Json::Value const& _node) return createASTNode( _node, nullOrASTString(_node, "documentation"), + _unchecked, statements ); } diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h index 0bfb111bf4ea..c64790d4df9d 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -74,6 +74,7 @@ class ASTJsonImporter ASTPointer createPragmaDirective(Json::Value const& _node); ASTPointer createImportDirective(Json::Value const& _node); ASTPointer createContractDefinition(Json::Value const& _node); + ASTPointer createIdentifierPath(Json::Value const& _node); ASTPointer createInheritanceSpecifier(Json::Value const& _node); ASTPointer createUsingForDirective(Json::Value const& _node); ASTPointer createStructDefinition(Json::Value const& _node); @@ -92,7 +93,7 @@ class ASTJsonImporter ASTPointer createMapping(Json::Value const& _node); ASTPointer createArrayTypeName(Json::Value const& _node); ASTPointer createInlineAssembly(Json::Value const& _node); - ASTPointer createBlock(Json::Value const& _node); + ASTPointer createBlock(Json::Value const& _node, bool _unchecked); ASTPointer createPlaceholderStatement(Json::Value const& _node); ASTPointer createIfStatement(Json::Value const& _node); ASTPointer createTryCatchClause(Json::Value const& _node); diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index c5aeba402f30..9ad652b06365 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -58,6 +58,7 @@ class ASTVisitor virtual bool visit(PragmaDirective& _node) { return visitNode(_node); } virtual bool visit(ImportDirective& _node) { return visitNode(_node); } virtual bool visit(ContractDefinition& _node) { return visitNode(_node); } + virtual bool visit(IdentifierPath& _node) { return visitNode(_node); } virtual bool visit(InheritanceSpecifier& _node) { return visitNode(_node); } virtual bool visit(UsingForDirective& _node) { return visitNode(_node); } virtual bool visit(StructDefinition& _node) { return visitNode(_node); } @@ -110,6 +111,7 @@ class ASTVisitor virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } virtual void endVisit(ImportDirective& _node) { endVisitNode(_node); } virtual void endVisit(ContractDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(IdentifierPath& _node) { endVisitNode(_node); } virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); } virtual void endVisit(UsingForDirective& _node) { endVisitNode(_node); } virtual void endVisit(StructDefinition& _node) { endVisitNode(_node); } @@ -184,6 +186,7 @@ class ASTConstVisitor virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); } virtual bool visit(ImportDirective const& _node) { return visitNode(_node); } virtual bool visit(ContractDefinition const& _node) { return visitNode(_node); } + virtual bool visit(IdentifierPath const& _node) { return visitNode(_node); } virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); } virtual bool visit(StructDefinition const& _node) { return visitNode(_node); } virtual bool visit(UsingForDirective const& _node) { return visitNode(_node); } @@ -236,6 +239,7 @@ class ASTConstVisitor virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); } virtual void endVisit(ContractDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(IdentifierPath const& _node) { endVisitNode(_node); } virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); } virtual void endVisit(UsingForDirective const& _node) { endVisitNode(_node); } virtual void endVisit(StructDefinition const& _node) { endVisitNode(_node); } @@ -313,46 +317,4 @@ class SimpleASTVisitor: public ASTConstVisitor std::function m_onEndVisit; }; -/** - * Utility class that visits the AST in depth-first order and calls a function on each node and each edge. - * Child nodes are only visited if the node callback of the parent returns true. - * The node callback of a parent is called before any edge or node callback involving the children. - * The edge callbacks of all children are called before the edge callback of the parent. - * This way, the node callback can be used as an initializing callback and the edge callbacks can be - * used to compute a "reduce" function. - */ -class ASTReduce: public ASTConstVisitor -{ -public: - /** - * Constructs a new ASTReduce object with the given callback functions. - * @param _onNode called for each node, before its child edges and nodes, should return true to descend deeper - * @param _onEdge called for each edge with (parent, child) - */ - ASTReduce( - std::function _onNode, - std::function _onEdge - ): m_onNode(std::move(_onNode)), m_onEdge(std::move(_onEdge)) - { - } - -protected: - bool visitNode(ASTNode const& _node) override - { - m_parents.push_back(&_node); - return m_onNode(_node); - } - void endVisitNode(ASTNode const& _node) override - { - m_parents.pop_back(); - if (!m_parents.empty()) - m_onEdge(*m_parents.back(), _node); - } - -private: - std::vector m_parents; - std::function m_onNode; - std::function m_onEdge; -}; - } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 2105e369da4d..afd9838a19f5 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -104,6 +104,18 @@ void ContractDefinition::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void IdentifierPath::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void IdentifierPath::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + void InheritanceSpecifier::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) @@ -368,13 +380,15 @@ void ElementaryTypeName::accept(ASTConstVisitor& _visitor) const void UserDefinedTypeName::accept(ASTVisitor& _visitor) { - _visitor.visit(*this); + if (_visitor.visit(*this)) + this->pathNode().accept(_visitor); _visitor.endVisit(*this); } void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const { - _visitor.visit(*this); + if (_visitor.visit(*this)) + this->pathNode().accept(_visitor); _visitor.endVisit(*this); } diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index fd63d12b4a48..17fe8146cf04 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -372,7 +372,7 @@ RationalNumberType const* TypeProvider::rationalNumber(Literal const& _literal) { size_t const digitCount = _literal.valueWithoutUnderscores().length() - 2; if (digitCount % 2 == 0 && (digitCount / 2) <= 32) - compatibleBytesType = fixedBytes(digitCount / 2); + compatibleBytesType = fixedBytes(static_cast(digitCount / 2)); } return rationalNumber(std::get<1>(validLiteral), compatibleBytesType); diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 651897f0cd8f..e2c68c3c144b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -26,9 +26,12 @@ #include #include +#include + #include #include #include +#include #include #include @@ -44,6 +47,7 @@ #include #include +#include #include using namespace std; @@ -54,101 +58,6 @@ using namespace solidity::frontend; namespace { -struct TypeComp -{ - bool operator()(Type const* lhs, Type const* rhs) const - { - solAssert(lhs && rhs, ""); - return lhs->richIdentifier() < rhs->richIdentifier(); - } -}; -using TypeSet = std::set; - -void oversizedSubtypesInner( - Type const& _type, - bool _includeType, - set& _structsSeen, - TypeSet& _oversizedSubtypes -) -{ - switch (_type.category()) - { - case Type::Category::Array: - { - auto const& t = dynamic_cast(_type); - if (_includeType && t.storageSizeUpperBound() >= bigint(1) << 64) - _oversizedSubtypes.insert(&t); - oversizedSubtypesInner(*t.baseType(), t.isDynamicallySized(), _structsSeen, _oversizedSubtypes); - break; - } - case Type::Category::Struct: - { - auto const& t = dynamic_cast(_type); - if (_structsSeen.count(&t.structDefinition())) - return; - if (_includeType && t.storageSizeUpperBound() >= bigint(1) << 64) - _oversizedSubtypes.insert(&t); - _structsSeen.insert(&t.structDefinition()); - for (auto const& m: t.members(nullptr)) - oversizedSubtypesInner(*m.type, false, _structsSeen, _oversizedSubtypes); - _structsSeen.erase(&t.structDefinition()); - break; - } - case Type::Category::Mapping: - { - auto const* valueType = dynamic_cast(_type).valueType(); - oversizedSubtypesInner(*valueType, true, _structsSeen, _oversizedSubtypes); - break; - } - default: - break; - } -} - -/// Check whether (_base ** _exp) fits into 4096 bits. -bool fitsPrecisionExp(bigint const& _base, bigint const& _exp) -{ - if (_base == 0) - return true; - - solAssert(_base > 0, ""); - - size_t const bitsMax = 4096; - - unsigned mostSignificantBaseBit = boost::multiprecision::msb(_base); - if (mostSignificantBaseBit == 0) // _base == 1 - return true; - if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096 - return false; - - bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1); - - return bitsNeeded <= bitsMax; -} - -/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits, -/// where X is given indirectly via _log2OfBase = log2(X). -bool fitsPrecisionBaseX( - bigint const& _mantissa, - double _log2OfBase, - uint32_t _exp -) -{ - if (_mantissa == 0) - return true; - - solAssert(_mantissa > 0, ""); - - size_t const bitsMax = 4096; - - unsigned mostSignificantMantissaBit = boost::multiprecision::msb(_mantissa); - if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096 - return false; - - bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1; - return bitsNeeded <= bitsMax; -} - /// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits. bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) { @@ -156,12 +65,6 @@ bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); } -/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits. -bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) -{ - return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); -} - /// Checks whether _value fits into IntegerType _type. BoolResult fitsIntegerType(bigint const& _value, IntegerType const& _type) { @@ -178,10 +81,13 @@ BoolResult fitsIntegerType(bigint const& _value, IntegerType const& _type) /// if _signed is true. bool fitsIntoBits(bigint const& _value, unsigned _bits, bool _signed) { - return fitsIntegerType(_value, *TypeProvider::integer( - _bits, - _signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned - )); + return fitsIntegerType( + _value, + *TypeProvider::integer( + _bits, + _signed ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned + ) + ); } util::Result transformParametersToExternal(TypePointers const& _parameters, bool _inLibrary) @@ -190,7 +96,9 @@ util::Result transformParametersToExternal(TypePointers const& _pa for (auto const& type: _parameters) { - if (TypePointer ext = type->interfaceType(_inLibrary).get()) + if (!type) + return util::Result::err("Type information not present."); + else if (TypePointer ext = type->interfaceType(_inLibrary).get()) transformed.push_back(ext); else return util::Result::err("Parameter should have external type."); @@ -201,16 +109,6 @@ util::Result transformParametersToExternal(TypePointers const& _pa } -vector solidity::frontend::oversizedSubtypes(frontend::Type const& _type) -{ - set structsSeen; - TypeSet oversized; - oversizedSubtypesInner(_type, false, structsSeen, oversized); - vector res; - copy(oversized.cbegin(), oversized.cend(), back_inserter(res)); - return res; -} - void Type::clearCache() const { m_members.clear(); @@ -404,10 +302,16 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c return encodingType; TypePointer baseType = encodingType; while (auto const* arrayType = dynamic_cast(baseType)) + { baseType = arrayType->baseType(); - if (dynamic_cast(baseType)) - if (!_encoderV2) + + auto const* baseArrayType = dynamic_cast(baseType); + if (!_encoderV2 && baseArrayType && baseArrayType->isDynamicallySized()) return nullptr; + } + if (!_encoderV2 && dynamic_cast(baseType)) + return nullptr; + return encodingType; } @@ -451,7 +355,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _sc ); for (FunctionDefinition const* function: library.definedFunctions()) { - if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) + if (!function->isOrdinary() || !function->isVisibleAsLibraryMember() || seenFunctions.count(function)) continue; seenFunctions.insert(function); if (function->parameters().empty()) @@ -491,13 +395,19 @@ BoolResult AddressType::isImplicitlyConvertibleTo(Type const& _other) const BoolResult AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.category() == category()) + if ((_convertTo.category() == category()) || isImplicitlyConvertibleTo(_convertTo)) return true; else if (auto const* contractType = dynamic_cast(&_convertTo)) return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable(); - return isImplicitlyConvertibleTo(_convertTo) || - _convertTo.category() == Category::Integer || - (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast(_convertTo).numBytes() * 8); + else if (m_stateMutability == StateMutability::NonPayable) + { + if (auto integerType = dynamic_cast(&_convertTo)) + return (!integerType->isSigned() && integerType->numBits() == 160); + else if (auto fixedBytesType = dynamic_cast(&_convertTo)) + return (fixedBytesType->numBytes() == 20); + } + + return false; } string AddressType::toString(bool) const @@ -546,6 +456,8 @@ MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const { MemberList::MemberMap members = { {"balance", TypeProvider::uint256()}, + {"code", TypeProvider::array(DataLocation::Memory)}, + {"codehash", TypeProvider::fixedBytes(32)}, {"isContract", TypeProvider::boolean()}, {"call", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)}, {"callcode", TypeProvider::function(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)}, @@ -629,12 +541,23 @@ BoolResult IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const BoolResult IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.category() == category() || - _convertTo.category() == Category::Address || - _convertTo.category() == Category::Contract || - _convertTo.category() == Category::Enum || - (_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast(_convertTo).numBytes() * 8) || - _convertTo.category() == Category::FixedPoint; + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + else if (auto integerType = dynamic_cast(&_convertTo)) + return (numBits() == integerType->numBits()) || (isSigned() == integerType->isSigned()); + else if (auto addressType = dynamic_cast(&_convertTo)) + return + (addressType->stateMutability() != StateMutability::Payable) && + !isSigned() && + (numBits() == 160); + else if (auto fixedBytesType = dynamic_cast(&_convertTo)) + return (!isSigned() && (numBits() == fixedBytesType->numBytes() * 8)); + else if (dynamic_cast(&_convertTo)) + return true; + else if (auto fixedPointType = dynamic_cast(&_convertTo)) + return (isSigned() == fixedPointType->isSigned()) && (numBits() == fixedPointType->numBits()); + + return false; } TypeResult IntegerType::unaryOperatorResult(Token _operator) const @@ -642,6 +565,11 @@ TypeResult IntegerType::unaryOperatorResult(Token _operator) const // "delete" is ok for all integer types if (_operator == Token::Delete) return TypeResult{TypeProvider::emptyTuple()}; + // unary negation only on signed types + else if (_operator == Token::Sub) + return isSigned() ? TypeResult{this} : TypeResult::err("Unary negation is only allowed for signed integers."); + else if (_operator == Token::Inc || _operator == Token::Dec || _operator == Token::BitNot) + return TypeResult{this}; // no further unary operators for trcToken else if (isTrcToken()) return TypeResult::err(""); @@ -735,6 +663,8 @@ TypeResult IntegerType::binaryOperatorResult(Token _operator, Type const* _other return TypeResult::err("Exponent is fractional."); if (!rationalNumberType->integerType()) return TypeResult::err("Exponent too large."); + if (rationalNumberType->isNegative()) + return TypeResult::err("Exponentiation power is not allowed to be a negative integer literal."); } return this; } @@ -883,7 +813,7 @@ tuple RationalNumberType::parseRational(string const& _value) denominator = bigint(string(fractionalBegin, _value.end())); denominator /= boost::multiprecision::pow( bigint(10), - static_cast(distance(radixPoint + 1, _value.end())) + static_cast(distance(radixPoint + 1, _value.end())) ); numerator = bigint(string(_value.begin(), radixPoint)); value = numerator + denominator; @@ -1039,37 +969,34 @@ BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) { if (isImplicitlyConvertibleTo(_convertTo)) return true; - else if (_convertTo.category() != Category::FixedBytes) - { - TypePointer mobType = mobileType(); - return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo)); - } - else + + auto category = _convertTo.category(); + if (category == Category::FixedBytes) + return false; + else if (auto addressType = dynamic_cast(&_convertTo)) + return (m_value == 0) || + ((addressType->stateMutability() != StateMutability::Payable) && + !isNegative() && + !isFractional() && + integerType() && + (integerType()->numBits() <= 160)); + else if (category == Category::Integer) return false; + else if (auto enumType = dynamic_cast(&_convertTo)) + if (isNegative() || isFractional() || m_value >= enumType->numberOfMembers()) + return false; + + TypePointer mobType = mobileType(); + return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo)); + } TypeResult RationalNumberType::unaryOperatorResult(Token _operator) const { - rational value; - switch (_operator) - { - case Token::BitNot: - if (isFractional()) - return nullptr; - value = ~m_value.numerator(); - break; - case Token::Add: - value = +(m_value); - break; - case Token::Sub: - value = -(m_value); - break; - case Token::After: - return this; - default: + if (optional value = ConstantEvaluator::evaluateUnaryOperator(_operator, m_value)) + return TypeResult{TypeProvider::rationalNumber(*value)}; + else return nullptr; - } - return TypeResult{TypeProvider::rationalNumber(value)}; } TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const* _other) const @@ -1124,165 +1051,16 @@ TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const* return nullptr; return thisMobile->binaryOperatorResult(_operator, otherMobile); } - else + else if (optional value = ConstantEvaluator::evaluateBinaryOperator(_operator, m_value, other.m_value)) { - rational value; - bool fractional = isFractional() || other.isFractional(); - switch (_operator) - { - //bit operations will only be enabled for integers and fixed types that resemble integers - case Token::BitOr: - if (fractional) - return nullptr; - value = m_value.numerator() | other.m_value.numerator(); - break; - case Token::BitXor: - if (fractional) - return nullptr; - value = m_value.numerator() ^ other.m_value.numerator(); - break; - case Token::BitAnd: - if (fractional) - return nullptr; - value = m_value.numerator() & other.m_value.numerator(); - break; - case Token::Add: - value = m_value + other.m_value; - break; - case Token::Sub: - value = m_value - other.m_value; - break; - case Token::Mul: - value = m_value * other.m_value; - break; - case Token::Div: - if (other.m_value == rational(0)) - return nullptr; - else - value = m_value / other.m_value; - break; - case Token::Mod: - if (other.m_value == rational(0)) - return nullptr; - else if (fractional) - { - rational tempValue = m_value / other.m_value; - value = m_value - (tempValue.numerator() / tempValue.denominator()) * other.m_value; - } - else - value = m_value.numerator() % other.m_value.numerator(); - break; - case Token::Exp: - { - if (other.isFractional()) - return nullptr; - solAssert(other.m_value.denominator() == 1, ""); - bigint const& exp = other.m_value.numerator(); - - // x ** 0 = 1 - // for 0, 1 and -1 the size of the exponent doesn't have to be restricted - if (exp == 0) - value = 1; - else if (m_value.numerator() == 0 || m_value == 1) - value = m_value; - else if (m_value == -1) - { - bigint isOdd = abs(exp) & bigint(1); - value = 1 - 2 * isOdd.convert_to(); - } - else - { - if (abs(exp) > numeric_limits::max()) - return nullptr; // This will need too much memory to represent. - - uint32_t absExp = bigint(abs(exp)).convert_to(); - - if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp)) - return TypeResult::err("Precision of rational constants is limited to 4096 bits."); - - static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint { - if (_base == 1) - return 1; - else if (_base == -1) - return 1 - 2 * static_cast(_exponent & 1); - else - return boost::multiprecision::pow(_base, _exponent); - }; - - bigint numerator = optimizedPow(m_value.numerator(), absExp); - bigint denominator = optimizedPow(m_value.denominator(), absExp); - - if (exp >= 0) - value = makeRational(numerator, denominator); - else - // invert - value = makeRational(denominator, numerator); - } - break; - } - case Token::SHL: - { - if (fractional) - return nullptr; - else if (other.m_value < 0) - return nullptr; - else if (other.m_value > numeric_limits::max()) - return nullptr; - if (m_value.numerator() == 0) - value = 0; - else - { - uint32_t exponent = other.m_value.numerator().convert_to(); - if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent)) - return nullptr; - value = m_value.numerator() * boost::multiprecision::pow(bigint(2), exponent); - } - break; - } - // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue - // determines the resulting type and the type of shift (SAR or SHR). - case Token::SAR: - { - if (fractional) - return nullptr; - else if (other.m_value < 0) - return nullptr; - else if (other.m_value > numeric_limits::max()) - return nullptr; - if (m_value.numerator() == 0) - value = 0; - else - { - uint32_t exponent = other.m_value.numerator().convert_to(); - if (exponent > boost::multiprecision::msb(boost::multiprecision::abs(m_value.numerator()))) - value = m_value.numerator() < 0 ? -1 : 0; - else - { - if (m_value.numerator() < 0) - // Add 1 to the negative value before dividing to get a result that is strictly too large, - // then subtract 1 afterwards to round towards negative infinity. - // This is the same algorithm as used in ExpressionCompiler::appendShiftOperatorCode(...). - // To see this note that for negative x, xor(x,all_ones) = (-x-1) and - // therefore xor(div(xor(x,all_ones), exp(2, shift_amount)), all_ones) is - // -(-x - 1) / 2^shift_amount - 1, which is the same as - // (x + 1) / 2^shift_amount - 1. - value = rational((m_value.numerator() + 1) / boost::multiprecision::pow(bigint(2), exponent) - bigint(1), 1); - else - value = rational(m_value.numerator() / boost::multiprecision::pow(bigint(2), exponent), 1); - } - } - break; - } - default: - return nullptr; - } - // verify that numerator and denominator fit into 4096 bit after every operation - if (value.numerator() != 0 && max(boost::multiprecision::msb(abs(value.numerator())), boost::multiprecision::msb(abs(value.denominator()))) > 4096) + if (value->numerator() != 0 && max(boost::multiprecision::msb(abs(value->numerator())), boost::multiprecision::msb(abs(value->denominator()))) > 4096) return TypeResult::err("Precision of rational constants is limited to 4096 bits."); - return TypeResult{TypeProvider::rationalNumber(value)}; + return TypeResult{TypeProvider::rationalNumber(*value)}; } + else + return nullptr; } string RationalNumberType::richIdentifier() const @@ -1430,12 +1208,25 @@ StringLiteralType::StringLiteralType(string _value): BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (auto fixedBytes = dynamic_cast(&_convertTo)) - return static_cast(fixedBytes->numBytes()) >= m_value.size(); + { + if (static_cast(fixedBytes->numBytes()) < m_value.size()) + return BoolResult::err("Literal is larger than the type."); + return true; + } else if (auto arrayType = dynamic_cast(&_convertTo)) + { + size_t invalidSequence; + if (arrayType->isString() && !util::validateUTF8(value(), invalidSequence)) + return BoolResult::err( + "Contains invalid UTF-8 sequence at position " + + util::toString(invalidSequence) + + "." + ); return + arrayType->location() != DataLocation::CallData && arrayType->isByteArray() && - !(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()) && - !(arrayType->isString() && !util::validateUTF8(value())); + !(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()); + } else return false; } @@ -1456,12 +1247,19 @@ bool StringLiteralType::operator==(Type const& _other) const std::string StringLiteralType::toString(bool) const { - size_t invalidSequence; - - if (!util::validateUTF8(m_value, invalidSequence)) - return "literal_string (contains invalid UTF-8 sequence at position " + util::toString(invalidSequence) + ")"; + auto isPrintableASCII = [](string const& s) + { + for (auto c: s) + { + if (static_cast(c) <= 0x1f || static_cast(c) >= 0x7f) + return false; + } + return true; + }; - return "literal_string \"" + m_value + "\""; + return isPrintableASCII(m_value) ? + ("literal_string \"" + m_value + "\"") : + ("literal_string hex\"" + util::toHex(util::asBytes(m_value)) + "\""); } TypePointer StringLiteralType::mobileType() const @@ -1487,10 +1285,18 @@ BoolResult FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) con BoolResult FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast(_convertTo).numBits()) || - (_convertTo.category() == Category::Address && numBytes() == 20) || - _convertTo.category() == Category::FixedPoint || - _convertTo.category() == category(); + if (_convertTo.category() == category()) + return true; + else if (auto integerType = dynamic_cast(&_convertTo)) + return (!integerType->isSigned() && integerType->numBits() == numBytes() * 8); + else if (auto addressType = dynamic_cast(&_convertTo)) + return + (addressType->stateMutability() != StateMutability::Payable) && + (numBytes() == 20); + else if (auto fixedPointType = dynamic_cast(&_convertTo)) + return fixedPointType->numBits() == numBytes() * 8; + + return false; } TypeResult FixedBytesType::unaryOperatorResult(Token _operator) const @@ -1594,12 +1400,15 @@ BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const return true; if (_convertTo.category() == Category::Contract) { - auto const& bases = contractDefinition().annotation().linearizedBaseContracts; - if (m_super && bases.size() <= 1) + auto const& targetContractType = dynamic_cast(_convertTo); + if (targetContractType.isSuper()) return false; + + auto const& bases = contractDefinition().annotation().linearizedBaseContracts; return find( - m_super ? ++bases.begin() : bases.begin(), bases.end(), - &dynamic_cast(_convertTo).contractDefinition() + bases.begin(), + bases.end(), + &targetContractType.contractDefinition() ) != bases.end(); } return false; @@ -1633,6 +1442,21 @@ TypeResult ContractType::unaryOperatorResult(Token _operator) const return nullptr; } +vector CompositeType::fullDecomposition() const +{ + vector res = {this}; + unordered_set seen = {richIdentifier()}; + for (size_t k = 0; k < res.size(); ++k) + if (auto composite = dynamic_cast(res[k])) + for (Type const* next: composite->decomposition()) + if (seen.count(next->richIdentifier()) == 0) + { + seen.insert(next->richIdentifier()); + res.push_back(next); + } + return res; +} + Type const* ReferenceType::withLocation(DataLocation _location, bool _isPointer) const { return TypeProvider::withLocation(this, _location, _isPointer); @@ -2112,9 +1936,13 @@ std::unique_ptr ArrayType::copyForLocation(DataLocation _location BoolResult ArraySliceType::isImplicitlyConvertibleTo(Type const& _other) const { - if (m_arrayType.location() == DataLocation::CallData && m_arrayType.isDynamicallySized() && m_arrayType == _other) - return true; - return (*this) == _other; + return + (*this) == _other || + ( + m_arrayType.dataStoredIn(DataLocation::CallData) && + m_arrayType.isDynamicallySized() && + m_arrayType.isImplicitlyConvertibleTo(_other) + ); } string ArraySliceType::richIdentifier() const @@ -2175,42 +2003,14 @@ string ContractType::toString(bool) const string ContractType::canonicalName() const { - return m_contract.annotation().canonicalName; + return *m_contract.annotation().canonicalName; } MemberList::MemberMap ContractType::nativeMembers(ASTNode const*) const { MemberList::MemberMap members; - if (m_super) - { - // add the most derived of all functions which are visible in derived contracts - auto bases = m_contract.annotation().linearizedBaseContracts; - solAssert(bases.size() >= 1, "linearizedBaseContracts should at least contain the most derived contract."); - // `sliced(1, ...)` ignores the most derived contract, which should not be searchable from `super`. - for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size())) - for (FunctionDefinition const* function: base->definedFunctions()) - { - if (!function->isVisibleInDerivedContracts() || !function->isImplemented()) - continue; - - auto functionType = TypeProvider::function(*function, FunctionType::Kind::Internal); - bool functionWithEqualArgumentsFound = false; - for (auto const& member: members) - { - if (member.name != function->name()) - continue; - auto memberType = dynamic_cast(member.type); - solAssert(!!memberType, "Override changes type."); - if (!memberType->hasEqualParameterTypes(*functionType)) - continue; - functionWithEqualArgumentsFound = true; - break; - } - if (!functionWithEqualArgumentsFound) - members.emplace_back(function->name(), functionType, function); - } - } - else if (!m_contract.isLibrary()) + solAssert(!m_super, ""); + if (!m_contract.isLibrary()) for (auto const& it: m_contract.interfaceFunctions()) members.emplace_back( it.second->declaration().name(), @@ -2426,7 +2226,7 @@ bool StructType::containsNestedMapping() const string StructType::toString(bool _short) const { - string ret = "struct " + m_struct.annotation().canonicalName; + string ret = "struct " + *m_struct.annotation().canonicalName; if (!_short) ret += " " + stringForReferencePart(); return ret; @@ -2488,52 +2288,56 @@ TypeResult StructType::interfaceType(bool _inLibrary) const TypeResult result{TypePointer{}}; + if (recursive() && !(_inLibrary && location() == DataLocation::Storage)) + return TypeResult::err( + "Recursive structs can only be passed as storage pointers to libraries, " + "not as memory objects to contract functions." + ); + util::BreadthFirstSearch breadthFirstSearch{{&m_struct}}; breadthFirstSearch.run( - [&](StructDefinition const* _struct, auto&& _addChild) { - // Check that all members have interface types. - // Return an error if at least one struct member does not have a type. - // This might happen, for example, if the type of the member does not exist. - for (ASTPointer const& variable: _struct->members()) + [&](StructDefinition const* _struct, auto&& _addChild) + { + // Check that all members have interface types. + // Return an error if at least one struct member does not have a type. + // This might happen, for example, if the type of the member does not exist. + for (ASTPointer const& variable: _struct->members()) + { + // If the struct member does not have a type return false. + // A TypeError is expected in this case. + if (!variable->annotation().type) { - // If the struct member does not have a type return false. - // A TypeError is expected in this case. - if (!variable->annotation().type) - { - result = TypeResult::err("Invalid type!"); - breadthFirstSearch.abort(); - return; - } + result = TypeResult::err("Invalid type!"); + breadthFirstSearch.abort(); + return; + } - Type const* memberType = variable->annotation().type; + Type const* memberType = variable->annotation().type; - while (dynamic_cast(memberType)) - memberType = dynamic_cast(memberType)->baseType(); + while ( + memberType->category() == Type::Category::Array || + memberType->category() == Type::Category::Mapping + ) + { + if (auto arrayType = dynamic_cast(memberType)) + memberType = arrayType->finalBaseType(false); + else if (auto mappingType = dynamic_cast(memberType)) + memberType = mappingType->valueType(); + } - if (StructType const* innerStruct = dynamic_cast(memberType)) - { - if (innerStruct->recursive() && !(_inLibrary && location() == DataLocation::Storage)) - { - result = TypeResult::err( - "Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions." - ); - breadthFirstSearch.abort(); - return; - } - else - _addChild(&innerStruct->structDefinition()); - } - else + if (StructType const* innerStruct = dynamic_cast(memberType)) + _addChild(&innerStruct->structDefinition()); + else + { + auto iType = memberType->interfaceType(_inLibrary); + if (!iType.get()) { - auto iType = memberType->interfaceType(_inLibrary); - if (!iType.get()) - { - solAssert(!iType.message().empty(), "Expected detailed error message!"); - result = iType; - breadthFirstSearch.abort(); - return; - } + solAssert(!iType.message().empty(), "Expected detailed error message!"); + result = iType; + breadthFirstSearch.abort(); + return; } + } } } ); @@ -2601,7 +2405,7 @@ string StructType::signatureInExternalFunction(bool _structsByName) const string StructType::canonicalName() const { - return m_struct.annotation().canonicalName; + return *m_struct.annotation().canonicalName; } FunctionTypePointer StructType::constructorType() const @@ -2666,10 +2470,18 @@ vector> StructType::makeStackItems() const solAssert(false, ""); } +vector StructType::decomposition() const +{ + vector res; + for (MemberList::Member const& member: members(nullptr)) + res.push_back(member.type); + return res; +} TypePointer EnumType::encodingType() const { - return TypeProvider::uint(8 * storageBytes()); + solAssert(numberOfMembers() <= 256, ""); + return TypeProvider::uint(8); } TypeResult EnumType::unaryOperatorResult(Token _operator) const @@ -2692,21 +2504,18 @@ bool EnumType::operator==(Type const& _other) const unsigned EnumType::storageBytes() const { - size_t elements = numberOfMembers(); - if (elements <= 1) - return 1; - else - return util::bytesRequired(elements - 1); + solAssert(numberOfMembers() <= 256, ""); + return 1; } string EnumType::toString(bool) const { - return string("enum ") + m_enum.annotation().canonicalName; + return string("enum ") + *m_enum.annotation().canonicalName; } string EnumType::canonicalName() const { - return m_enum.annotation().canonicalName; + return *m_enum.annotation().canonicalName; } size_t EnumType::numberOfMembers() const @@ -2716,7 +2525,11 @@ size_t EnumType::numberOfMembers() const BoolResult EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo == *this || _convertTo.category() == Category::Integer; + if (_convertTo == *this) + return true; + else if (auto integerType = dynamic_cast(&_convertTo)) + return !integerType->isSigned(); + return false; } unsigned EnumType::memberValue(ASTString const& _member) const @@ -3028,8 +2841,11 @@ TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const m_kind == Kind::BareStaticCall ) for (auto& param: returnParameterTypes) - if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage)) + { + solAssert(param->decodingType(), ""); + if (param->decodingType()->isDynamicallyEncoded()) param = TypeProvider::inaccessibleDynamic(); + } return returnParameterTypes; } @@ -3041,6 +2857,11 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } +TypePointers const& FunctionType::parameterTypesIncludingSelf() const +{ + return m_parameterTypes; +} + string FunctionType::richIdentifier() const { string id = "t_function_"; @@ -3071,11 +2892,6 @@ string FunctionType::richIdentifier() const case Kind::pedersenHash: id += "pedersenHash";break; case Kind::SHA256: id += "sha256"; break; case Kind::RIPEMD160: id += "ripemd160"; break; - case Kind::Log0: id += "log0"; break; - case Kind::Log1: id += "log1"; break; - case Kind::Log2: id += "log2"; break; - case Kind::Log3: id += "log3"; break; - case Kind::Log4: id += "log4"; break; case Kind::GasLeft: id += "gasleft"; break; case Kind::Event: id += "event"; break; case Kind::SetGas: id += "setgas"; break; @@ -3141,6 +2957,13 @@ BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const FunctionType const& convertTo = dynamic_cast(_convertTo); + // These two checks are duplicated in equalExcludingStateMutability, but are added here for error reporting. + if (convertTo.bound() != bound()) + return BoolResult::err("Bound functions can not be converted to non-bound functions."); + + if (convertTo.kind() != kind()) + return BoolResult::err("Special functions can not be converted to function types."); + if (!equalExcludingStateMutability(convertTo)) return false; @@ -3189,10 +3012,8 @@ string FunctionType::toString(bool _short) const { auto const* functionDefinition = dynamic_cast(m_declaration); solAssert(functionDefinition, ""); - auto const* contract = dynamic_cast(functionDefinition->scope()); - solAssert(contract, ""); - name += contract->annotation().canonicalName; - name += '.'; + if (auto const* contract = dynamic_cast(functionDefinition->scope())) + name += *contract->annotation().canonicalName + "."; name += functionDefinition->name(); } name += '('; @@ -3304,8 +3125,7 @@ vector> FunctionType::makeStackItems() const if (m_saltSet) slots.emplace_back("salt", TypeProvider::fixedBytes(32)); if (bound()) - for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems()) - slots.emplace_back("self_" + boundName, boundType); + slots.emplace_back("self", m_parameterTypes.front()); return slots; } @@ -3313,7 +3133,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const { // Note that m_declaration might also be a state variable! solAssert(m_declaration, "Declaration needed to determine interface function type."); - bool isLibraryFunction = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool isLibraryFunction = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + isLibraryFunction = contract->isLibrary(); util::Result paramTypes = transformParametersToExternal(m_parameterTypes, isLibraryFunction); @@ -3516,12 +3339,12 @@ bool FunctionType::canTakeArguments( size_t matchedNames = 0; - for (auto const& argName: _arguments.names) - for (size_t i = 0; i < paramNames.size(); i++) - if (*argName == paramNames[i]) + for (size_t a = 0; a < _arguments.names.size(); a++) + for (size_t p = 0; p < paramNames.size(); p++) + if (*_arguments.names[a] == paramNames[p]) { matchedNames++; - if (!_arguments.types[i]->isImplicitlyConvertibleTo(*paramTypes[i])) + if (!_arguments.types[a]->isImplicitlyConvertibleTo(*paramTypes[p])) return false; } @@ -3616,7 +3439,10 @@ string FunctionType::externalSignature() const } // "inLibrary" is only relevant if this is not an event. - bool const inLibrary = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool inLibrary = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + inLibrary = contract->isLibrary(); auto extParams = transformParametersToExternal(m_parameterTypes, inLibrary); @@ -3635,7 +3461,7 @@ string FunctionType::externalSignature() const u256 FunctionType::externalIdentifier() const { - return util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256(externalSignature()))); + return util::selectorFromSignature32(externalSignature()); } string FunctionType::externalIdentifierHex() const @@ -3877,7 +3703,11 @@ vector> TypeType::makeStackItems() const { if (auto contractType = dynamic_cast(m_actualType)) if (contractType->contractDefinition().isLibrary()) + { + solAssert(!contractType->isSuper(), ""); return {make_tuple("address", TypeProvider::address())}; + } + return {}; } @@ -3886,30 +3716,65 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons MemberList::MemberMap members; if (m_actualType->category() == Category::Contract) { - auto const* contractScope = dynamic_cast(_currentScope); - ContractDefinition const& contract = dynamic_cast(*m_actualType).contractDefinition(); - bool inDerivingScope = contractScope && contractScope->derivesFrom(contract); + auto contractType = dynamic_cast(m_actualType); + ContractDefinition const& contract = contractType->contractDefinition(); + if (contractType->isSuper()) + { + // add the most derived of all functions which are visible in derived contracts + auto bases = contract.annotation().linearizedBaseContracts; + solAssert(bases.size() >= 1, "linearizedBaseContracts should at least contain the most derived contract."); + // `sliced(1, ...)` ignores the most derived contract, which should not be searchable from `super`. + for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size())) + for (FunctionDefinition const* function: base->definedFunctions()) + { + if (!function->isVisibleInDerivedContracts() || !function->isImplemented()) + continue; - for (auto const* declaration: contract.declarations()) + auto functionType = TypeProvider::function(*function, FunctionType::Kind::Internal); + bool functionWithEqualArgumentsFound = false; + for (auto const& member: members) + { + if (member.name != function->name()) + continue; + auto memberType = dynamic_cast(member.type); + solAssert(!!memberType, "Override changes type."); + if (!memberType->hasEqualParameterTypes(*functionType)) + continue; + functionWithEqualArgumentsFound = true; + break; + } + if (!functionWithEqualArgumentsFound) + members.emplace_back(function->name(), functionType, function); + } + } + else { - if (dynamic_cast(declaration)) - continue; + auto const* contractScope = dynamic_cast(_currentScope); + bool inDerivingScope = contractScope && contractScope->derivesFrom(contract); - if (!contract.isLibrary() && inDerivingScope && declaration->isVisibleInDerivedContracts()) + for (auto const* declaration: contract.declarations()) { - if ( - auto const* functionDefinition = dynamic_cast(declaration); - functionDefinition && !functionDefinition->isImplemented() + if (dynamic_cast(declaration)) + continue; + if (declaration->name().empty()) + continue; + + if (!contract.isLibrary() && inDerivingScope && declaration->isVisibleInDerivedContracts()) + { + if ( + auto const* functionDefinition = dynamic_cast(declaration); + functionDefinition && !functionDefinition->isImplemented() + ) + members.emplace_back(declaration->name(), declaration->typeViaContractName(), declaration); + else + members.emplace_back(declaration->name(), declaration->type(), declaration); + } + else if ( + (contract.isLibrary() && declaration->isVisibleAsLibraryMember()) || + declaration->isVisibleViaContractTypeAccess() ) members.emplace_back(declaration->name(), declaration->typeViaContractName(), declaration); - else - members.emplace_back(declaration->name(), declaration->type(), declaration); } - else if ( - (contract.isLibrary() && declaration->isVisibleAsLibraryMember()) || - declaration->isVisibleViaContractTypeAccess() - ) - members.emplace_back(declaration->name(), declaration->typeViaContractName(), declaration); } } else if (m_actualType->category() == Category::Enum) @@ -3993,7 +3858,7 @@ bool ModuleType::operator==(Type const& _other) const MemberList::MemberMap ModuleType::nativeMembers(ASTNode const*) const { MemberList::MemberMap symbols; - for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols) + for (auto const& symbolName: *m_sourceUnit.annotation().exportedSymbols) for (Declaration const* symbol: symbolName.second) symbols.emplace_back(symbolName.first, symbol->type(), symbol); return symbols; @@ -4001,7 +3866,7 @@ MemberList::MemberMap ModuleType::nativeMembers(ASTNode const*) const string ModuleType::toString(bool) const { - return string("module \"") + m_sourceUnit.annotation().path + string("\""); + return string("module \"") + *m_sourceUnit.annotation().path + string("\""); } string MagicType::richIdentifier() const @@ -4042,11 +3907,12 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const {"blockhash", TypeProvider::function(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"difficulty", TypeProvider::uint256()}, {"number", TypeProvider::uint256()}, - {"gaslimit", TypeProvider::uint256()} + {"gaslimit", TypeProvider::uint256()}, + {"chainid", TypeProvider::uint256()} }); case Kind::Message: return MemberList::MemberMap({ - {"sender", TypeProvider::payableAddress()}, + {"sender", TypeProvider::address()}, {"gas", TypeProvider::uint256()}, {"value", TypeProvider::uint256()}, {"data", TypeProvider::array(DataLocation::CallData)}, @@ -4056,7 +3922,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const }); case Kind::Transaction: return MemberList::MemberMap({ - {"origin", TypeProvider::payableAddress()}, + {"origin", TypeProvider::address()}, {"gasprice", TypeProvider::uint256()} }); case Kind::ABI: @@ -4129,6 +3995,7 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const else return MemberList::MemberMap({ {"interfaceId", TypeProvider::fixedBytes(4)}, + {"name", TypeProvider::stringMemory()}, }); } else if (m_typeArgument->category() == Type::Category::Integer) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index bac5f1c4f736..590254a00556 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -60,8 +60,6 @@ using BoolResult = util::Result; namespace solidity::frontend { -std::vector oversizedSubtypes(frontend::Type const& _type); - inline rational makeRational(bigint const& _numerator, bigint const& _denominator) { solAssert(_denominator != 0, "division by zero"); @@ -296,6 +294,7 @@ class Type return *m_stackItems; } /// Total number of stack slots occupied by this type. This is the sum of ``sizeOnStack`` of all ``stackItems()``. + // TODO: consider changing the return type to be size_t unsigned sizeOnStack() const { if (!m_stackSize) @@ -308,7 +307,7 @@ class Type ++sizeOnStack; m_stackSize = sizeOnStack; } - return *m_stackSize; + return static_cast(*m_stackSize); } /// If it is possible to initialize such a value in memory by just writing zeros /// of the size memoryHeadSize(). @@ -572,6 +571,11 @@ class RationalNumberType: public Type u256 literalValue(Literal const* _literal) const override; TypePointer mobileType() const override; + /// @returns the underlying raw literal value. + /// + /// @see literalValue(Literal const*)) + rational const& value() const noexcept { return m_value; } + /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. IntegerType const* integerType() const; /// @returns the smallest fixed type that can hold the value or incurs the least precision loss, @@ -696,11 +700,37 @@ class BoolType: public Type TypeResult interfaceType(bool) const override { return this; } }; +/** + * Base class for types which can be thought of as several elements of other types put together. + * For example a struct is composed of its members, an array is composed of multiple copies of its + * base element and a mapping is composed of its value type elements (note that keys are not + * stored anywhere). + */ +class CompositeType: public Type +{ +protected: + CompositeType() = default; + +public: + /// @returns a list containing the type itself, elements of its decomposition, + /// elements of decomposition of these elements and so on, up to non-composite types. + /// Each type is included only once. + std::vector fullDecomposition() const; + +protected: + /// @returns a list of types that together make up the data part of this type. + /// Contains all types that will have to be implicitly stored, whenever an object of this type is stored. + /// In particular, it returns the base type for arrays and array slices, the member types for structs, + /// the component types for tuples and the value type for mappings + /// (note that the key type of a mapping is *not* part of the list). + virtual std::vector decomposition() const = 0; +}; + /** * Base class used by types which are not value types and can be stored either in storage, memory * or calldata. This is currently used by arrays and structs. */ -class ReferenceType: public Type +class ReferenceType: public CompositeType { protected: explicit ReferenceType(DataLocation _location): m_location(_location) {} @@ -831,6 +861,8 @@ class ArrayType: public ReferenceType protected: std::vector> makeStackItems() const override; + std::vector decomposition() const override { return {m_baseType}; } + private: /// String is interpreted as a subtype of Bytes. enum class ArrayKind { Ordinary, Bytes, String }; @@ -871,6 +903,8 @@ class ArraySliceType: public ReferenceType protected: std::vector> makeStackItems() const override; + std::vector decomposition() const override { return {m_arrayType.baseType()}; } + private: ArrayType const& m_arrayType; }; @@ -996,6 +1030,8 @@ class StructType: public ReferenceType protected: std::vector> makeStackItems() const override; + std::vector decomposition() const override; + private: StructDefinition const& m_struct; // Caches for interfaceType(bool) @@ -1046,7 +1082,7 @@ class EnumType: public Type * Type that can hold a finite sequence of values of different types. * In some cases, the components are empty pointers (when used as placeholders). */ -class TupleType: public Type +class TupleType: public CompositeType { public: explicit TupleType(std::vector _types = {}): m_components(std::move(_types)) {} @@ -1069,6 +1105,16 @@ class TupleType: public Type protected: std::vector> makeStackItems() const override; + std::vector decomposition() const override + { + // Currently calling TupleType::decomposition() is not expected, because we cannot declare a variable of a tuple type. + // If that changes, before removing the solAssert, make sure the function does the right thing and is used properly. + // Note that different tuple members can have different data locations, so using decomposition() to check + // the tuple validity for a data location might require special care. + solUnimplemented("Tuple decomposition is not expected."); + return m_components; + } + private: std::vector const m_components; }; @@ -1111,11 +1157,6 @@ class FunctionType: public Type Freeze,//< CALL to freeze balance Unfreeze,//< CALL to unfreeze balance FreezeExpireTime,// < CALL to freeze expire time - Log0, - Log1, - Log2, - Log3, - Log4, Event, ///< syntactic sugar for LOG* SetGas, ///< modify the default gas value for the function call SetValue, ///< modify the default value transfer for the function call @@ -1219,6 +1260,7 @@ class FunctionType: public Type static FunctionTypePointer newExpressionType(ContractDefinition const& _contract); TypePointers parameterTypes() const; + TypePointers const& parameterTypesIncludingSelf() const; std::vector parameterNames() const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } /// @returns the list of return parameter types. All dynamically-sized types (this excludes @@ -1356,8 +1398,10 @@ class FunctionType: public Type bool const m_arbitraryParameters = false; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack - bool const m_tokenSet = false; - bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn) + bool const m_tokenSet = false; + /// true iff the function is called as arg1.fun(arg2, ..., argn). + /// This is achieved through the "using for" directive. + bool const m_bound = false; Declaration const* m_declaration = nullptr; bool m_saltSet = false; ///< true iff the salt value to be used is on the stack }; @@ -1366,7 +1410,7 @@ class FunctionType: public Type * The type of a mapping, there is one distinct type per key/value type pair. * Mappings always occupy their own storage slot, but do not actually use it. */ -class MappingType: public Type +class MappingType: public CompositeType { public: MappingType(Type const* _keyType, Type const* _valueType): @@ -1390,6 +1434,9 @@ class MappingType: public Type Type const* keyType() const { return m_keyType; } Type const* valueType() const { return m_valueType; } +protected: + std::vector decomposition() const override { return {m_valueType}; } + private: TypePointer m_keyType; TypePointer m_valueType; diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index a5bf9a21100d..5a1fbc185536 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -42,6 +42,7 @@ string ABIFunctions::tupleEncoder( bool _reversed ) { + solAssert(_givenTypes.size() == _targetTypes.size(), ""); EncodingOptions options; options.encodeAsLibraryTypes = _encodeAsLibraryTypes; options.encodeFunctionFromStack = true; @@ -218,23 +219,18 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) valueReturnParams.emplace_back("value" + to_string(stackPos)); stackPos++; } - bool dynamic = decodingTypes[i]->isDynamicallyEncoded(); - Whiskers elementTempl( - dynamic ? - R"( + Whiskers elementTempl(R"( { - let offset := (add(headStart, )) - if gt(offset, 0xffffffffffffffff) { } + + let offset := (add(headStart, )) + if gt(offset, 0xffffffffffffffff) { } + + let offset := + := (add(headStart, offset), dataEnd) } - )" : - R"( - { - let offset := - := (add(headStart, offset), dataEnd) - } - )" - ); + )"); + elementTempl("dynamic", decodingTypes[i]->isDynamicallyEncoded()); // TODO add test elementTempl("revertString", revertReasonIfDebug("ABI decoding: invalid tuple offset")); elementTempl("load", _fromMemory ? "mload" : "calldataload"); @@ -683,18 +679,16 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( // -> function (value, pos) -> ret { let slotValue := sload(value) + let length := (slotValue) + pos := (pos, length) switch and(slotValue, 1) case 0 { // short byte array - let length := and(div(slotValue, 2), 0x7f) - pos := (pos, length) mstore(pos, and(slotValue, not(0xff))) ret := add(pos, ) } case 1 { // long byte array - let length := div(slotValue, 2) - pos := (pos, length) let dataPos := (value) let i := 0 for { } lt(i, length) { i := add(i, 0x20) } { @@ -708,6 +702,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( templ("functionName", functionName); templ("readableTypeNameFrom", _from.toString(true)); templ("readableTypeNameTo", _to.toString(true)); + templ("byteArrayLengthFunction", m_utils.extractByteArrayLengthFunction()); templ("storeLength", arrayStoreLengthForEncodingFunction(_to, _options)); templ("lengthPaddedShort", _options.padded ? "0x20" : "length"); templ("lengthPaddedLong", _options.padded ? "i" : "length"); @@ -802,7 +797,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( items[i]["inRange"] = "1"; else items[i]["inRange"] = "0"; - items[i]["extractFromSlot"] = m_utils.extractFromStorageValue(*_from.baseType(), i * storageBytes, false); + items[i]["extractFromSlot"] = m_utils.extractFromStorageValue(*_from.baseType(), i * storageBytes); } templ("items", items); return templ.render(); @@ -888,7 +883,7 @@ string ABIFunctions::abiEncodingFunctionStruct( members.back()["preprocess"] = "slotValue := sload(add(value, " + toCompactHexWithPrefix(storageSlotOffset) + "))"; previousSlotOffset = storageSlotOffset; } - members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset, false) + "(slotValue)"; + members.back()["retrieveValue"] = m_utils.extractFromStorageValue(*memberTypeFrom, intraSlotOffset) + "(slotValue)"; } else { @@ -1079,8 +1074,6 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo solAssert(!_fromMemory, ""); return abiDecodingFunctionCalldataArray(*arrayType); } - else if (arrayType->isByteArray()) - return abiDecodingFunctionByteArray(*arrayType, _fromMemory); else return abiDecodingFunctionArray(*arrayType, _fromMemory); } @@ -1150,36 +1143,21 @@ string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromM string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); - solAssert(!_type.isByteArray(), ""); string functionName = "abi_decode_" + _type.identifier() + (_fromMemory ? "_fromMemory" : ""); - solAssert(!_type.dataStoredIn(DataLocation::Storage), ""); - return createFunction(functionName, [&]() { string load = _fromMemory ? "mload" : "calldataload"; - bool dynamicBase = _type.baseType()->isDynamicallyEncoded(); Whiskers templ( R"( // function (offset, end) -> array { if iszero(slt(add(offset, 0x1f), end)) { } let length := - array := ((length)) - let dst := array - // might update offset and dst - let src := offset - - for { let i := 0 } lt(i, length) { i := add(i, 1) } - { - let elementPos := - mstore(dst, (elementPos, end)) - dst := add(dst, 0x20) - src := add(src, ) - } + array := (, length, end) } )" ); @@ -1187,18 +1165,56 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset")); templ("functionName", functionName); templ("readableTypeName", _type.toString(true)); - templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)"); + templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length())); + templ("offset", _type.isDynamicallySized() ? "add(offset, 0x20)" : "offset"); + templ("abiDecodeAvailableLen", abiDecodingFunctionArrayAvailableLength(_type, _fromMemory)); + return templ.render(); + }); +} + +string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory) +{ + solAssert(_type.dataStoredIn(DataLocation::Memory), ""); + if (_type.isByteArray()) + return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory); + + string functionName = + "abi_decode_available_length_" + + _type.identifier() + + (_fromMemory ? "_fromMemory" : ""); + + return createFunction(functionName, [&]() { + Whiskers templ(R"( + // + function (offset, length, end) -> array { + array := ((length)) + let dst := array + + let src := offset + + for { let i := 0 } lt(i, length) { i := add(i, 1) } + { + let elementPos := + mstore(dst, (elementPos, end)) + dst := add(dst, 0x20) + src := add(src, ) + } + } + )"); + templ("functionName", functionName); + templ("readableTypeName", _type.toString(true)); templ("allocate", m_utils.allocationFunction()); templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type)); string calldataStride = toCompactHexWithPrefix(_type.calldataStride()); templ("stride", calldataStride); if (_type.isDynamicallySized()) - templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)"); + templ("storeLength", "mstore(array, length) dst := add(array, 0x20)"); else templ("storeLength", ""); - if (dynamicBase) + if (_type.baseType()->isDynamicallyEncoded()) { templ("staticBoundsCheck", ""); + string load = _fromMemory ? "mload" : "calldataload"; templ("retrieveElementPos", "add(offset, " + load + "(src))"); } else @@ -1262,36 +1278,28 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) }); } -string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory) +string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); solAssert(_type.isByteArray(), ""); string functionName = - "abi_decode_" + + "abi_decode_available_length_" + _type.identifier() + (_fromMemory ? "_fromMemory" : ""); return createFunction(functionName, [&]() { - Whiskers templ( - R"( - function (offset, end) -> array { - if iszero(slt(add(offset, 0x1f), end)) { } - let length := (offset) - array := ((length)) - mstore(array, length) - let src := add(offset, 0x20) - let dst := add(array, 0x20) - if gt(add(src, length), end) { } - (src, dst, length) - } - )" - ); - // TODO add test - templ("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid byte array offset")); + Whiskers templ(R"( + function (src, length, end) -> array { + array := ((length)) + mstore(array, length) + let dst := add(array, 0x20) + if gt(add(src, length), end) { } + (src, dst, length) + } + )"); templ("revertStringLength", revertReasonIfDebug("ABI decoding: invalid byte array length")); templ("functionName", functionName); - templ("load", _fromMemory ? "mload" : "calldataload"); templ("allocate", m_utils.allocationFunction()); templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type)); templ("copyToMemFun", m_utils.copyToMemoryFunction(!_fromMemory)); @@ -1360,19 +1368,16 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr solAssert(!member.type->containsNestedMapping(), ""); auto decodingType = member.type->decodingType(); solAssert(decodingType, ""); - bool dynamic = decodingType->isDynamicallyEncoded(); - Whiskers memberTempl( - dynamic ? - R"( + Whiskers memberTempl(R"( + let offset := (add(headStart, )) if gt(offset, 0xffffffffffffffff) { } - mstore(add(value, ), (add(headStart, offset), end)) - )" : - R"( + let offset := - mstore(add(value, ), (add(headStart, offset), end)) - )" - ); + + mstore(add(value, ), (add(headStart, offset), end)) + )"); + memberTempl("dynamic", decodingType->isDynamicallyEncoded()); // TODO add test memberTempl("revertString", revertReasonIfDebug("ABI decoding: invalid struct offset")); memberTempl("load", _fromMemory ? "mload" : "calldataload"); diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e3a7e6f187d7..b90494168796 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -126,7 +126,6 @@ class ABIFunctions /// stack slot, it takes exactly that number of values. std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false); -private: struct EncodingOptions { /// Pad/signextend value types and bytes/string to multiples of 32 bytes. @@ -146,6 +145,7 @@ class ABIFunctions std::string toFunctionNameSuffix() const; }; + /// Internal encoding function that is also used by some copying routines. /// @returns the name of the ABI encoding function with the given type /// and queues the generation of the function to the requested functions. /// @param _fromStack if false, the input value was just loaded from storage @@ -155,6 +155,7 @@ class ABIFunctions Type const& _targetType, EncodingOptions const& _options ); + /// Internal encoding function that is also used by some copying routines. /// @returns the name of a function that internally calls `abiEncodingFunction` /// but always returns the updated encoding position, even if the type is /// statically encoded. @@ -163,6 +164,18 @@ class ABIFunctions Type const& _targetType, EncodingOptions const& _options ); + + /// Decodes array in case of dynamic arrays with offset pointing to + /// data and length already on stack + /// signature: (dataOffset, length, dataEnd) -> decodedArray + std::string abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory); + + /// Internal decoding function that is also used by some copying routines. + /// @returns the name of a function that decodes structs. + /// signature: (dataStart, dataEnd) -> decodedStruct + std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory); + +private: /// Part of @a abiEncodingFunction for array target type and given calldata array. /// Uses calldatacopy and does not perform cleanup or validation and can therefore only /// be used for byte arrays and arrays with the base type uint256 or bytes32. @@ -231,15 +244,12 @@ class ABIFunctions 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 abiDecodingFunction for byte array types. - std::string abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory); + /// Part of @a abiDecodingFunctionArrayWithAvailableLength + std::string abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory); /// Part of @a abiDecodingFunction for calldata struct types. std::string abiDecodingFunctionCalldataStruct(StructType const& _type); - /// Part of @a abiDecodingFunction for struct types. - std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory); /// Part of @a abiDecodingFunction for array types. std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack); - /// @returns the name of a function that retrieves an element from calldata. std::string calldataAccessFunction(Type const& _type); diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index ef94b6bf241f..c67dd6ffbd91 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -29,6 +29,9 @@ #include #include +#include +#include + #include #include @@ -125,6 +128,15 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // special case for short byte arrays: Store them together with their length. if (_targetType.isByteArray()) { + // stack: target_ref target_data_end source_length target_data_pos source_ref + _context << Instruction::DUP3; + evmasm::AssemblyItem nonEmptyByteArray = _context.appendConditionalJump(); + // Empty source, just zero out the main slot. + _context << u256(0) << Instruction::DUP6 << Instruction::SSTORE; + _context.appendJumpTo(copyLoopEndWithoutByteOffset); + + _context << nonEmptyByteArray; + // Non-empty source. // stack: target_ref target_data_end source_length target_data_pos source_ref _context << Instruction::DUP3 << u256(31) << Instruction::LT; evmasm::AssemblyItem longByteArray = _context.appendConditionalJump(); @@ -290,7 +302,10 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; // stack: target_ref target_data_end target_data_pos_updated - utils.clearStorageLoop(targetBaseType); + if (targetBaseType->storageBytes() < 32) + utils.clearStorageLoop(TypeProvider::uint256()); + else + utils.clearStorageLoop(targetBaseType); _context << Instruction::POP; } ); @@ -803,30 +818,25 @@ void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const // lowest-order byte (we actually use a mask with fewer bits) must // be (31*2+0) = 62 + m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1; + m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); m_context.appendInlineAssembly(R"({ - let data := sload(ref) - let shifted_length := and(data, 63) // We have to copy if length is exactly 31, because that marks // the transition between in-place and out-of-place storage. - switch shifted_length - case 62 + switch length + case 31 { mstore(0, ref) let data_area := keccak256(0, 0x20) sstore(data_area, and(data, not(0xff))) - // New length is 32, encoded as (32 * 2 + 1) - sstore(ref, 65) - // Replace ref variable by new length - ref := 32 - } - default - { - sstore(ref, add(data, 2)) - // Replace ref variable by new length - if iszero(and(data, 1)) { data := shifted_length } - ref := add(div(data, 2), 1) + // Set old length in new format (31 * 2 + 1) + data := 63 } - })", {"ref"}); + sstore(ref, add(data, 2)) + // return new length in ref + ref := add(length, 1) + })", {"ref", "data", "length"}); + m_context << Instruction::POP << Instruction::POP; } else m_context.appendInlineAssembly(R"({ @@ -845,24 +855,25 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const if (_type.isByteArray()) { - m_context.appendInlineAssembly(R"({ - let slot_value := sload(ref) - switch and(slot_value, 1) + m_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::DUP1; + m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); + util::Whiskers code(R"({ + if iszero(length) { + mstore(0, ) + mstore(4, ) + revert(0, 0x24) + } + switch gt(length, 31) case 0 { // short byte array - let length := and(div(slot_value, 2), 0x1f) - if iszero(length) { invalid() } - // Zero-out the suffix including the least significant byte. let mask := sub(exp(0x100, sub(33, length)), 1) length := sub(length, 1) slot_value := or(and(not(mask), slot_value), mul(length, 2)) - sstore(ref, slot_value) } case 1 { // long byte array mstore(0, ref) - let length := div(slot_value, 2) let slot := keccak256(0, 0x20) switch length case 32 @@ -870,7 +881,7 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const let data := sload(slot) sstore(slot, 0) data := and(data, not(0xff)) - sstore(ref, or(data, 62)) + slot_value := or(data, 62) } default { @@ -886,11 +897,14 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const // Reduce the length by 1 slot_value := sub(slot_value, 2) - sstore(ref, slot_value) } } - })", {"ref"}); - m_context << Instruction::POP; + sstore(ref, slot_value) + })"); + code("panicSelector", util::selectorFromSignature("Panic(uint256)").str()); + code("emptyArrayPop", to_string(unsigned(util::PanicCode::EmptyArrayPop))); + m_context.appendInlineAssembly(code.render(), {"ref", "slot_value", "length"}); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; } else { @@ -900,7 +914,7 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const m_context << Instruction::DUP1; // stack: ArrayReference oldLength oldLength m_context << Instruction::ISZERO; - m_context.appendConditionalInvalid(); + m_context.appendConditionalPanic(util::PanicCode::EmptyArrayPop); // Stack: ArrayReference oldLength m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB; @@ -922,6 +936,7 @@ void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const void ArrayUtils::clearStorageLoop(TypePointer _type) const { + solAssert(_type->storageBytes() >= 32, ""); m_context.callLowLevelFunction( "$clearStorageLoop_" + _type->identifier(), 2, @@ -1017,16 +1032,7 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept case DataLocation::Storage: m_context << Instruction::SLOAD; if (_arrayType.isByteArray()) - { - // Retrieve length both for in-place strings and off-place strings: - // Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2 - // i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it - // computes (x & (-1)) / 2, which is equivalent to just x / 2. - m_context << u256(1) << Instruction::DUP2 << u256(1) << Instruction::AND; - m_context << Instruction::ISZERO << u256(0x100) << Instruction::MUL; - m_context << Instruction::SUB << Instruction::AND; - m_context << u256(2) << Instruction::SWAP1 << Instruction::DIV; - } + m_context.callYulFunction(m_context.utilFunctions().extractByteArrayLengthFunction(), 1, 1); break; } } @@ -1045,7 +1051,7 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck, b // check out-of-bounds access m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; // out-of-bounds access throws exception - m_context.appendConditionalInvalid(); + m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds); } if (location == DataLocation::CallData && _arrayType.isDynamicallySized()) // remove length if present @@ -1149,7 +1155,7 @@ void ArrayUtils::accessCallDataArrayElement(ArrayType const& _arrayType, bool _d if ( !_arrayType.isByteArray() && _arrayType.baseType()->storageBytes() < 32 && - m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) + m_context.useABICoderV2() ) { m_context << u256(32); diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 7cc01dd26c62..5ddc63a27ee7 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -72,7 +72,7 @@ class ArrayUtils /// Stack pre: reference (excludes byte offset) /// Stack post: new_length void incrementDynamicArraySize(ArrayType const& _type) const; - /// Decrements the size of a dynamic array by one if length is nonzero. Causes an invalid instruction otherwise. + /// Decrements the size of a dynamic array by one if length is nonzero. Causes a Panic otherwise. /// Clears the removed data element. In case of a byte array, this might move the data. /// Stack pre: reference /// Stack post: diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index a64a3400986c..0b6385943fef 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -51,8 +51,8 @@ void Compiler::compileContract( m_context.optimise(m_optimiserSettings); - solAssert(m_context.requestedYulFunctionsRan(), "requestedYulFunctions() was not called."); - solAssert(m_runtimeContext.requestedYulFunctionsRan(), "requestedYulFunctions() was not called."); + solAssert(m_context.appendYulUtilityFunctionsRan(), "appendYulUtilityFunctions() was not called."); + solAssert(m_runtimeContext.appendYulUtilityFunctionsRan(), "appendYulUtilityFunctions() was not called."); } std::shared_ptr Compiler::runtimeAssemblyPtr() const diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 11d5c2abd9d8..6e6fd968e253 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -51,28 +51,15 @@ class Compiler ); /// @returns Entire assembly. evmasm::Assembly const& assembly() const { return m_context.assembly(); } + /// @returns Runtime assembly. + evmasm::Assembly const& runtimeAssembly() const { return m_context.assembly().sub(m_runtimeSub); } /// @returns Entire assembly as a shared pointer to non-const. std::shared_ptr assemblyPtr() const { return m_context.assemblyPtr(); } - /// @returns Runtime assembly. + /// @returns Runtime assembly as a shared pointer. std::shared_ptr runtimeAssemblyPtr() const; - /// @returns The entire assembled object (with constructor). - evmasm::LinkerObject assembledObject() const { return m_context.assembledObject(); } - /// @returns Only the runtime object (without constructor). - evmasm::LinkerObject runtimeObject() const { return m_context.assembledRuntimeObject(m_runtimeSub); } - /// @arg _sourceCodes is the map of input files to source code strings - std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const - { - return m_context.assemblyString(_sourceCodes); - } - /// @arg _sourceCodes is the map of input files to source code strings - Json::Value assemblyJSON(std::map const& _indices = std::map()) const - { - return m_context.assemblyJSON(_indices); - } - /// @returns Assembly items of the normal compiler context - evmasm::AssemblyItems const& assemblyItems() const { return m_context.assembly().items(); } - /// @returns Assembly items of the runtime compiler context - evmasm::AssemblyItems const& runtimeAssemblyItems() const { return m_context.assembly().sub(m_runtimeSub).items(); } + + std::string generatedYulUtilityCode() const { return m_context.generatedYulUtilityCode(); } + std::string runtimeGeneratedYulUtilityCode() const { return m_runtimeContext.generatedYulUtilityCode(); } /// @returns the entry label of the given function. Might return an AssemblyItem of type /// UndefinedItem if it does not exist yet. diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 9fde94e8a40a..1b65e31c7485 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -29,16 +29,20 @@ #include #include +#include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include #include @@ -51,9 +55,6 @@ // Change to "define" to output all intermediate code #undef SOL_OUTPUT_ASM -#ifdef SOL_OUTPUT_ASM -#include -#endif using namespace std; @@ -191,14 +192,24 @@ void CompilerContext::appendMissingLowLevelFunctions() } } -pair> CompilerContext::requestedYulFunctions() +void CompilerContext::appendYulUtilityFunctions(OptimiserSettings const& _optimiserSettings) { - solAssert(!m_requestedYulFunctionsRan, "requestedYulFunctions called more than once."); - m_requestedYulFunctionsRan = true; + solAssert(!m_appendYulUtilityFunctionsRan, "requestedYulFunctions called more than once."); + m_appendYulUtilityFunctionsRan = true; - set empty; - swap(empty, m_externallyUsedYulFunctions); - return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)}; + string code = m_yulFunctionCollector.requestedFunctions(); + if (!code.empty()) + { + appendInlineAssembly( + yul::reindent("{\n" + move(code) + "\n}"), + {}, + m_externallyUsedYulFunctions, + true, + _optimiserSettings, + yulUtilityFileName() + ); + solAssert(!m_generatedYulUtilityCode.empty(), ""); + } } void CompilerContext::addVariable( @@ -238,7 +249,7 @@ void CompilerContext::removeVariablesAboveStackHeight(unsigned _stackHeight) unsigned CompilerContext::numberOfLocalVariables() const { - return m_localVariables.size(); + return static_cast(m_localVariables.size()); } shared_ptr CompilerContext::compiledContract(ContractDefinition const& _contract) const @@ -321,16 +332,24 @@ CompilerContext& CompilerContext::appendJump(evmasm::AssemblyItem::JumpType _jum return *this << item; } -CompilerContext& CompilerContext::appendInvalid() +CompilerContext& CompilerContext::appendPanic(util::PanicCode _code) { - return *this << Instruction::INVALID; + Whiskers templ(R"({ + mstore(0, ) + mstore(4, ) + revert(0, 0x24) + })"); + templ("selector", util::selectorFromSignature("Panic(uint256)").str()); + templ("code", u256(_code).str()); + appendInlineAssembly(templ.render()); + return *this; } -CompilerContext& CompilerContext::appendConditionalInvalid() +CompilerContext& CompilerContext::appendConditionalPanic(util::PanicCode _code) { *this << Instruction::ISZERO; evmasm::AssemblyItem afterTag = appendConditionalJump(); - *this << Instruction::INVALID; + appendPanic(_code); *this << afterTag; return *this; } @@ -369,7 +388,8 @@ void CompilerContext::appendInlineAssembly( vector const& _localVariables, set const& _externallyUsedFunctions, bool _system, - OptimiserSettings const& _optimiserSettings + OptimiserSettings const& _optimiserSettings, + string _sourceName ) { unsigned startStackHeight = stackHeight(); @@ -410,17 +430,17 @@ void CompilerContext::appendInlineAssembly( util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); if (_context == yul::IdentifierContext::RValue) - _assembly.appendInstruction(dupInstruction(stackDiff)); + _assembly.appendInstruction(dupInstruction(static_cast(stackDiff))); else { - _assembly.appendInstruction(swapInstruction(stackDiff)); + _assembly.appendInstruction(swapInstruction(static_cast(stackDiff))); _assembly.appendInstruction(Instruction::POP); } }; ErrorList errors; ErrorReporter errorReporter(errors); - auto scanner = make_shared(langutil::CharStream(_assembly, "--CODEGEN--")); + auto scanner = make_shared(langutil::CharStream(_assembly, _sourceName)); yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); optional locationOverride; if (!_system) @@ -469,6 +489,17 @@ void CompilerContext::appendInlineAssembly( optimizeYul(obj, dialect, _optimiserSettings, externallyUsedIdentifiers); + if (_system) + { + // Store as generated sources, but first re-parse to update the source references. + solAssert(m_generatedYulUtilityCode.empty(), ""); + m_generatedYulUtilityCode = yul::AsmPrinter(dialect)(*obj.code); + string code = yul::AsmPrinter{dialect}(*obj.code); + scanner = make_shared(langutil::CharStream(m_generatedYulUtilityCode, _sourceName)); + obj.code = yul::Parser(errorReporter, dialect).parse(scanner, false); + *obj.analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(dialect, obj); + } + analysisInfo = std::move(*obj.analysisInfo); parserResult = std::move(obj.code); @@ -477,6 +508,12 @@ void CompilerContext::appendInlineAssembly( cout << yul::AsmPrinter(&dialect)(*parserResult) << endl; #endif } + else if (_system) + { + // Store as generated source. + solAssert(m_generatedYulUtilityCode.empty(), ""); + m_generatedYulUtilityCode = _assembly; + } if (!errorReporter.errors().empty()) reportError("Failed to analyze inline assembly block."); @@ -520,13 +557,6 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _ #endif } -LinkerObject const& CompilerContext::assembledObject() const -{ - LinkerObject const& object = m_asm->assemble(); - solAssert(object.immutableReferences.empty(), "Leftover immutables."); - return object; -} - string CompilerContext::revertReasonIfDebug(string const& _message) { return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index f82c705d8847..1ec62027123d 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -76,10 +77,8 @@ class CompilerContext langutil::EVMVersion const& evmVersion() const { return m_evmVersion; } - /// Update currently enabled set of experimental features. - void setExperimentalFeatures(std::set const& _features) { m_experimentalFeatures = _features; } - /// @returns true if the given feature is enabled. - bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } + void setUseABICoderV2(bool _value) { m_useABICoderV2 = _value; } + bool useABICoderV2() const { return m_useABICoderV2; } void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); void addImmutable(VariableDeclaration const& _declaration); @@ -122,6 +121,9 @@ class CompilerContext void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; } ContractDefinition const& mostDerivedContract() const; + void setArithmetic(Arithmetic _value) { m_arithmetic = _value; } + Arithmetic arithmetic() const { return m_arithmetic; } + /// @returns the next function in the queue of functions that are still to be compiled /// (i.e. that were referenced during compilation but where we did not yet generate code for). /// Returns nullptr if the queue is empty. Does not remove the function from the queue, @@ -163,12 +165,14 @@ class CompilerContext void appendMissingLowLevelFunctions(); ABIFunctions& abiFunctions() { return m_abiFunctions; } YulUtilFunctions& utilFunctions() { return m_yulUtilFunctions; } - /// @returns concatenation of all generated functions and a set of the - /// externally used functions. - /// Clears the internal list, i.e. calling it again will result in an - /// empty return value. - std::pair> requestedYulFunctions(); - bool requestedYulFunctionsRan() const { return m_requestedYulFunctionsRan; } + + /// Appends concatenation of all generated Yul functions to the bytecode + /// and stores the Yul source code to be returned by @a generatedYulUtilityCode. + /// Should be called exactly once on each context. + void appendYulUtilityFunctions(OptimiserSettings const& _optimiserSettings); + bool appendYulUtilityFunctionsRan() const { return m_appendYulUtilityFunctionsRan; } + std::string const& generatedYulUtilityCode() const { return m_generatedYulUtilityCode; } + static std::string yulUtilityFileName() { return "#utility.yul"; } /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; @@ -189,10 +193,10 @@ class CompilerContext evmasm::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); } /// Appends a JUMP to a tag already on the stack CompilerContext& appendJump(evmasm::AssemblyItem::JumpType _jumpType = evmasm::AssemblyItem::JumpType::Ordinary); - /// Appends an INVALID instruction - CompilerContext& appendInvalid(); - /// Appends a conditional INVALID instruction - CompilerContext& appendConditionalInvalid(); + /// Appends code to revert with a Panic(uint256) error. + CompilerContext& appendPanic(util::PanicCode _code); + /// Appends code to revert with a Panic(uint256) error if the topmost stack element is nonzero. + CompilerContext& appendConditionalPanic(util::PanicCode _code); /// Appends a REVERT(0, 0) call /// @param _message is an optional revert message used in debug mode CompilerContext& appendRevert(std::string const& _message = ""); @@ -246,17 +250,21 @@ class CompilerContext CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; } CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; } - /// Appends inline assembly (strict mode). - /// @a _replacements are string-matching replacements that are performed prior to parsing the inline assembly. + /// Appends inline assembly (strict-EVM dialect for the current version). + /// @param _assembly the assembly text, should be a block. /// @param _localVariables assigns stack positions to variables with the last one being the stack top /// @param _externallyUsedFunctions a set of function names that are not to be renamed or removed. - /// @param _system if true, this is a "system-level" assembly where all functions use named labels. + /// @param _system if true, this is a "system-level" assembly where all functions use named labels + /// and the code is marked to be exported as "compiler-generated assembly utility file". + /// @param _optimiserSettings settings for the Yul optimiser, which is run in this function already. + /// @param _sourceName the name of the assembly file to be used for source locations void appendInlineAssembly( std::string const& _assembly, std::vector const& _localVariables = std::vector(), std::set const& _externallyUsedFunctions = std::set(), bool _system = false, - OptimiserSettings const& _optimiserSettings = OptimiserSettings::none() + OptimiserSettings const& _optimiserSettings = OptimiserSettings::none(), + std::string _sourceName = "--CODEGEN--" ); /// If m_revertStrings is debug, @returns inline assembly code that @@ -283,21 +291,6 @@ class CompilerContext /// Should be avoided except when adding sub-assemblies. std::shared_ptr assemblyPtr() const { return m_asm; } - /// @arg _sourceCodes is the map of input files to source code strings - std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const - { - return m_asm->assemblyString(_sourceCodes); - } - - /// @arg _sourceCodes is the map of input files to source code strings - Json::Value assemblyJSON(std::map const& _indicies = std::map()) const - { - return m_asm->assemblyJSON(_indicies); - } - - evmasm::LinkerObject const& assembledObject() const; - evmasm::LinkerObject const& assembledRuntimeObject(size_t _subIndex) const { return m_asm->sub(_subIndex).assemble(); } - /** * Helper class to pop the visited nodes stack when a scope closes */ @@ -354,8 +347,7 @@ class CompilerContext /// Version of the EVM to compile against. langutil::EVMVersion m_evmVersion; RevertStrings const m_revertStrings; - /// Activated experimental features. - std::set m_experimentalFeatures; + bool m_useABICoderV2 = false; /// Other already compiled contracts to be used in contract creation calls. std::map> m_otherCompilers; /// Storage offsets of state variables @@ -373,6 +365,8 @@ class CompilerContext std::map> m_localVariables; /// The contract currently being compiled. Virtual function lookup starts from this contarct. ContractDefinition const* m_mostDerivedContract = nullptr; + /// Whether to use checked arithmetic. + Arithmetic m_arithmetic = Arithmetic::Checked; /// Stack of current visited AST nodes, used for location attachment std::stack m_visitedNodes; /// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime. @@ -385,14 +379,17 @@ class CompilerContext MultiUseYulFunctionCollector m_yulFunctionCollector; /// Set of externally used yul functions. std::set m_externallyUsedYulFunctions; + /// Generated Yul code used as utility. Source references from the bytecode can point here. + /// Produced from @a m_yulFunctionCollector. + std::string m_generatedYulUtilityCode; /// Container for ABI functions to be generated. ABIFunctions m_abiFunctions; /// Container for Yul Util functions to be generated. YulUtilFunctions m_yulUtilFunctions; /// The queue of low-level functions to generate. std::queue>> m_lowLevelFunctionGenerationQueue; - /// Flag to check that requestedYulFunctions() was called exactly once - bool m_requestedYulFunctionsRan = false; + /// Flag to check that appendYulUtilityFunctions() was called exactly once + bool m_appendYulUtilityFunctionsRan = false; }; } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index dfc7cd233678..ad461d2da651 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -93,7 +94,7 @@ void CompilerUtils::revertWithStringData(Type const& _argumentType) { solAssert(_argumentType.isImplicitlyConvertibleTo(*TypeProvider::fromElementaryTypeName("string memory")), ""); fetchFreeMemoryPointer(); - m_context << (u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)); + m_context << util::selectorFromSignature("Error(string)"); m_context << Instruction::DUP2 << Instruction::MSTORE; m_context << u256(4) << Instruction::ADD; // Stack: @@ -229,7 +230,7 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) { /// Stack: - if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + if (m_context.useABICoderV2()) { // Use the new Yul-based decoding function auto stackHeightBefore = m_context.stackHeight(); @@ -411,7 +412,7 @@ void CompilerUtils::encodeToMemory( ) { // stack: ... - bool const encoderV2 = m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2); + bool const encoderV2 = m_context.useABICoderV2(); TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; solAssert(targetTypes.size() == _givenTypes.size(), ""); for (TypePointer& t: targetTypes) @@ -530,10 +531,15 @@ void CompilerUtils::encodeToMemory( if (_givenTypes[i]->category() == Type::Category::StringLiteral) { auto const& strType = dynamic_cast(*_givenTypes[i]); - m_context << u256(strType.value().size()); + auto const size = strType.value().size(); + m_context << u256(size); storeInMemoryDynamic(*TypeProvider::uint256(), true); // stack: ... - storeInMemoryDynamic(strType, _padToWordBoundaries); + + // Do not output empty padding for zero-length strings. + // TODO: handle this in storeInMemoryDynamic + if (size != 0) + storeInMemoryDynamic(strType, _padToWordBoundaries); } else { @@ -809,7 +815,7 @@ void CompilerUtils::convertType( if (_asPartOfArgumentDecoding) m_context.appendConditionalRevert(false, "Enum out of range"); else - m_context.appendConditionalInvalid(); + m_context.appendConditionalPanic(util::PanicCode::EnumConversionError); enumOverflowCheckPending = false; } break; @@ -848,7 +854,7 @@ void CompilerUtils::convertType( EnumType const& enumType = dynamic_cast(_targetType); solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; - m_context.appendConditionalInvalid(); + m_context.appendConditionalPanic(util::PanicCode::EnumConversionError); enumOverflowCheckPending = false; } else if (targetTypeCategory == Type::Category::FixedPoint) @@ -916,13 +922,13 @@ void CompilerUtils::convertType( { unsigned const numBytes = dynamic_cast(_targetType).numBytes(); solAssert(data.size() <= 32, ""); - m_context << (h256::Arith(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes)))); + m_context << (u256(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes)))); } else if (targetTypeCategory == Type::Category::Array) { auto const& arrayType = dynamic_cast(_targetType); solAssert(arrayType.isByteArray(), ""); - unsigned storageSize = 32 + ((data.size() + 31) / 32) * 32; + size_t storageSize = 32 + ((data.size() + 31) / 32) * 32; allocateMemory(storageSize); // stack: mempos m_context << Instruction::DUP1 << u256(data.size()); @@ -940,8 +946,8 @@ void CompilerUtils::convertType( case Type::Category::Array: { solAssert(targetTypeCategory == stackTypeCategory, ""); - ArrayType const& typeOnStack = dynamic_cast(_typeOnStack); - ArrayType const& targetType = dynamic_cast(_targetType); + auto const& typeOnStack = dynamic_cast(_typeOnStack); + auto const& targetType = dynamic_cast(_targetType); switch (targetType.location()) { case DataLocation::Storage: @@ -957,65 +963,77 @@ void CompilerUtils::convertType( // Copy the array to a free position in memory, unless it is already in memory. if (typeOnStack.location() != DataLocation::Memory) { - // stack: (variably sized) - unsigned stackSize = typeOnStack.sizeOnStack(); - ArrayUtils(m_context).retrieveLength(typeOnStack); - - // allocate memory - // stack: (variably sized) - m_context << Instruction::DUP1; - ArrayUtils(m_context).convertLengthToSize(targetType, true); - // stack: (variably sized) - if (targetType.isDynamicallySized()) - m_context << u256(0x20) << Instruction::ADD; - allocateMemory(); - // stack: (variably sized) - m_context << Instruction::DUP1; - moveIntoStack(2 + stackSize); - if (targetType.isDynamicallySized()) - { - m_context << Instruction::DUP2; - storeInMemoryDynamic(*TypeProvider::uint256()); - } - // stack: (variably sized) - if (targetType.baseType()->isValueType()) + if ( + typeOnStack.dataStoredIn(DataLocation::CallData) && + typeOnStack.baseType()->isDynamicallyEncoded() + ) { - copyToStackTop(2 + stackSize, stackSize); - ArrayUtils(m_context).copyArrayToMemory(typeOnStack); + solAssert(m_context.useABICoderV2(), ""); + // stack: offset length(optional in case of dynamically sized array) + solAssert(typeOnStack.sizeOnStack() == (typeOnStack.isDynamicallySized() ? 2 : 1), ""); + if (typeOnStack.isDynamicallySized()) + m_context << Instruction::SWAP1; + + m_context.callYulFunction( + m_context.utilFunctions().conversionFunction(typeOnStack, targetType), + typeOnStack.isDynamicallySized() ? 2 : 1, + 1 + ); } else { - if (auto baseType = dynamic_cast(typeOnStack.baseType())) - solUnimplementedAssert( - typeOnStack.location() != DataLocation::CallData || - !typeOnStack.isDynamicallyEncoded() || - !baseType->isDynamicallySized(), - "Copying nested dynamic calldata arrays to memory is not implemented in the old code generator." - ); + // stack: (variably sized) + unsigned stackSize = typeOnStack.sizeOnStack(); + ArrayUtils(m_context).retrieveLength(typeOnStack); - m_context << u256(0) << Instruction::SWAP1; - // stack: (variably sized) - auto repeat = m_context.newTag(); - m_context << repeat; - m_context << Instruction::DUP3 << Instruction::DUP3; - m_context << Instruction::LT << Instruction::ISZERO; - auto loopEnd = m_context.appendConditionalJump(); - copyToStackTop(3 + stackSize, stackSize); - copyToStackTop(2 + stackSize, 1); - ArrayUtils(m_context).accessIndex(typeOnStack, false); - if (typeOnStack.location() == DataLocation::Storage) - StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true); - convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded); - storeInMemoryDynamic(*targetType.baseType(), true); - m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD; - m_context << Instruction::SWAP1; - m_context.appendJumpTo(repeat); - m_context << loopEnd; - m_context << Instruction::POP; + // allocate memory + // stack: (variably sized) + m_context << Instruction::DUP1; + ArrayUtils(m_context).convertLengthToSize(targetType, true); + // stack: (variably sized) + if (targetType.isDynamicallySized()) + m_context << u256(0x20) << Instruction::ADD; + allocateMemory(); + // stack: (variably sized) + m_context << Instruction::DUP1; + moveIntoStack(2 + stackSize); + if (targetType.isDynamicallySized()) + { + m_context << Instruction::DUP2; + storeInMemoryDynamic(*TypeProvider::uint256()); + } + // stack: (variably sized) + if (targetType.baseType()->isValueType()) + { + copyToStackTop(2 + stackSize, stackSize); + ArrayUtils(m_context).copyArrayToMemory(typeOnStack); + } + else + { + m_context << u256(0) << Instruction::SWAP1; + // stack: (variably sized) + auto repeat = m_context.newTag(); + m_context << repeat; + m_context << Instruction::DUP3 << Instruction::DUP3; + m_context << Instruction::LT << Instruction::ISZERO; + auto loopEnd = m_context.appendConditionalJump(); + copyToStackTop(3 + stackSize, stackSize); + copyToStackTop(2 + stackSize, 1); + ArrayUtils(m_context).accessIndex(typeOnStack, false); + if (typeOnStack.location() == DataLocation::Storage) + StorageItem(m_context, *typeOnStack.baseType()).retrieveValue(SourceLocation(), true); + convertType(*typeOnStack.baseType(), *targetType.baseType(), _cleanupNeeded); + storeInMemoryDynamic(*targetType.baseType(), true); + m_context << Instruction::SWAP1 << u256(1) << Instruction::ADD; + m_context << Instruction::SWAP1; + m_context.appendJumpTo(repeat); + m_context << loopEnd; + m_context << Instruction::POP; + } + // stack: (variably sized) + popStackSlots(2 + stackSize); + // Stack: } - // stack: (variably sized) - popStackSlots(2 + stackSize); - // Stack: } break; } @@ -1032,18 +1050,18 @@ void CompilerUtils::convertType( } case Type::Category::ArraySlice: { + solAssert(_targetType.category() == Type::Category::Array, ""); auto& typeOnStack = dynamic_cast(_typeOnStack); - solUnimplementedAssert( - _targetType.dataStoredIn(DataLocation::CallData), - "Conversion from calldata slices to memory not yet implemented." - ); - solAssert(_targetType == typeOnStack.arrayType(), ""); - solUnimplementedAssert( - typeOnStack.arrayType().location() == DataLocation::CallData && + auto const& targetArrayType = dynamic_cast(_targetType); + solAssert(typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType), ""); + solAssert( + typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) && typeOnStack.arrayType().isDynamicallySized() && !typeOnStack.arrayType().baseType()->isDynamicallyEncoded(), "" ); + if (!_targetType.dataStoredIn(DataLocation::CallData)) + return convertType(typeOnStack.arrayType(), _targetType); break; } case Type::Category::Struct: @@ -1105,11 +1123,22 @@ void CompilerUtils::convertType( } case DataLocation::CallData: { - solUnimplementedAssert(!typeOnStack.isDynamicallyEncoded(), ""); - m_context << Instruction::DUP1; - m_context << Instruction::CALLDATASIZE; - m_context << Instruction::SUB; - abiDecode({&targetType}, false); + if (typeOnStack.isDynamicallyEncoded()) + { + solAssert(m_context.useABICoderV2(), ""); + m_context.callYulFunction( + m_context.utilFunctions().conversionFunction(typeOnStack, targetType), + 1, + 1 + ); + } + else + { + m_context << Instruction::DUP1; + m_context << Instruction::CALLDATASIZE; + m_context << Instruction::SUB; + abiDecode({&targetType}, false); + } break; } case DataLocation::Memory: @@ -1212,13 +1241,13 @@ void CompilerUtils::pushZeroValue(Type const& _type) if (funType->kind() == FunctionType::Kind::Internal) { m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) { - _context.appendInvalid(); + _context.appendPanic(util::PanicCode::InvalidInternalFunction); }); if (CompilerContext* runCon = m_context.runtimeContext()) { leftShiftNumberOnStack(32); m_context << runCon->lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) { - _context.appendInvalid(); + _context.appendPanic(util::PanicCode::InvalidInternalFunction); }).toSubAssemblyTag(m_context.runtimeSub()); m_context << Instruction::OR; } @@ -1421,7 +1450,7 @@ void CompilerUtils::storeStringData(bytesConstRef _data) { for (unsigned i = 0; i < _data.size(); i += 32) { - m_context << h256::Arith(h256(_data.cropped(i), h256::AlignLeft)); + m_context << u256(h256(_data.cropped(i), h256::AlignLeft)); storeInMemoryDynamic(*TypeProvider::uint256()); } m_context << Instruction::POP; diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 64ab84fe78f9..7bc26d971f99 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -104,12 +104,13 @@ class CompilerUtils /// Stores a 256 bit integer from stack in memory. /// @param _offset offset in memory void storeInMemory(unsigned _offset); + /// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack /// and also updates that. For reference types, only copies the data pointer. Fails for - /// non-memory-references. + /// non-memory-references. For string literals no value is available on the stack. /// @param _padToWords if true, adds zeros to pad to multiple of 32 bytes. Array elements + /// are always padded (except for byte arrays), regardless of this parameter. /// @param _cleanup if true, adds code to cleanup the value before storing it. - /// are always padded (except for byte arrays), regardless of this parameter. /// Stack pre: memory_offset value... /// Stack post: (memory_offset+length) void storeInMemoryDynamic(Type const& _type, bool _padToWords = true, bool _cleanup = true); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index d977db57d480..7d718c1969b6 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include #include #include @@ -47,6 +47,7 @@ #include #include +#include #include @@ -126,7 +127,7 @@ void ContractCompiler::initializeContext( map> const& _otherCompilers ) { - m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); + m_context.setUseABICoderV2(*_contract.sourceUnit().annotation().useABICoderV2); m_context.setOtherCompilers(_otherCompilers); m_context.setMostDerivedContract(_contract); if (m_runtimeCompiler) @@ -198,7 +199,7 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont auto const& immutables = contractType.immutableVariables(); // Push all immutable values on the stack. for (auto const& immutable: immutables) - CompilerUtils(m_context).loadFromMemory(m_context.immutableMemoryOffset(*immutable), *immutable->annotation().type); + CompilerUtils(m_context).loadFromMemory(static_cast(m_context.immutableMemoryOffset(*immutable)), *immutable->annotation().type); m_context.pushSubroutineSize(m_context.runtimeSub()); if (immutables.empty()) m_context << Instruction::DUP1; @@ -209,7 +210,10 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont { auto slotNames = m_context.immutableVariableSlotNames(*immutable); for (auto&& slotName: slotNames | boost::adaptors::reversed) + { + m_context << u256(0); m_context.appendImmutableAssignment(slotName); + } } if (!immutables.empty()) m_context.pushSubroutineSize(m_context.runtimeSub()); @@ -232,19 +236,26 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract) m_context.pushSubroutineSize(m_context.runtimeSub()); m_context.pushSubroutineOffset(m_context.runtimeSub()); // This code replaces the address added by appendDeployTimeAddress(). - m_context.appendInlineAssembly(R"( - { - // If code starts at 11, an mstore(0) writes to the full PUSH20 plus data - // without the need for a shift. - let codepos := 11 - codecopy(codepos, subOffset, subSize) - // Check that the first opcode is a PUSH20 - if iszero(eq(0x73, byte(0, mload(codepos)))) { invalid() } - mstore(0, address()) - mstore8(codepos, 0x73) - return(codepos, subSize) - } - )", {"subSize", "subOffset"}); + m_context.appendInlineAssembly( + Whiskers(R"( + { + // If code starts at 11, an mstore(0) writes to the full PUSH20 plus data + // without the need for a shift. + let codepos := 11 + codecopy(codepos, subOffset, subSize) + // Check that the first opcode is a PUSH20 + if iszero(eq(0x73, byte(0, mload(codepos)))) { + mstore(0, ) + mstore(4, 0) + revert(0, 0x24) + } + mstore(0, address()) + mstore8(codepos, 0x73) + return(codepos, subSize) + } + )")("panicSig", util::selectorFromSignature("Panic(uint256)").str()).render(), + {"subSize", "subOffset"} + ); return m_context.runtimeSub(); } @@ -410,7 +421,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have fallback functions"); FunctionDefinition const* etherReceiver = _contract.receiveFunction(); - solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have ether receiver functions"); + solAssert(!_contract.isLibrary() || !etherReceiver, "Libraries can't have ether receiver functions"); bool needToAddCallvalueCheck = true; if (!hasPayableFunctions(_contract) && !interfaceFunctions.empty() && !_contract.isLibrary()) @@ -473,10 +484,21 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac appendCallValueCheck(); solAssert(fallback->isFallback(), ""); - solAssert(FunctionType(*fallback).parameterTypes().empty(), ""); - solAssert(FunctionType(*fallback).returnParameterTypes().empty(), ""); + m_context.setStackOffset(0); + + if (!FunctionType(*fallback).parameterTypes().empty()) + m_context << u256(0) << Instruction::CALLDATASIZE; + fallback->accept(*this); - m_context << Instruction::STOP; + + if (FunctionType(*fallback).returnParameterTypes().empty()) + m_context << Instruction::STOP; + else + { + m_context << Instruction::DUP1 << Instruction::MLOAD << Instruction::SWAP1; + m_context << u256(0x20) << Instruction::ADD; + m_context << Instruction::RETURN; + } } else m_context.appendRevert("Unknown signature and no fallback defined"); @@ -485,6 +507,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac for (auto const& it: interfaceFunctions) { + m_context.setStackOffset(1); FunctionTypePointer const& functionType = it.second; solAssert(functionType->hasDeclaration(), ""); CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration()); @@ -594,7 +617,9 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) // reserve additional slots: [retarg0] ... [retargm] unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters()); - if (!_function.isConstructor()) + if (_function.isFallback()) + m_context.adjustStackOffset(static_cast(parametersSize)); + else if (!_function.isConstructor()) // adding 1 for return address. m_context.adjustStackOffset(static_cast(parametersSize) + 1); for (ASTPointer const& variable: _function.parameters()) @@ -604,7 +629,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) } for (ASTPointer const& variable: _function.returnParameters()) - appendStackVariableInitialisation(*variable); + appendStackVariableInitialisation(*variable, /* _provideDefaultValue = */ true); if (_function.isConstructor()) if (auto c = dynamic_cast(*_function.scope()).nextConstructor( @@ -634,7 +659,8 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters()); vector stackLayout; - stackLayout.push_back(static_cast(c_returnValuesSize)); // target of return address + if (!_function.isConstructor() && !_function.isFallback()) + stackLayout.push_back(static_cast(c_returnValuesSize)); // target of return address stackLayout += vector(c_argumentsSize, -1); // discard all arguments for (size_t i = 0; i < c_returnValuesSize; ++i) stackLayout.push_back(static_cast(i)); @@ -645,7 +671,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) errinfo_sourceLocation(_function.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); - while (stackLayout.back() != static_cast(stackLayout.size() - 1)) + while (!stackLayout.empty() && stackLayout.back() != static_cast(stackLayout.size() - 1)) if (stackLayout.back() < 0) { m_context << Instruction::POP; @@ -653,7 +679,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) } else { - m_context << swapInstruction(stackLayout.size() - static_cast(stackLayout.back()) - 1u); + m_context << swapInstruction(static_cast(stackLayout.size()) - static_cast(stackLayout.back()) - 1u); swap(stackLayout[static_cast(stackLayout.back())], stackLayout.back()); } for (size_t i = 0; i < stackLayout.size(); ++i) @@ -697,26 +723,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { int const depositBefore = _assembly.stackHeight(); solAssert(!!decl->type(), "Type of declaration required but not yet determined."); - if (FunctionDefinition const* functionDef = dynamic_cast(decl)) - { - solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); - functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); - auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag(); - solAssert(functionEntryLabel.data() <= std::numeric_limits::max(), ""); - _assembly.appendLabelReference(static_cast(functionEntryLabel.data())); - // If there is a runtime context, we have to merge both labels into the same - // stack slot in case we store it in storage. - if (CompilerContext* rtc = m_context.runtimeContext()) - { - _assembly.appendConstant(u256(1) << 32); - _assembly.appendInstruction(Instruction::MUL); - auto runtimeEntryLabel = rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()); - solAssert(runtimeEntryLabel.data() <= std::numeric_limits::max(), ""); - _assembly.appendLabelReference(static_cast(runtimeEntryLabel.data())); - _assembly.appendInstruction(Instruction::OR); - } - } - else if (auto variable = dynamic_cast(decl)) + if (auto variable = dynamic_cast(decl)) { solAssert(!variable->immutable(), ""); if (variable->isConstant()) @@ -770,9 +777,9 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else if (m_context.isStateVariable(decl)) { auto const& location = m_context.storageLocationOfVariable(*decl); - if (ref->second.isSlot) + if (ref->second.suffix == "slot") m_context << location.first; - else if (ref->second.isOffset) + else if (ref->second.suffix == "offset") m_context << u256(location.second); else solAssert(false, ""); @@ -780,26 +787,44 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else if (m_context.isLocalVariable(decl)) { unsigned stackDiff = static_cast(_assembly.stackHeight()) - m_context.baseStackOffsetOfVariable(*variable); - if (ref->second.isSlot || ref->second.isOffset) + if (!ref->second.suffix.empty()) { - solAssert(variable->type()->dataStoredIn(DataLocation::Storage), ""); - unsigned size = variable->type()->sizeOnStack(); - if (size == 2) - { - // slot plus offset - if (ref->second.isOffset) - stackDiff--; - } - else + string const& suffix = ref->second.suffix; + if (variable->type()->dataStoredIn(DataLocation::Storage)) { - solAssert(size == 1, ""); - // only slot, offset is zero - if (ref->second.isOffset) + solAssert(suffix == "offset" || suffix == "slot", ""); + unsigned size = variable->type()->sizeOnStack(); + if (size == 2) { - _assembly.appendConstant(u256(0)); - return; + // slot plus offset + if (suffix == "offset") + stackDiff--; + } + else + { + solAssert(size == 1, ""); + // only slot, offset is zero + if (suffix == "offset") + { + _assembly.appendConstant(u256(0)); + return; + } } } + else if (variable->type()->dataStoredIn(DataLocation::CallData)) + { + auto const* arrayType = dynamic_cast(variable->type()); + solAssert( + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData), + "" + ); + solAssert(suffix == "offset" || suffix == "length", ""); + solAssert(variable->type()->sizeOnStack() == 2, ""); + if (suffix == "length") + stackDiff--; + } + else + solAssert(false, ""); } else solAssert(variable->type()->sizeOnStack() == 1, ""); @@ -809,7 +834,6 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_comment("Stack too deep, try removing local variables.") ); - solAssert(variable->type()->sizeOnStack() == 1, ""); _assembly.appendInstruction(dupInstruction(stackDiff)); } else @@ -817,7 +841,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } else if (auto contract = dynamic_cast(decl)) { - solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); + solAssert(ref->second.suffix.empty(), ""); solAssert(contract->isLibrary(), ""); _assembly.appendLinkerSymbol(contract->fullyQualifiedName()); } @@ -828,20 +852,39 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) else { // lvalue context - solAssert(!ref->second.isOffset, ""); auto variable = dynamic_cast(decl); - solAssert( - !!variable && m_context.isLocalVariable(variable), - "Can only assign to stack variables in inline assembly." - ); - solAssert(variable->type()->sizeOnStack() == 1, ""); unsigned stackDiff = static_cast(_assembly.stackHeight()) - m_context.baseStackOffsetOfVariable(*variable) - 1; - if (stackDiff > 16 || stackDiff < 1) - BOOST_THROW_EXCEPTION( - StackTooDeepError() << - errinfo_sourceLocation(_inlineAssembly.location()) << - errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") + string const& suffix = ref->second.suffix; + if (variable->type()->dataStoredIn(DataLocation::Storage)) + { + solAssert( + !!variable && m_context.isLocalVariable(variable), + "Can only assign to stack variables in inline assembly." + ); + solAssert(variable->type()->sizeOnStack() == 1, ""); + solAssert(suffix == "slot", ""); + if (stackDiff > 16 || stackDiff < 1) + BOOST_THROW_EXCEPTION( + StackTooDeepError() << + errinfo_sourceLocation(_inlineAssembly.location()) << + errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.") + ); + } + else if (variable->type()->dataStoredIn(DataLocation::CallData)) + { + auto const* arrayType = dynamic_cast(variable->type()); + solAssert( + arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData), + "" ); + solAssert(suffix == "offset" || suffix == "length", ""); + solAssert(variable->type()->sizeOnStack() == 2, ""); + if (suffix == "length") + stackDiff--; + } + else + solAssert(suffix.empty(), ""); + _assembly.appendInstruction(swapInstruction(stackDiff)); _assembly.appendInstruction(Instruction::POP); } @@ -959,8 +1002,6 @@ void ContractCompiler::handleCatch(vector> const& _ca ); solAssert(m_context.evmVersion().supportsReturndata(), ""); - string errorHash = FixedHash<4>(util::keccak256("Error(string)")).hex(); - // Try to decode the error message. // If this fails, leaves 0 on the stack, otherwise the pointer to the data string. m_context.callYulFunction(m_context.utilFunctions().tryDecodeErrorMessageFunction(), 0, 1); @@ -1206,7 +1247,7 @@ bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclar // and freed in the end of their scope. for (auto decl: _variableDeclarationStatement.declarations()) if (decl) - appendStackVariableInitialisation(*decl); + appendStackVariableInitialisation(*decl, !_variableDeclarationStatement.initialValue()); StackHeightChecker checker(m_context); if (Expression const* expression = _variableDeclarationStatement.initialValue()) @@ -1252,19 +1293,31 @@ bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement) { StackHeightChecker checker(m_context); CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement); + solAssert(m_context.arithmetic() == Arithmetic::Checked, "Placeholder cannot be used inside checked block."); appendModifierOrFunctionCode(); + solAssert(m_context.arithmetic() == Arithmetic::Checked, "Arithmetic not reset to 'checked'."); checker.check(); return true; } bool ContractCompiler::visit(Block const& _block) { + if (_block.unchecked()) + { + solAssert(m_context.arithmetic() == Arithmetic::Checked, ""); + m_context.setArithmetic(Arithmetic::Wrapping); + } storeStackHeight(&_block); return true; } void ContractCompiler::endVisit(Block const& _block) { + if (_block.unchecked()) + { + solAssert(m_context.arithmetic() == Arithmetic::Wrapping, ""); + m_context.setArithmetic(Arithmetic::Checked); + } // Frees local variables declared in the scope of this block. popScopedVariables(&_block); } @@ -1278,15 +1331,7 @@ void ContractCompiler::appendMissingFunctions() solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } m_context.appendMissingLowLevelFunctions(); - auto [yulFunctions, externallyUsedYulFunctions] = m_context.requestedYulFunctions(); - if (!yulFunctions.empty()) - m_context.appendInlineAssembly( - "{" + move(yulFunctions) + "}", - {}, - externallyUsedYulFunctions, - true, - m_optimiserSettings - ); + m_context.appendYulUtilityFunctions(m_optimiserSettings); } void ContractCompiler::appendModifierOrFunctionCode() @@ -1309,13 +1354,20 @@ void ContractCompiler::appendModifierOrFunctionCode() ASTPointer const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth]; // constructor call should be excluded - if (dynamic_cast(modifierInvocation->name()->annotation().referencedDeclaration)) + if (dynamic_cast(modifierInvocation->name().annotation().referencedDeclaration)) appendModifierOrFunctionCode(); else { - ModifierDefinition const& modifier = dynamic_cast( - *modifierInvocation->name()->annotation().referencedDeclaration - ).resolveVirtual(m_context.mostDerivedContract()); + ModifierDefinition const& referencedModifier = dynamic_cast( + *modifierInvocation->name().annotation().referencedDeclaration + ); + VirtualLookup lookup = *modifierInvocation->name().annotation().requiredLookup; + solAssert(lookup == VirtualLookup::Virtual || lookup == VirtualLookup::Static, ""); + ModifierDefinition const& modifier = + lookup == VirtualLookup::Virtual ? + referencedModifier.resolveVirtual(m_context.mostDerivedContract()) : + referencedModifier; + CompilerContext::LocationSetter locationSetter(m_context, modifier); std::vector> const& modifierArguments = modifierInvocation->arguments() ? *modifierInvocation->arguments() : std::vector>(); @@ -1338,9 +1390,16 @@ void ContractCompiler::appendModifierOrFunctionCode() if (codeBlock) { + m_context.setArithmetic(Arithmetic::Checked); + + bool coderV2Outside = m_context.useABICoderV2(); + m_context.setUseABICoderV2(*codeBlock->sourceUnit().annotation().useABICoderV2); + m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight()); codeBlock->accept(*this); + m_context.setUseABICoderV2(coderV2Outside); + solAssert(!m_returnTags.empty(), ""); m_context << m_returnTags.back().first; m_returnTags.pop_back(); @@ -1353,11 +1412,20 @@ void ContractCompiler::appendModifierOrFunctionCode() m_context.setModifierDepth(m_modifierDepth); } -void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration const& _variable) +void ContractCompiler::appendStackVariableInitialisation( + VariableDeclaration const& _variable, + bool _provideDefaultValue +) { CompilerContext::LocationSetter location(m_context, _variable); m_context.addVariable(_variable); - CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type); + if (!_provideDefaultValue && _variable.type()->dataStoredIn(DataLocation::Memory)) + { + solAssert(_variable.type()->sizeOnStack() == 1, ""); + m_context << u256(0); + } + else + CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type); } void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index d4badc1974a2..d3290a4c79e6 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -130,7 +130,10 @@ class ContractCompiler: private ASTConstVisitor /// body itself if the last modifier was reached. void appendModifierOrFunctionCode(); - void appendStackVariableInitialisation(VariableDeclaration const& _variable); + /// Creates a stack slot for the given variable and assigns a default value. + /// If the default value is complex (needs memory allocation) and @a _provideDefaultValue + /// is false, this might be skipped. + void appendStackVariableInitialisation(VariableDeclaration const& _variable, bool _provideDefaultValue); void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); /// Frees the variables of a certain scope (to be used when leaving). @@ -143,6 +146,7 @@ class ContractCompiler: private ASTConstVisitor /// Pointer to the runtime compiler in case this is a creation compiler. ContractCompiler* m_runtimeCompiler = nullptr; CompilerContext& m_context; + /// Tag to jump to for a "break" statement and the stack height after freeing the local loop variables. std::vector> m_breakTags; /// Tag to jump to for a "continue" statement and the stack height after freeing the local loop variables. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 3240b71f5044..11156f384567 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -130,7 +131,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& // stack: // copy key[i] to top. - utils().copyToStackTop(paramTypes.size() - i + 1, 1); + utils().copyToStackTop(static_cast(paramTypes.size() - i + 1), 1); m_context.appendInlineAssembly(R"({ let key_len := mload(key_ptr) @@ -154,7 +155,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& utils().storeInMemory(32); // move key to memory. - utils().copyToStackTop(paramTypes.size() - i, 1); + utils().copyToStackTop(static_cast(paramTypes.size() - i), 1); utils().storeInMemory(0); m_context << u256(64) << u256(0); m_context << Instruction::KECCAK256; @@ -168,8 +169,17 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { // pop offset m_context << Instruction::POP; - utils().copyToStackTop(paramTypes.size() - i + 1, 1); - ArrayUtils(m_context).accessIndex(*arrayType); + utils().copyToStackTop(static_cast(paramTypes.size() - i + 1), 1); + + ArrayUtils(m_context).retrieveLength(*arrayType, 1); + // Stack: ref [length] index length + // check out-of-bounds access + m_context << Instruction::DUP2 << Instruction::LT; + auto tag = m_context.appendConditionalJump(); + m_context << u256(0) << Instruction::DUP1 << Instruction::REVERT; + m_context << tag; + + ArrayUtils(m_context).accessIndex(*arrayType, false); returnType = arrayType->baseType(); } else @@ -180,9 +190,9 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& m_context << Instruction::SWAP2 << Instruction::POP << Instruction::SWAP1; else if (paramTypes.size() >= 2) { - m_context << swapInstruction(paramTypes.size()); + m_context << swapInstruction(static_cast(paramTypes.size())); m_context << Instruction::POP; - m_context << swapInstruction(paramTypes.size()); + m_context << swapInstruction(static_cast(paramTypes.size())); utils().popStackSlots(paramTypes.size() - 1); } unsigned retSizeOnStack = 0; @@ -265,7 +275,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) solAssert(*_assignment.annotation().type == leftType, ""); bool cleanupNeeded = false; if (op != Token::Assign) - cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp); + cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp, m_context.arithmetic()); _assignment.rightHandSide().accept(*this); // Perform some conversion already. This will convert storage types to memory and literals // to their actual type, but will not convert e.g. memory to storage. @@ -371,9 +381,10 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) { CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation); - if (_unaryOperation.annotation().type->category() == Type::Category::RationalNumber) + Type const& type = *_unaryOperation.annotation().type; + if (type.category() == Type::Category::RationalNumber) { - m_context << _unaryOperation.annotation().type->literalValue(nullptr); + m_context << type.literalValue(nullptr); return false; } @@ -396,24 +407,39 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) case Token::Dec: // -- (pre- or postfix) solAssert(!!m_currentLValue, "LValue not retrieved."); solUnimplementedAssert( - _unaryOperation.annotation().type->category() != Type::Category::FixedPoint, + type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType." ); m_currentLValue->retrieveValue(_unaryOperation.location()); if (!_unaryOperation.isPrefixOperation()) { // store value for later - solUnimplementedAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented."); + solUnimplementedAssert(type.sizeOnStack() == 1, "Stack size != 1 not implemented."); m_context << Instruction::DUP1; if (m_currentLValue->sizeOnStack() > 0) for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i) m_context << swapInstruction(i); } - m_context << u256(1); if (_unaryOperation.getOperator() == Token::Inc) - m_context << Instruction::ADD; + { + if (m_context.arithmetic() == Arithmetic::Checked) + m_context.callYulFunction(m_context.utilFunctions().incrementCheckedFunction(type), 1, 1); + else + { + m_context << u256(1); + m_context << Instruction::ADD; + } + } else - m_context << Instruction::SWAP1 << Instruction::SUB; + { + if (m_context.arithmetic() == Arithmetic::Checked) + m_context.callYulFunction(m_context.utilFunctions().decrementCheckedFunction(type), 1, 1); + else + { + m_context << u256(1); + m_context << Instruction::SWAP1 << Instruction::SUB; + } + } // Stack for prefix: [ref...] (*ref)+-1 // Stack for postfix: *ref [ref...] (*ref)+-1 for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i) @@ -427,7 +453,14 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) // unary add, so basically no-op break; case Token::Sub: // - - m_context << u256(0) << Instruction::SUB; + solUnimplementedAssert( + type.category() != Type::Category::FixedPoint, + "Not yet implemented - FixedPointType." + ); + if (m_context.arithmetic() == Arithmetic::Checked) + m_context.callYulFunction(m_context.utilFunctions().negateNumberCheckedFunction(type), 1, 1); + else + m_context << u256(0) << Instruction::SUB; break; default: solAssert(false, "Invalid unary operator: " + string(TokenTraits::toString(_unaryOperation.getOperator()))); @@ -450,7 +483,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) m_context << commonType->literalValue(nullptr); else { - bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op); + bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op, m_context.arithmetic()); TypePointer leftTargetType = commonType; TypePointer rightTargetType = @@ -492,8 +525,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { + auto functionCallKind = *_functionCall.annotation().kind; + CompilerContext::LocationSetter locationSetter(m_context, _functionCall); - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); @@ -517,7 +552,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } FunctionTypePointer functionType; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -527,28 +562,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) functionType = dynamic_cast(_functionCall.expression().annotation().type); TypePointers parameterTypes = functionType->parameterTypes(); - vector> const& callArguments = _functionCall.arguments(); - vector> const& callArgumentNames = _functionCall.names(); - if (!functionType->takesArbitraryParameters()) - solAssert(callArguments.size() == parameterTypes.size(), ""); - - vector> arguments; - if (callArgumentNames.empty()) - // normal arguments - arguments = callArguments; - else - // named arguments - for (auto const& parameterName: functionType->parameterNames()) - { - bool found = false; - for (size_t j = 0; j < callArgumentNames.size() && !found; j++) - if ((found = (parameterName == *callArgumentNames[j]))) - // we found the actual parameter position - arguments.push_back(callArguments[j]); - solAssert(found, ""); - } - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + vector> const& arguments = _functionCall.sortedArguments(); + + if (functionCallKind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -593,6 +610,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Do not directly visit the identifier, because this way, we can avoid // the runtime entry label to be created at the creation time context. CompilerContext::LocationSetter locationSetter2(m_context, *identifier); + solAssert(*identifier->annotation().requiredLookup == VirtualLookup::Virtual, ""); utils().pushCombinedFunctionEntryLabel( functionDef->resolveVirtual(m_context.mostDerivedContract()), false @@ -844,7 +862,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(function.parameterTypes().size() == 1, ""); if (m_context.revertStrings() == RevertStrings::Strip) { - if (!arguments.front()->annotation().isPure) + if (!*arguments.front()->annotation().isPure) { arguments.front()->accept(*this); utils().popStackElement(*arguments.front()->annotation().type); @@ -886,26 +904,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } break; } - case FunctionType::Kind::Log0: - case FunctionType::Kind::Log1: - case FunctionType::Kind::Log2: - case FunctionType::Kind::Log3: - case FunctionType::Kind::Log4: - { - unsigned logNumber = static_cast(function.kind()) - static_cast(FunctionType::Kind::Log0); - for (unsigned arg = logNumber; arg > 0; --arg) - acceptAndConvert(*arguments[arg], *function.parameterTypes()[arg], true); - arguments.front()->accept(*this); - utils().fetchFreeMemoryPointer(); - solAssert(function.parameterTypes().front()->isValueType(), ""); - utils().packedEncode( - {arguments.front()->annotation().type}, - {function.parameterTypes().front()} - ); - utils().toSizeAfterFreeMemoryPointer(); - m_context << logInstruction(logNumber); - break; - } case FunctionType::Kind::Event: { _functionCall.expression().accept(*this); @@ -913,7 +911,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) unsigned numIndexed = 0; TypePointers paramTypes = function.parameterTypes(); // All indexed arguments go to the stack - for (unsigned arg = arguments.size(); arg > 0; --arg) + for (size_t arg = arguments.size(); arg > 0; --arg) if (event.parameters()[arg - 1]->isIndexed()) { ++numIndexed; @@ -931,11 +929,26 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) else { solAssert(paramTypes[arg - 1]->isValueType(), ""); - utils().convertType( - *arguments[arg - 1]->annotation().type, - *paramTypes[arg - 1], - true - ); + if (auto functionType = dynamic_cast(paramTypes[arg - 1])) + { + auto argumentType = + dynamic_cast(arguments[arg-1]->annotation().type); + solAssert( + argumentType && + functionType->kind() == FunctionType::Kind::External && + argumentType->kind() == FunctionType::Kind::External && + !argumentType->bound(), + "" + ); + + utils().combineExternalFunctionType(true); + } + else + utils().convertType( + *arguments[arg - 1]->annotation().type, + *paramTypes[arg - 1], + true + ); } } if (!event.isAnonymous()) @@ -973,7 +986,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { acceptAndConvert(*arguments[2], *TypeProvider::uint256()); m_context << Instruction::DUP1 << Instruction::ISZERO; - m_context.appendConditionalInvalid(); + m_context.appendConditionalPanic(util::PanicCode::DivisionByZero); for (unsigned i = 1; i < 3; i ++) acceptAndConvert(*arguments[2 - i], *TypeProvider::uint256()); if (function.kind() == FunctionType::Kind::AddMod) @@ -1103,7 +1116,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << u256(0xffffffffffffffff); m_context << Instruction::DUP2; m_context << Instruction::GT; - m_context.appendConditionalRevert(); + m_context.appendConditionalPanic(PanicCode::ResourceError); // Stack: requested_length utils().fetchFreeMemoryPointer(); @@ -1155,7 +1168,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(function.kind() == FunctionType::Kind::Require, ""); if (m_context.revertStrings() == RevertStrings::Strip) { - if (!arguments.at(1)->annotation().isPure) + if (!*arguments.at(1)->annotation().isPure) { arguments.at(1)->accept(*this); utils().popStackElement(*arguments.at(1)->annotation().type); @@ -1173,7 +1186,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) auto success = m_context.appendConditionalJump(); if (function.kind() == FunctionType::Kind::Assert) // condition was not met, flag an error - m_context.appendInvalid(); + m_context.appendPanic(util::PanicCode::Assert); else if (haveReasonString) { utils().revertWithStringData(*arguments.at(1)->annotation().type); @@ -1248,8 +1261,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // hash the signature if (auto const* stringType = dynamic_cast(selectorType)) { - FixedHash<4> hash(keccak256(stringType->value())); - m_context << (u256(FixedHash<4>::Arith(hash)) << (256 - 32)); + m_context << util::selectorFromSignature(stringType->value()); dataOnStack = TypeProvider::fixedBytes(4); } else @@ -1398,7 +1410,7 @@ bool ExpressionCompiler::visit(FunctionCallOptions const& _functionCallOptions) solAssert(!contains(presentOptions, newOption), ""); ptrdiff_t insertPos = presentOptions.end() - lower_bound(presentOptions.begin(), presentOptions.end(), newOption); - utils().moveIntoStack(static_cast(insertPos), 1); + utils().moveIntoStack(static_cast(insertPos), 1); presentOptions.insert(presentOptions.end() - insertPos, newOption); } @@ -1423,6 +1435,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) if (funType->kind() == FunctionType::Kind::Internal) { FunctionDefinition const& funDef = dynamic_cast(funType->declaration()); + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); utils().pushCombinedFunctionEntryLabel(funDef); utils().moveIntoStack(funType->selfType()->sizeOnStack(), 1); } @@ -1442,34 +1455,49 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) // for internal functions, or enum/struct definitions. if (TypeType const* type = dynamic_cast(_memberAccess.expression().annotation().type)) { - if (dynamic_cast(type->actualType())) + if (auto contractType = dynamic_cast(type->actualType())) { solAssert(_memberAccess.annotation().type, "_memberAccess has no type"); - if (auto variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - appendVariable(*variable, static_cast(_memberAccess)); - else if (auto funType = dynamic_cast(_memberAccess.annotation().type)) + if (contractType->isSuper()) + { + _memberAccess.expression().accept(*this); + solAssert(_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Super, ""); + utils().pushCombinedFunctionEntryLabel(m_context.superFunction( + dynamic_cast(*_memberAccess.annotation().referencedDeclaration), + contractType->contractDefinition() + )); + } + else { - switch (funType->kind()) + if (auto variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + appendVariable(*variable, static_cast(_memberAccess)); + else if (auto funType = dynamic_cast(_memberAccess.annotation().type)) { - case FunctionType::Kind::Declaration: - break; - case FunctionType::Kind::Internal: - // We do not visit the expression here on purpose, because in the case of an - // internal library function call, this would push the library address forcing - // us to link against it although we actually do not need it. - if (auto const* function = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - utils().pushCombinedFunctionEntryLabel(*function); - else - solAssert(false, "Function not found in member access"); - break; - case FunctionType::Kind::Event: - if (!dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - solAssert(false, "event not found"); - // no-op, because the parent node will do the job - break; - case FunctionType::Kind::DelegateCall: - _memberAccess.expression().accept(*this); - m_context << funType->externalIdentifier(); + switch (funType->kind()) + { + case FunctionType::Kind::Declaration: + break; + case FunctionType::Kind::Internal: + // We do not visit the expression here on purpose, because in the case of an + // internal library function call, this would push the library address forcing + // us to link against it although we actually do not need it. + if (auto const* function = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); + utils().pushCombinedFunctionEntryLabel(*function); + } + else + solAssert(false, "Function not found in member access"); + break; + case FunctionType::Kind::Event: + if (!dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + solAssert(false, "event not found"); + // no-op, because the parent node will do the job + break; + case FunctionType::Kind::DelegateCall: + _memberAccess.expression().accept(*this); + m_context << funType->externalIdentifier(); break; case FunctionType::Kind::External: case FunctionType::Kind::Creation: @@ -1481,11 +1509,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) case FunctionType::Kind::Transfer: case FunctionType::Kind::TransferToken: case FunctionType::Kind::TokenBalance: - case FunctionType::Kind::Log0: - case FunctionType::Kind::Log1: - case FunctionType::Kind::Log2: - case FunctionType::Kind::Log3: - case FunctionType::Kind::Log4: case FunctionType::Kind::ECRecover: case FunctionType::Kind::ValidateMultiSign: case FunctionType::Kind::BatchValidateSign: @@ -1577,16 +1600,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) case Type::Category::Contract: { ContractType const& type = dynamic_cast(*_memberAccess.expression().annotation().type); - if (type.isSuper()) - { - solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); - utils().pushCombinedFunctionEntryLabel(m_context.superFunction( - dynamic_cast(*_memberAccess.annotation().referencedDeclaration), - type.contractDefinition() - )); - } // ordinary contract type - else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) + if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) { u256 identifier; if (auto const* variable = dynamic_cast(declaration)) @@ -1627,6 +1642,48 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) ); m_context << Instruction::ISCONTRACT; } + else if (member == "code") + { + // Stack:
+ utils().convertType( + *_memberAccess.expression().annotation().type, + *TypeProvider::address(), + true + ); + + m_context << Instruction::DUP1 << Instruction::EXTCODESIZE; + // Stack post:
+ + m_context << Instruction::DUP1; + // Account for the size field of `bytes memory` + m_context << u256(32) << Instruction::ADD; + utils().allocateMemory(); + // Stack post:
+ + // Store size at mem_offset + m_context << Instruction::DUP2 << Instruction::DUP2 << Instruction::MSTORE; + + m_context << u256(0) << Instruction::SWAP1 << Instruction::DUP1; + // Stack post:
0 + + m_context << u256(32) << Instruction::ADD << Instruction::SWAP1; + // Stack post:
0 + + m_context << Instruction::SWAP4; + // Stack post: 0
+ + m_context << Instruction::EXTCODECOPY; + // Stack post: + } + else if (member == "codehash") + { + utils().convertType( + *_memberAccess.expression().annotation().type, + *TypeProvider::address(), + true + ); + m_context << Instruction::EXTCODEHASH; + } else if ((set{"send", "transfer", "transferToken"}).count(member)) { solAssert(dynamic_cast(*_memberAccess.expression().annotation().type).stateMutability() @@ -1699,6 +1756,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::ORIGIN; else if (member == "gasprice") m_context << Instruction::GASPRICE; + else if (member == "chainid") + m_context << Instruction::CHAINID; else if (member == "data") m_context << u256(0) << Instruction::CALLDATASIZE; else if (member == "sig") @@ -1711,7 +1770,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "creationCode" || member == "runtimeCode") { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + auto const& contractType = dynamic_cast(*arg); + solAssert(!contractType.isSuper(), ""); + ContractDefinition const& contract = contractType.contractDefinition(); utils().fetchFreeMemoryPointer(); m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; utils().copyContractCodeToMemory(contract, member == "creationCode"); @@ -1728,7 +1789,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "name") { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + auto const& contractType = dynamic_cast(*arg); + ContractDefinition const& contract = contractType.isSuper() ? + *contractType.contractDefinition().superContract(m_context.mostDerivedContract()) : + dynamic_cast(*arg).contractDefinition(); utils().allocateMemory(((contract.name().length() + 31) / 32) * 32 + 32); // store string length m_context << u256(contract.name().length()) << Instruction::DUP2 << Instruction::MSTORE; @@ -1740,10 +1804,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); - uint64_t result{0}; - for (auto const& function: contract.interfaceFunctionList(false)) - result ^= fromBigEndian(function.first.ref()); - m_context << (u256{result} << (256 - 32)); + m_context << (u256{contract.interfaceId()} << (256 - 32)); } else if (member == "min" || member == "max") { @@ -1797,7 +1858,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { solAssert(memberType->calldataEncodedSize() > 0, ""); solAssert(memberType->storageBytes() <= 32, ""); - if (memberType->storageBytes() < 32 && m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + if (memberType->storageBytes() < 32 && m_context.useABICoderV2()) { m_context << u256(32); CompilerUtils(m_context).abiDecodeV2({memberType}, false); @@ -1877,10 +1938,30 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { Type::Category category = _memberAccess.annotation().type->category(); solAssert( + dynamic_cast(_memberAccess.annotation().referencedDeclaration) || + dynamic_cast(_memberAccess.annotation().referencedDeclaration) || category == Type::Category::TypeType || category == Type::Category::Module, "" ); + if (auto variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + solAssert(variable->isConstant(), ""); + appendVariable(*variable, static_cast(_memberAccess)); + } + else if (auto const* function = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + auto funType = dynamic_cast(_memberAccess.annotation().type); + solAssert(function && function->isFree(), ""); + solAssert(funType->kind() == FunctionType::Kind::Internal, ""); + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); + utils().pushCombinedFunctionEntryLabel(*function); + } + else if (auto const* contract = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + if (contract->isLibrary()) + m_context.appendLibraryAddress(contract->fullyQualifiedName()); + } break; } default: @@ -1987,7 +2068,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) m_context << u256(fixedBytesType.numBytes()); m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; // out-of-bounds access throws exception - m_context.appendConditionalInvalid(); + m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds); m_context << Instruction::BYTE; utils().leftShiftNumberOnStack(256 - 8); @@ -2061,20 +2142,25 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) switch (magicVar->type()->category()) { case Type::Category::Contract: - // "this" or "super" - if (!dynamic_cast(*magicVar->type()).isSuper()) + if (dynamic_cast(magicVar->type())) + { + solAssert(_identifier.name() == "this", ""); m_context << Instruction::ADDRESS; + } break; default: break; } } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) + { // If the identifier is called right away, this code is executed in visit(FunctionCall...), because // we want to avoid having a reference to the runtime function entry point in the // constructor context, since this would force the compiler to include unreferenced // internal functions in the runtime context. + solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, ""); utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract())); + } else if (auto variable = dynamic_cast(declaration)) appendVariable(*variable, static_cast(_identifier)); else if (auto contract = dynamic_cast(declaration)) @@ -2204,34 +2290,65 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type cons solUnimplemented("Not yet implemented - FixedPointType."); IntegerType const& type = dynamic_cast(_type); - bool const c_isSigned = type.isSigned(); - - switch (_operator) + if (m_context.arithmetic() == Arithmetic::Checked) { - case Token::Add: - m_context << Instruction::ADD; - break; - case Token::Sub: - m_context << Instruction::SUB; - break; - case Token::Mul: - m_context << Instruction::MUL; - break; - case Token::Div: - case Token::Mod: + string functionName; + switch (_operator) + { + case Token::Add: + functionName = m_context.utilFunctions().overflowCheckedIntAddFunction(type); + break; + case Token::Sub: + functionName = m_context.utilFunctions().overflowCheckedIntSubFunction(type); + break; + case Token::Mul: + functionName = m_context.utilFunctions().overflowCheckedIntMulFunction(type); + break; + case Token::Div: + functionName = m_context.utilFunctions().overflowCheckedIntDivFunction(type); + break; + case Token::Mod: + functionName = m_context.utilFunctions().intModFunction(type); + break; + case Token::Exp: + // EXP is handled in a different function. + default: + solAssert(false, "Unknown arithmetic operator."); + } + // TODO Maybe we want to force-inline this? + m_context.callYulFunction(functionName, 2, 1); + } + else { - // Test for division by zero - m_context << Instruction::DUP2 << Instruction::ISZERO; - m_context.appendConditionalInvalid(); + bool const c_isSigned = type.isSigned(); - if (_operator == Token::Div) - m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); - else - m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD); - break; - } - default: - solAssert(false, "Unknown arithmetic operator."); + switch (_operator) + { + case Token::Add: + m_context << Instruction::ADD; + break; + case Token::Sub: + m_context << Instruction::SUB; + break; + case Token::Mul: + m_context << Instruction::MUL; + break; + case Token::Div: + case Token::Mod: + { + // Test for division by zero + m_context << Instruction::DUP2 << Instruction::ISZERO; + m_context.appendConditionalPanic(util::PanicCode::DivisionByZero); + + if (_operator == Token::Div) + m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); + else + m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD); + break; + } + default: + solAssert(false, "Unknown arithmetic operator."); + } } } @@ -2329,7 +2446,14 @@ void ExpressionCompiler::appendExpOperatorCode(Type const& _valueType, Type cons solAssert(_valueType.category() == Type::Category::Integer, ""); solAssert(!dynamic_cast(_exponentType).isSigned(), ""); - m_context << Instruction::EXP; + + if (m_context.arithmetic() == Arithmetic::Checked) + m_context.callYulFunction(m_context.utilFunctions().overflowCheckedIntExpFunction( + dynamic_cast(_valueType), + dynamic_cast(_exponentType) + ), 2, 1); + else + m_context << Instruction::EXP; } void ExpressionCompiler::appendExternalFunctionCall( @@ -2628,7 +2752,7 @@ void ExpressionCompiler::appendExternalFunctionCall( // memory pointer), but kept references to the return data for // (statically-sized) arrays bool needToUpdateFreeMemoryPtr = false; - if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + if (dynamicReturnSize || m_context.useABICoderV2()) needToUpdateFreeMemoryPtr = true; else for (auto const& retType: returnTypes) @@ -2699,11 +2823,15 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue(_expression, *_expression.annotation().type); } -bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op) +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op, Arithmetic _arithmetic) { if (TokenTraits::isCompareOp(_op) || TokenTraits::isShiftOp(_op)) return true; - else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp)) + else if ( + _arithmetic == Arithmetic::Wrapping && + _type == Type::Category::Integer && + (_op == Token::Div || _op == Token::Mod || _op == Token::Exp) + ) // We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0 // It would suffice to clean the exponent, though. return true; diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 7b574b59c135..66399d070ff7 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -132,7 +132,7 @@ class ExpressionCompiler: private ASTConstVisitor /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. - static bool cleanupNeededForOp(Type::Category _type, Token _op); + static bool cleanupNeededForOp(Type::Category _type, Token _op, Arithmetic _arithmetic); void acceptAndConvert(Expression const& _expression, Type const& _type, bool _cleanupNeeded = false); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index f4fdd0cbb8a3..77e629c650f5 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -291,7 +291,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack: value storage_ref multiplier // fetch old value m_context << Instruction::DUP2 << Instruction::SLOAD; - // stack: value storege_ref multiplier old_full_value + // stack: value storage_ref multiplier old_full_value // clear bytes in old value m_context << Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1) @@ -357,38 +357,47 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc "Struct assignment with conversion." ); solAssert(!structType.containsNestedMapping(), ""); - solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported."); - for (auto const& member: structType.members(nullptr)) + if (sourceType.location() == DataLocation::CallData) { - // assign each member that can live outside of storage - TypePointer const& memberType = member.type; - solAssert(memberType->nameable(), ""); - TypePointer sourceMemberType = sourceType.memberType(member.name); - if (sourceType.location() == DataLocation::Storage) + solAssert(sourceType.sizeOnStack() == 1, ""); + solAssert(structType.sizeOnStack() == 1, ""); + m_context << Instruction::DUP2 << Instruction::DUP2; + m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(sourceType, structType, 0), 2, 0); + } + else + { + for (auto const& member: structType.members(nullptr)) { - // stack layout: source_ref target_ref - pair const& offsets = sourceType.storageOffsetsOfMember(member.name); - m_context << offsets.first << Instruction::DUP3 << Instruction::ADD; + // assign each member that can live outside of storage + TypePointer const& memberType = member.type; + solAssert(memberType->nameable(), ""); + TypePointer sourceMemberType = sourceType.memberType(member.name); + if (sourceType.location() == DataLocation::Storage) + { + // stack layout: source_ref target_ref + pair const& offsets = sourceType.storageOffsetsOfMember(member.name); + m_context << offsets.first << Instruction::DUP3 << Instruction::ADD; + m_context << u256(offsets.second); + // stack: source_ref target_ref source_member_ref source_member_off + StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true); + // stack: source_ref target_ref source_value... + } + else + { + solAssert(sourceType.location() == DataLocation::Memory, ""); + // stack layout: source_ref target_ref + m_context << sourceType.memoryOffsetOfMember(member.name); + m_context << Instruction::DUP3 << Instruction::ADD; + MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true); + // stack layout: source_ref target_ref source_value... + } + unsigned stackSize = sourceMemberType->sizeOnStack(); + pair const& offsets = structType.storageOffsetsOfMember(member.name); + m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD; m_context << u256(offsets.second); - // stack: source_ref target_ref source_member_ref source_member_off - StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true); - // stack: source_ref target_ref source_value... - } - else - { - solAssert(sourceType.location() == DataLocation::Memory, ""); - // stack layout: source_ref target_ref - m_context << sourceType.memoryOffsetOfMember(member.name); - m_context << Instruction::DUP3 << Instruction::ADD; - MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true); - // stack layout: source_ref target_ref source_value... + // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off + StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); } - unsigned stackSize = sourceMemberType->sizeOnStack(); - pair const& offsets = structType.storageOffsetsOfMember(member.name); - m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD; - m_context << u256(offsets.second); - // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off - StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); } // stack layout: source_ref target_ref solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size."); @@ -452,7 +461,7 @@ void StorageItem::setToZero(SourceLocation const&, bool _removeReference) const // stack: storage_ref multiplier // fetch old value m_context << Instruction::DUP2 << Instruction::SLOAD; - // stack: storege_ref multiplier old_full_value + // stack: storage_ref multiplier old_full_value // clear bytes in old value m_context << Instruction::SWAP1 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1) diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp index 0049deb780f8..541171818e2f 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp @@ -33,8 +33,11 @@ string MultiUseYulFunctionCollector::requestedFunctions() { string result; for (auto const& f: m_requestedFunctions) + { + solAssert(f.second != "<isDynamicallyEncoded()) + { + solAssert(retType->decodingType(), ""); + if (retType->decodingType()->isDynamicallyEncoded()) { solAssert(haveReturndatacopy, ""); dynamicReturnSize = true; estimatedReturnSize = 0; break; } - else if (retType->decodingType()) - estimatedReturnSize += retType->decodingType()->calldataEncodedSize(); else - estimatedReturnSize += retType->calldataEncodedSize(); + estimatedReturnSize += retType->decodingType()->calldataEncodedSize(); + } } } diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 61ee9c461c10..dbc8d568ce37 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -118,19 +119,15 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess if (!_messageType) return Whiskers(R"( function (condition) { - if iszero(condition) { } + if iszero(condition) { } } )") - ("invalidOrRevert", _assert ? "invalid()" : "revert(0, 0)") + ("error", _assert ? panicFunction(PanicCode::Assert) + "()" : "revert(0, 0)") ("functionName", functionName) .render(); int const hashHeaderSize = 4; - int const byteSize = 8; - u256 const errorHash = - u256(FixedHash::Arith( - FixedHash(keccak256("Error(string)")) - )) << (256 - hashHeaderSize * byteSize); + u256 const errorHash = util::selectorFromSignature("Error(string)"); string const encodeFunc = ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector) .tupleEncoder( @@ -206,8 +203,8 @@ string YulUtilFunctions::leftAlignFunction(Type const& _type) break; case Type::Category::Enum: { - unsigned storageBytes = dynamic_cast(_type).storageBytes(); - templ("body", "aligned := " + leftAlignFunction(IntegerType(8 * storageBytes)) + "(value)"); + solAssert(dynamic_cast(_type).storageBytes() == 1, ""); + templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)"); break; } case Type::Category::InaccessibleDynamic: @@ -437,6 +434,51 @@ string YulUtilFunctions::updateByteSliceFunctionDynamic(size_t _numBytes) }); } +string YulUtilFunctions::maskBytesFunctionDynamic() +{ + string functionName = "mask_bytes_dynamic"; + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (data, bytes) -> result { + let mask := not((mul(8, bytes), not(0))) + result := and(data, mask) + })") + ("functionName", functionName) + ("shr", shiftRightFunctionDynamic()) + .render(); + }); +} + +string YulUtilFunctions::maskLowerOrderBytesFunction(size_t _bytes) +{ + string functionName = "mask_lower_order_bytes_" + to_string(_bytes); + solAssert(_bytes <= 32, ""); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (data) -> result { + result := and(data, ) + })") + ("functionName", functionName) + ("mask", formatNumber((~u256(0)) >> (256 - 8 * _bytes))) + .render(); + }); +} + +string YulUtilFunctions::maskLowerOrderBytesFunctionDynamic() +{ + string functionName = "mask_lower_order_bytes_dynamic"; + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (data, bytes) -> result { + let mask := not((mul(8, bytes), not(0))) + result := and(data, mask) + })") + ("functionName", functionName) + ("shl", shiftLeftFunctionDynamic()) + .render(); + }); +} + string YulUtilFunctions::roundUpFunction() { string functionName = "round_up_to_mul_of_32"; @@ -457,7 +499,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) string functionName = "checked_add_" + _type.identifier(); // TODO: Consider to add a special case for unsigned 256-bit integers // and use the following instead: - // sum := add(x, y) if lt(sum, x) { revert(0, 0) } + // sum := add(x, y) if lt(sum, x) { () } return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( @@ -466,12 +508,12 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) y := (y) // overflow, if x >= 0 and y > (maxValue - x) - if and(iszero(slt(x, 0)), sgt(y, sub(, x))) { revert(0, 0) } + if and(iszero(slt(x, 0)), sgt(y, sub(, x))) { () } // underflow, if x < 0 and y < (minValue - x) - if and(slt(x, 0), slt(y, sub(, x))) { revert(0, 0) } + if and(slt(x, 0), slt(y, sub(, x))) { () } // overflow, if x > (maxValue - y) - if gt(x, sub(, y)) { revert(0, 0) } + if gt(x, sub(, y)) { () } sum := add(x, y) } @@ -481,6 +523,23 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction(PanicCode::UnderOverflow)) + .render(); + }); +} + +string YulUtilFunctions::wrappingIntAddFunction(IntegerType const& _type) +{ + string functionName = "wrapping_add_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (x, y) -> sum { + sum := (add(x, y)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(_type)) .render(); }); } @@ -497,16 +556,16 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) y := (y) // overflow, if x > 0, y > 0 and x > (maxValue / y) - if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(, y))) { revert(0, 0) } + if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(, y))) { () } // underflow, if x > 0, y < 0 and y < (minValue / x) - if and(and(sgt(x, 0), slt(y, 0)), slt(y, sdiv(, x))) { revert(0, 0) } + if and(and(sgt(x, 0), slt(y, 0)), slt(y, sdiv(, x))) { () } // underflow, if x < 0, y > 0 and x < (minValue / y) - if and(and(slt(x, 0), sgt(y, 0)), slt(x, sdiv(, y))) { revert(0, 0) } + if and(and(slt(x, 0), sgt(y, 0)), slt(x, sdiv(, y))) { () } // overflow, if x < 0, y < 0 and x < (maxValue / y) - if and(and(slt(x, 0), slt(y, 0)), slt(x, sdiv(, y))) { revert(0, 0) } + if and(and(slt(x, 0), slt(y, 0)), slt(x, sdiv(, y))) { () } // overflow, if x != 0 and y > (maxValue / x) - if and(iszero(iszero(x)), gt(y, div(, x))) { revert(0, 0) } + if and(iszero(iszero(x)), gt(y, div(, x))) { () } product := mul(x, y) } @@ -516,6 +575,23 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction(PanicCode::UnderOverflow)) + .render(); + }); +} + +string YulUtilFunctions::wrappingIntMulFunction(IntegerType const& _type) +{ + string functionName = "wrapping_mul_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (x, y) -> product { + product := (mul(x, y)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(_type)) .render(); }); } @@ -529,13 +605,13 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) function (x, y) -> r { x := (x) y := (y) - if iszero(y) { revert(0, 0) } + if iszero(y) { () } // overflow for minVal / -1 if and( eq(x, ), eq(y, sub(0, 1)) - ) { revert(0, 0) } + ) { () } r := sdiv(x, y) } @@ -544,26 +620,50 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) ("signed", _type.isSigned()) ("minVal", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panicDivZero", panicFunction(PanicCode::DivisionByZero)) + ("panicOverflow", panicFunction(PanicCode::UnderOverflow)) .render(); }); } -string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type) +string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type) { - string functionName = "checked_mod_" + _type.identifier(); + string functionName = "wrapping_div_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (x, y) -> r { x := (x) y := (y) - if iszero(y) { revert(0, 0) } + if iszero(y) { () } + r := sdiv(x, y) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(_type)) + ("signed", _type.isSigned()) + ("error", panicFunction(PanicCode::DivisionByZero)) + .render(); + }); +} + +string YulUtilFunctions::intModFunction(IntegerType const& _type) +{ + string functionName = "mod_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (x, y) -> r { + x := (x) + y := (y) + if iszero(y) { () } r := smod(x, y) } )") ("functionName", functionName) ("signed", _type.isSigned()) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction(PanicCode::DivisionByZero)) .render(); }); } @@ -579,11 +679,11 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) y := (y) // underflow, if y >= 0 and x < (minValue + y) - if and(iszero(slt(y, 0)), slt(x, add(, y))) { revert(0, 0) } + if and(iszero(slt(y, 0)), slt(x, add(, y))) { () } // overflow, if y < 0 and x > (maxValue + y) - if and(slt(y, 0), sgt(x, add(, y))) { revert(0, 0) } + if and(slt(y, 0), sgt(x, add(, y))) { () } - if lt(x, y) { revert(0, 0) } + if lt(x, y) { () } diff := sub(x, y) } @@ -593,26 +693,343 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction(PanicCode::UnderOverflow)) .render(); }); } -string YulUtilFunctions::extractByteArrayLengthFunction() +string YulUtilFunctions::wrappingIntSubFunction(IntegerType const& _type) { - string functionName = "extract_byte_array_length"; + string functionName = "wrapping_sub_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&] { + return + Whiskers(R"( + function (x, y) -> diff { + diff := (sub(x, y)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(_type)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedIntExpFunction( + IntegerType const& _type, + IntegerType const& _exponentType +) +{ + solAssert(!_exponentType.isSigned(), ""); + + string functionName = "checked_exp_" + _type.identifier() + "_" + _exponentType.identifier(); return m_functionCollector.createFunction(functionName, [&]() { - Whiskers w(R"( - function (data) -> length { - // Retrieve length both for in-place strings and off-place strings: - // Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2 - // i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it - // computes (x & (-1)) / 2, which is equivalent to just x / 2. - let mask := sub(mul(0x100, iszero(and(data, 1))), 1) - length := div(and(data, mask), 2) + return + Whiskers(R"( + function (base, exponent) -> power { + base := (base) + exponent := (exponent) + + power := (base, exponent, , ) + + power := (base, exponent, ) + + } - )"); - w("functionName", functionName); - return w.render(); + )") + ("functionName", functionName) + ("signed", _type.isSigned()) + ("exp", _type.isSigned() ? overflowCheckedSignedExpFunction() : overflowCheckedUnsignedExpFunction()) + ("maxValue", toCompactHexWithPrefix(_type.max())) + ("minValue", toCompactHexWithPrefix(_type.min())) + ("baseCleanupFunction", cleanupFunction(_type)) + ("exponentCleanupFunction", cleanupFunction(_exponentType)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedIntLiteralExpFunction( + RationalNumberType const& _baseType, + IntegerType const& _exponentType, + IntegerType const& _commonType +) +{ + solAssert(!_exponentType.isSigned(), ""); + solAssert(_baseType.isNegative() == _commonType.isSigned(), ""); + solAssert(_commonType.numBits() == 256, ""); + + string functionName = "checked_exp_" + _baseType.richIdentifier() + "_" + _exponentType.identifier(); + + return m_functionCollector.createFunction(functionName, [&]() + { + // Converts a bigint number into u256 (negative numbers represented in two's complement form.) + // We assume that `_v` fits in 256 bits. + auto bigint2u = [&](bigint const& _v) -> u256 + { + if (_v < 0) + return s2u(s256(_v)); + return u256(_v); + }; + + // Calculates the upperbound for exponentiation, that is, calculate `b`, such that + // _base**b <= _maxValue and _base**(b + 1) > _maxValue + auto findExponentUpperbound = [](bigint const _base, bigint const _maxValue) -> unsigned + { + // There is no overflow for these cases + if (_base == 0 || _base == -1 || _base == 1) + return 0; + + unsigned first = 0; + unsigned last = 255; + unsigned middle; + + while (first < last) + { + middle = (first + last) / 2; + + if ( + // The condition on msb is a shortcut that avoids computing large powers in + // arbitrary precision. + boost::multiprecision::msb(_base) * middle <= boost::multiprecision::msb(_maxValue) && + boost::multiprecision::pow(_base, middle) <= _maxValue + ) + { + if (boost::multiprecision::pow(_base, middle + 1) > _maxValue) + return middle; + else + first = middle + 1; + } + else + last = middle; + } + + return last; + }; + + bigint baseValue = _baseType.isNegative() ? + u2s(_baseType.literalValue(nullptr)) : + _baseType.literalValue(nullptr); + bool needsOverflowCheck = !((baseValue == 0) || (baseValue == -1) || (baseValue == 1)); + unsigned exponentUpperbound; + + if (_baseType.isNegative()) + { + // Only checks for underflow. The only case where this can be a problem is when, for a + // negative base, say `b`, and an even exponent, say `e`, `b**e = 2**255` (which is an + // overflow.) But this never happens because, `255 = 3*5*17`, and therefore there is no even + // number `e` such that `b**e = 2**255`. + exponentUpperbound = findExponentUpperbound(abs(baseValue), abs(_commonType.minValue())); + + bigint power = boost::multiprecision::pow(baseValue, exponentUpperbound); + bigint overflowedPower = boost::multiprecision::pow(baseValue, exponentUpperbound + 1); + + if (needsOverflowCheck) + solAssert( + (power <= _commonType.maxValue()) && (power >= _commonType.minValue()) && + !((overflowedPower <= _commonType.maxValue()) && (overflowedPower >= _commonType.minValue())), + "Incorrect exponent upper bound calculated." + ); + } + else + { + exponentUpperbound = findExponentUpperbound(baseValue, _commonType.maxValue()); + + if (needsOverflowCheck) + solAssert( + boost::multiprecision::pow(baseValue, exponentUpperbound) <= _commonType.maxValue() && + boost::multiprecision::pow(baseValue, exponentUpperbound + 1) > _commonType.maxValue(), + "Incorrect exponent upper bound calculated." + ); + } + + return Whiskers(R"( + function (exponent) -> power { + exponent := (exponent) + + if gt(exponent, ) { () } + + power := exp(, exponent) + } + )") + ("functionName", functionName) + ("exponentCleanupFunction", cleanupFunction(_exponentType)) + ("needsOverflowCheck", needsOverflowCheck) + ("exponentUpperbound", to_string(exponentUpperbound)) + ("panic", panicFunction(PanicCode::UnderOverflow)) + ("base", bigint2u(baseValue).str()) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedUnsignedExpFunction() +{ + // Checks for the "small number specialization" below. + using namespace boost::multiprecision; + solAssert(pow(bigint(10), 77) < pow(bigint(2), 256), ""); + solAssert(pow(bigint(11), 77) >= pow(bigint(2), 256), ""); + solAssert(pow(bigint(10), 78) >= pow(bigint(2), 256), ""); + + solAssert(pow(bigint(306), 31) < pow(bigint(2), 256), ""); + solAssert(pow(bigint(307), 31) >= pow(bigint(2), 256), ""); + solAssert(pow(bigint(306), 32) >= pow(bigint(2), 256), ""); + + string functionName = "checked_exp_unsigned"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent, max) -> power { + // This function currently cannot be inlined because of the + // "leave" statements. We have to improve the optimizer. + + // Note that 0**0 == 1 + if iszero(exponent) { power := 1 leave } + if iszero(base) { power := 0 leave } + + // Specializations for small bases + switch base + // 0 is handled above + case 1 { power := 1 leave } + case 2 + { + if gt(exponent, 255) { () } + power := exp(2, exponent) + if gt(power, max) { () } + leave + } + if or( + and(lt(base, 11), lt(exponent, 78)), + and(lt(base, 307), lt(exponent, 32)) + ) + { + power := exp(base, exponent) + if gt(power, max) { () } + leave + } + + power, base := (1, base, exponent, max) + + if gt(power, div(max, base)) { () } + power := mul(power, base) + } + )") + ("functionName", functionName) + ("panic", panicFunction(PanicCode::UnderOverflow)) + ("expLoop", overflowCheckedExpLoopFunction()) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedSignedExpFunction() +{ + string functionName = "checked_exp_signed"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent, min, max) -> power { + // Currently, `leave` avoids this function being inlined. + // We have to improve the optimizer. + + // Note that 0**0 == 1 + switch exponent + case 0 { power := 1 leave } + case 1 { power := base leave } + if iszero(base) { power := 0 leave } + + power := 1 + + // We pull out the first iteration because it is the only one in which + // base can be negative. + // Exponent is at least 2 here. + + // overflow check for base * base + switch sgt(base, 0) + case 1 { if gt(base, div(max, base)) { () } } + case 0 { if slt(base, sdiv(max, base)) { () } } + if and(exponent, 1) + { + power := base + } + base := mul(base, base) + exponent := (exponent) + + // Below this point, base is always positive. + + power, base := (power, base, exponent, max) + + if and(sgt(power, 0), gt(power, div(max, base))) { () } + if and(slt(power, 0), slt(power, sdiv(min, base))) { () } + power := mul(power, base) + } + )") + ("functionName", functionName) + ("panic", panicFunction(PanicCode::UnderOverflow)) + ("expLoop", overflowCheckedExpLoopFunction()) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedExpLoopFunction() +{ + // We use this loop for both signed and unsigned exponentiation + // because we pull out the first iteration in the signed case which + // results in the base always being positive. + + // This function does not include the final multiplication. + + string functionName = "checked_exp_helper"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (_power, _base, exponent, max) -> power, base { + power := _power + base := _base + for { } gt(exponent, 1) {} + { + // overflow check for base * base + if gt(base, div(max, base)) { () } + if and(exponent, 1) + { + // No checks for power := mul(power, base) needed, because the check + // for base * base above is sufficient, since: + // |power| <= base (proof by induction) and thus: + // |power * base| <= base * base <= max <= |min| (for signed) + // (this is equally true for signed and unsigned exp) + power := mul(power, base) + } + base := mul(base, base) + exponent := (exponent) + } + } + )") + ("functionName", functionName) + ("panic", panicFunction(PanicCode::UnderOverflow)) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + +string YulUtilFunctions::wrappingIntExpFunction( + IntegerType const& _type, + IntegerType const& _exponentType +) +{ + solAssert(!_exponentType.isSigned(), ""); + + string functionName = "wrapping_exp_" + _type.identifier() + "_" + _exponentType.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (base, exponent) -> power { + base := (base) + exponent := (exponent) + power := (exp(base, exponent)) + } + )") + ("functionName", functionName) + ("baseCleanupFunction", cleanupFunction(_type)) + ("exponentCleanupFunction", cleanupFunction(_exponentType)) + .render(); }); } @@ -621,7 +1038,7 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type) string functionName = "array_length_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { Whiskers w(R"( - function (value) -> length { + function (value, len) -> length { length := mload(value) @@ -632,6 +1049,9 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type) length := (length) + + length := len + length := @@ -643,41 +1063,63 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type) w("length", toCompactHexWithPrefix(_type.length())); w("memory", _type.location() == DataLocation::Memory); w("storage", _type.location() == DataLocation::Storage); + w("calldata", _type.location() == DataLocation::CallData); if (_type.location() == DataLocation::Storage) { w("byteArray", _type.isByteArray()); if (_type.isByteArray()) w("extractByteArrayLength", extractByteArrayLengthFunction()); } - if (_type.isDynamicallySized()) - solAssert( - _type.location() != DataLocation::CallData, - "called regular array length function on calldata array" - ); + + return w.render(); + }); +} + +string YulUtilFunctions::extractByteArrayLengthFunction() +{ + string functionName = "extract_byte_array_length"; + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers w(R"( + function (data) -> length { + length := div(data, 2) + let outOfPlaceEncoding := and(data, 1) + if iszero(outOfPlaceEncoding) { + length := and(length, 0x7f) + } + + if eq(outOfPlaceEncoding, lt(length, 32)) { + () + } + } + )"); + w("functionName", functionName); + w("panic", panicFunction(PanicCode::StorageEncodingError)); return w.render(); }); } -std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type) +std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); - solAssert(_type.isDynamicallySized(), ""); - solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!"); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "..."); - solUnimplementedAssert(_type.baseType()->storageSize() == 1, ""); + + if (_type.isByteArray()) + return resizeDynamicByteArrayFunction(_type); string functionName = "resize_array_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array, newLen) { if gt(newLen, ) { - invalid() + () } let oldLen := (array) - // Store new length - sstore(array, newLen) + + // Store new length + sstore(array, newLen) + // Size was reduced, clear end of array if lt(newLen, oldLen) { @@ -686,15 +1128,171 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type) let arrayDataStart := (array) let deleteStart := add(arrayDataStart, newSlotCount) let deleteEnd := add(arrayDataStart, oldSlotCount) + + // if we are dealing with packed array and offset is greater than zero + // we have to partially clear last slot that is still used, so decreasing start by one + let offset := mul(mod(newLen, ), ) + if gt(offset, 0) { (sub(deleteStart, 1), offset) } + (deleteStart, deleteEnd) } })") ("functionName", functionName) + ("panic", panicFunction(PanicCode::ResourceError)) ("fetchLength", arrayLengthFunction(_type)) + ("isDynamic", _type.isDynamicallySized()) ("convertToSize", arrayConvertLengthToSize(_type)) ("dataPosition", arrayDataAreaFunction(_type)) ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) ("maxArrayLength", (u256(1) << 64).str()) + ("packed", _type.baseType()->storageBytes() <= 16) + ("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes())) + ("storageBytes", to_string(_type.baseType()->storageBytes())) + ("partialClearStorageSlot", partialClearStorageSlotFunction()) + .render(); + }); +} + +string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type) +{ + string functionName = "resize_array_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (array, newLen) { + if gt(newLen, ) { + () + } + + let data := sload(array) + let oldLen := (data) + + if gt(newLen, oldLen) { + (array, data, oldLen, newLen) + } + + if lt(newLen, oldLen) { + (array, data, oldLen, newLen) + } + })") + ("functionName", functionName) + ("panic", panicFunction(PanicCode::ResourceError)) + ("extractLength", extractByteArrayLengthFunction()) + ("maxArrayLength", (u256(1) << 64).str()) + ("decreaseSize", decreaseByteArraySizeFunction(_type)) + ("increaseSize", increaseByteArraySizeFunction(_type)) + .render(); + }); +} + +string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type) +{ + string functionName = "byte_array_decrease_size_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (array, data, oldLen, newLen) { + switch lt(newLen, 32) + case 0 { + let arrayDataStart := (array) + let deleteStart := add(arrayDataStart, div(add(newLen, 31), 32)) + + // we have to partially clear last slot that is still used + let offset := and(newLen, 0x1f) + if offset { (sub(deleteStart, 1), offset) } + + (deleteStart, add(arrayDataStart, div(add(oldLen, 31), 32))) + + sstore(array, or(mul(2, newLen), 1)) + } + default { + switch gt(oldLen, 31) + case 1 { + let arrayDataStart := (array) + // clear whole old array, as we are transforming to short bytes array + (add(arrayDataStart, 1), add(arrayDataStart, div(add(oldLen, 31), 32))) + (array, newLen) + } + default { + sstore(array, (data, newLen)) + } + } + })") + ("functionName", functionName) + ("dataPosition", arrayDataAreaFunction(_type)) + ("partialClearStorageSlot", partialClearStorageSlotFunction()) + ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) + ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) + .render(); + }); +} + +string YulUtilFunctions::increaseByteArraySizeFunction(ArrayType const& _type) +{ + string functionName = "byte_array_increase_size_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (array, data, oldLen, newLen) { + switch lt(oldLen, 32) + case 0 { + // in this case array stays unpacked, so we just set new length + sstore(array, add(mul(2, newLen), 1)) + } + default { + switch lt(newLen, 32) + case 0 { + // we need to copy elements to data area as we changed array from packed to unpacked + data := and(not(0xff), data) + sstore((array), data) + sstore(array, add(mul(2, newLen), 1)) + } + default { + // here array stays packed, we just need to increase length + sstore(array, (data, newLen)) + } + } + })") + ("functionName", functionName) + ("dataPosition", arrayDataAreaFunction(_type)) + ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) + .render(); + }); +} + +string YulUtilFunctions::byteArrayTransitLongToShortFunction(ArrayType const& _type) +{ + string functionName = "transit_byte_array_long_to_short_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (array, len) { + // we need to copy elements from old array to new + // we want to copy only elements that are part of the array after resizing + let dataPos := (array) + let data := (sload(dataPos), len) + sstore(array, data) + sstore(dataPos, 0) + })") + ("functionName", functionName) + ("dataPosition", arrayDataAreaFunction(_type)) + ("extractUsedApplyLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) + ("shl", shiftLeftFunctionDynamic()) + ("ones", formatNumber((bigint(1) << 256) - 1)) + .render(); + }); +} + +string YulUtilFunctions::shortByteArrayEncodeUsedAreaSetLengthFunction() +{ + string functionName = "extract_used_part_and_set_length_of_short_byte_array"; + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (data, len) -> used { + // we want to save only elements that are part of the array after resizing + // others should be set to zero + data := (data, len) + used := or(data, mul(2, len)) + })") + ("functionName", functionName) + ("maskBytes", maskBytesFunctionDynamic()) .render(); }); } @@ -712,13 +1310,14 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type) return Whiskers(R"( function (array) { let oldLen := (array) - if iszero(oldLen) { invalid() } + if iszero(oldLen) { () } let newLen := sub(oldLen, 1) let slot, offset := (array, newLen) (slot, offset) sstore(array, newLen) })") ("functionName", functionName) + ("panic", panicFunction(PanicCode::EmptyArrayPop)) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) ("setToZero", storageSetToZeroFunction(*_type.baseType())) @@ -738,37 +1337,32 @@ string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type) function (array) { let data := sload(array) let oldLen := (data) - if iszero(oldLen) { invalid() } + if iszero(oldLen) { () } - switch eq(oldLen, 32) - case 1 { + switch oldLen + case 32 { // Here we have a special case where array transitions to shorter than 32 // So we need to copy data - let copyFromSlot := (array) - data := sload(copyFromSlot) - sstore(copyFromSlot, 0) - // New length is 31, encoded to 31 * 2 = 62 - data := or(and(data, not(0xff)), 62) + (array, 31) } default { - data := sub(data, 2) let newLen := sub(oldLen, 1) switch lt(oldLen, 32) case 1 { - // set last element to zero - let mask := not((mul(8, sub(31, newLen)), 0xff)) - data := and(data, mask) + sstore(array, (data, newLen)) } default { let slot, offset := (array, newLen) (slot, offset) + sstore(array, sub(data, 2)) } } - sstore(array, data) })") ("functionName", functionName) + ("panic", panicFunction(PanicCode::EmptyArrayPop)) ("extractByteArrayLength", extractByteArrayLengthFunction()) - ("dataAreaFunction", arrayDataAreaFunction(_type)) + ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) + ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) ("indexAccess", storageArrayIndexAccessFunction(_type)) ("setToZero", storageSetToZeroFunction(*_type.baseType())) ("shl", shiftLeftFunctionDynamic()) @@ -780,7 +1374,6 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); string functionName = "array_push_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { @@ -789,7 +1382,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) let data := sload(array) let oldLen := (data) - if iszero(lt(oldLen, )) { invalid() } + if iszero(lt(oldLen, )) { () } switch gt(oldLen, 31) case 0 { @@ -820,18 +1413,19 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) } let oldLen := sload(array) - if iszero(lt(oldLen, )) { invalid() } + if iszero(lt(oldLen, )) { () } sstore(array, add(oldLen, 1)) let slot, offset := (array, oldLen) (slot, offset, value) })") ("functionName", functionName) + ("panic", panicFunction(PanicCode::ResourceError)) ("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "") ("dataAreaFunction", arrayDataAreaFunction(_type)) ("isByteArray", _type.isByteArray()) ("indexAccess", storageArrayIndexAccessFunction(_type)) - ("storeValue", updateStorageValueFunction(*_type.baseType())) + ("storeValue", updateStorageValueFunction(*_type.baseType(), *_type.baseType())) ("maxArrayLength", (u256(1) << 64).str()) ("shl", shiftLeftFunctionDynamic()) ("shr", shiftRightFunction(248)) @@ -843,36 +1437,58 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.isDynamicallySized(), ""); - solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!"); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); - solAssert(_type.baseType()->isValueType(), ""); - string functionName = "array_push_zero_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (array) -> slot, offset { - let oldLen := (array) - if iszero(lt(oldLen, )) { invalid() } - sstore(array, add(oldLen, 1)) + + let data := sload(array) + let oldLen := (data) + (array, data, oldLen, add(oldLen, 1)) + + let oldLen := (array) + if iszero(lt(oldLen, )) { () } + sstore(array, add(oldLen, 1)) + slot, offset := (array, oldLen) - (slot, offset, ()) })") ("functionName", functionName) + ("isBytes", _type.isByteArray()) + ("increaseBytesSize", _type.isByteArray() ? increaseByteArraySizeFunction(_type) : "") + ("extractLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "") + ("panic", panicFunction(PanicCode::ResourceError)) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) - ("storeValue", updateStorageValueFunction(*_type.baseType())) ("maxArrayLength", (u256(1) << 64).str()) - ("zeroValueFunction", zeroValueFunction(*_type.baseType())) .render(); }); } +string YulUtilFunctions::partialClearStorageSlotFunction() +{ + string functionName = "partial_clear_storage_slot"; + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (slot, offset) { + let mask := (mul(8, sub(32, offset)), ) + sstore(slot, and(mask, sload(slot))) + } + )") + ("functionName", functionName) + ("ones", formatNumber((bigint(1) << 256) - 1)) + ("shr", shiftRightFunctionDynamic()) + .render(); + }); +} + string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) { - string functionName = "clear_storage_range_" + _type.identifier(); + if (_type.storageBytes() < 32) + solAssert(_type.isValueType(), ""); - solAssert(_type.storageBytes() >= 32, "Expected smaller value for storage bytes"); + string functionName = "clear_storage_range_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( @@ -884,7 +1500,7 @@ string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) } )") ("functionName", functionName) - ("setToZero", storageSetToZeroFunction(_type)) + ("setToZero", storageSetToZeroFunction(_type.storageBytes() < 32 ? *TypeProvider::uint256() : _type)) ("increment", _type.storageSize().str()) .render(); }); @@ -917,7 +1533,7 @@ string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type) )") ("functionName", functionName) ("dynamic", _type.isDynamicallySized()) - ("resizeArray", _type.isDynamicallySized() ? resizeDynamicArrayFunction(_type) : "") + ("resizeArray", _type.isDynamicallySized() ? resizeArrayFunction(_type) : "") ( "clearRange", clearStorageRangeFunction( @@ -932,6 +1548,311 @@ string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type) }); } +string YulUtilFunctions::clearStorageStructFunction(StructType const& _type) +{ + solAssert(_type.location() == DataLocation::Storage, ""); + + string functionName = "clear_struct_storage_" + _type.identifier(); + + return m_functionCollector.createFunction(functionName, [&] { + MemberList::MemberMap structMembers = _type.nativeMembers(nullptr); + vector> memberSetValues; + + set slotsCleared; + for (auto const& member: structMembers) + if (member.type->storageBytes() < 32) + { + auto const& slotDiff = _type.storageOffsetsOfMember(member.name).first; + if (!slotsCleared.count(slotDiff)) + { + memberSetValues.emplace_back().emplace("clearMember", "sstore(add(slot, " + slotDiff.str() + "), 0)"); + slotsCleared.emplace(slotDiff); + } + } + else + { + auto const& [memberSlotDiff, memberStorageOffset] = _type.storageOffsetsOfMember(member.name); + solAssert(memberStorageOffset == 0, ""); + + memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"( + (add(slot, ), ) + )") + ("setZero", storageSetToZeroFunction(*member.type)) + ("memberSlotDiff", memberSlotDiff.str()) + ("memberStorageOffset", to_string(memberStorageOffset)) + .render() + ); + } + + return Whiskers(R"( + function (slot) { + <#member> + + + } + )") + ("functionName", functionName) + ("allocStruct", allocateMemoryStructFunction(_type)) + ("storageSize", _type.storageSize().str()) + ("member", memberSetValues) + .render(); + }); +} + +string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType) +{ + solAssert( + *_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast(_toType), + "" + ); + if (!_toType.isDynamicallySized()) + solAssert(!_fromType.isDynamicallySized() && _fromType.length() <= _toType.length(), ""); + + if (_fromType.isByteArray()) + return copyByteArrayToStorageFunction(_fromType, _toType); + if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType()) + return copyValueArrayStorageToStorageFunction(_fromType, _toType); + + string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier(); + return m_functionCollector.createFunction(functionName, [&](){ + Whiskers templ(R"( + function (slot, value, len) { + if eq(slot, value) { leave } + let length := (value, len) + + (slot, length) + + let srcPtr := (value) + + let elementSlot := (slot) + let elementOffset := 0 + + for { let i := 0 } lt(i, length) {i := add(i, 1)} { + + let := + + (value, srcPtr) + + srcPtr + + + + := () + + + + + let := (srcPtr) + + + + let := srcPtr + + + (elementSlot, elementOffset, ) + + srcPtr := add(srcPtr, ) + + + elementOffset := add(elementOffset, ) + if gt(elementOffset, sub(32, )) { + elementOffset := 0 + elementSlot := add(elementSlot, 1) + } + + elementSlot := add(elementSlot, ) + + } + } + )"); + if (_fromType.dataStoredIn(DataLocation::Storage)) + solAssert(!_fromType.isValueType(), ""); + templ("functionName", functionName); + bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData); + templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata); + templ("fromStorage", _fromType.dataStoredIn(DataLocation::Storage)); + bool fromMemory = _fromType.dataStoredIn(DataLocation::Memory); + templ("fromMemory", fromMemory); + templ("fromCalldata", fromCalldata); + templ("srcDataLocation", arrayDataAreaFunction(_fromType)); + if (fromCalldata) + { + templ("dynamicallySizedBase", _fromType.baseType()->isDynamicallySized()); + templ("dynamicallyEncodedBase", _fromType.baseType()->isDynamicallyEncoded()); + if (_fromType.baseType()->isDynamicallyEncoded()) + templ("accessCalldataTail", accessCalldataTailFunction(*_fromType.baseType())); + } + templ("resizeArray", resizeArrayFunction(_toType)); + templ("arrayLength",arrayLengthFunction(_fromType)); + templ("isValueType", _fromType.baseType()->isValueType()); + templ("dstDataLocation", arrayDataAreaFunction(_toType)); + if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType())) + templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata)); + templ("elementValues", suffixedVariableNameList( + "elementValue_", + 0, + _fromType.baseType()->stackItems().size() + )); + templ("updateStorageValue", updateStorageValueFunction(*_fromType.baseType(), *_toType.baseType())); + templ("srcStride", + fromCalldata ? + to_string(_fromType.calldataStride()) : + fromMemory ? + to_string(_fromType.memoryStride()) : + formatNumber(_fromType.baseType()->storageSize()) + ); + templ("multipleItemsPerSlot", _toType.storageStride() <= 16); + templ("storageStride", to_string(_toType.storageStride())); + templ("storageSize", _toType.baseType()->storageSize().str()); + + return templ.render(); + }); +} + + +string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType) +{ + solAssert( + *_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast(_toType), + "" + ); + solAssert(_fromType.isByteArray(), ""); + solAssert(_toType.isByteArray(), ""); + + string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier(); + return m_functionCollector.createFunction(functionName, [&](){ + Whiskers templ(R"( + function (slot, src, len) { + if eq(slot, src) { leave } + + let newLen := (src, len) + // Make sure array length is sane + if gt(newLen, 0xffffffffffffffff) { () } + + let oldLen := (sload(slot)) + + let srcOffset := 0 + + srcOffset := 0x20 + + + // This is not needed in all branches. + let dstDataArea + if or(gt(oldLen, 31), gt(newLen, 31)) { + dstDataArea := (slot) + } + + if gt(oldLen, 31) { + // potentially truncate data + let deleteStart := add(dstDataArea, div(add(newLen, 31), 32)) + if lt(newLen, 32) { deleteStart := dstDataArea } + (deleteStart, add(dstDataArea, div(add(oldLen, 31), 32))) + } + switch gt(newLen, 31) + case 1 { + let loopEnd := and(newLen, not(0x1f)) + src := (src) + let dstPtr := dstDataArea + let i := 0 + for { } lt(i, loopEnd) { i := add(i, 0x20) } { + sstore(dstPtr, (add(src, srcOffset))) + dstPtr := add(dstPtr, 1) + srcOffset := add(srcOffset, ) + } + if lt(loopEnd, newLen) { + let lastValue := (add(src, srcOffset)) + sstore(dstPtr, (lastValue, and(newLen, 0x1f))) + } + sstore(slot, add(mul(newLen, 2), 1)) + } + default { + let value := 0 + if newLen { + value := (add(src, srcOffset)) + } + sstore(slot, (value, newLen)) + } + } + )"); + templ("functionName", functionName); + bool fromStorage = _fromType.dataStoredIn(DataLocation::Storage); + templ("fromStorage", fromStorage); + bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData); + templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory)); + templ("fromCalldata", fromCalldata); + templ("arrayLength", arrayLengthFunction(_fromType)); + templ("panic", panicFunction(PanicCode::ResourceError)); + templ("byteArrayLength", extractByteArrayLengthFunction()); + templ("dstDataLocation", arrayDataAreaFunction(_toType)); + if (fromStorage) + templ("srcDataLocation", arrayDataAreaFunction(_fromType)); + templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType())); + templ("srcIncrement", to_string(fromStorage ? 1 : 0x20)); + templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload"); + templ("maskBytes", maskBytesFunctionDynamic()); + templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction()); + + return templ.render(); + }); +} + + +string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType) +{ + solAssert( + *_fromType.copyForLocation(_toType.location(), _toType.isPointer()) == dynamic_cast(_toType), + "" + ); + solAssert(!_fromType.isByteArray(), ""); + solAssert(_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType(), ""); + solAssert(_toType.dataStoredIn(DataLocation::Storage), ""); + + solUnimplementedAssert(_fromType.storageStride() == _toType.storageStride(), ""); + + string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier(); + return m_functionCollector.createFunction(functionName, [&](){ + Whiskers templ(R"( + function (dst, src) { + if eq(dst, src) { leave } + let length := (src) + // Make sure array length is sane + if gt(length, 0xffffffffffffffff) { () } + (dst, length) + + let srcPtr := (src) + + let dstPtr := (dst) + + let fullSlots := div(length, ) + let i := 0 + for { } lt(i, fullSlots) { i := add(i, 1) } { + sstore(add(dstPtr, i), (sload(add(srcPtr, i)))) + } + let spill := sub(length, mul(i, )) + if gt(spill, 0) { + sstore(add(dstPtr, i), (sload(add(srcPtr, i)), mul(spill, ))) + } + } + )"); + if (_fromType.dataStoredIn(DataLocation::Storage)) + solAssert(!_fromType.isValueType(), ""); + templ("functionName", functionName); + templ("resizeArray", resizeArrayFunction(_toType)); + templ("arrayLength",arrayLengthFunction(_fromType)); + templ("panic", panicFunction(PanicCode::ResourceError)); + templ("srcDataLocation", arrayDataAreaFunction(_fromType)); + templ("dstDataLocation", arrayDataAreaFunction(_toType)); + unsigned itemsPerSlot = 32 / _toType.storageStride(); + templ("itemsPerSlot", to_string(itemsPerSlot)); + templ("bytesPerItem", to_string(_toType.storageStride())); + templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride())); + templ("maskBytes", maskLowerOrderBytesFunctionDynamic()); + + return templ.render(); + }); +} + + string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type) { string functionName = "array_convert_length_to_size_" + _type.identifier(); @@ -993,7 +1914,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) Whiskers w(R"( function (length) -> size { // Make sure we can allocate memory without overflow - if gt(length, 0xffffffffffffffff) { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { () } // round up size := and(add(length, 0x1f), not(0x1f)) @@ -1007,6 +1928,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) } )"); w("functionName", functionName); + w("panic", panicFunction(PanicCode::ResourceError)); w("byteArray", _type.isByteArray()); w("dynamic", _type.isDynamicallySized()); return w.render(); @@ -1051,7 +1973,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) return Whiskers(R"( function (array, index) -> slot, offset { let arrayLength := (array) - if iszero(lt(index, arrayLength)) { invalid() } + if iszero(lt(index, arrayLength)) { () } @@ -1065,10 +1987,9 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) slot := array } - let itemsPerSlot := div(0x20, ) let dataArea := (array) - slot := add(dataArea, div(index, itemsPerSlot)) - offset := mod(index, itemsPerSlot) + slot := add(dataArea, div(index, )) + offset := mul(mod(index, ), ) let dataArea := (array) @@ -1078,12 +1999,14 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) } )") ("functionName", functionName) + ("panic", panicFunction(PanicCode::ArrayOutOfBounds)) ("arrayLen", arrayLengthFunction(_type)) ("dataAreaFunc", arrayDataAreaFunction(_type)) ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16) ("isBytesArray", _type.isByteArray()) ("storageSize", _type.baseType()->storageSize().str()) ("storageBytes", toString(_type.baseType()->storageBytes())) + ("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes())) .render(); }); } @@ -1095,7 +2018,7 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type) return Whiskers(R"( function (baseRef, index) -> addr { if iszero(lt(index, (baseRef))) { - invalid() + () } let offset := mul(index, ) @@ -1106,6 +2029,7 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type) } )") ("functionName", functionName) + ("panic", panicFunction(PanicCode::ArrayOutOfBounds)) ("arrayLen", arrayLengthFunction(_type)) ("stride", to_string(_type.memoryStride())) ("dynamicallySized", _type.isDynamicallySized()) @@ -1120,7 +2044,7 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (base_ref, length, index) -> addr, len { - if iszero(lt(index, length)) { invalid() } + if iszero(lt(index, length)) { () } addr := add(base_ref, mul(index, )) addr, len := (base_ref, addr) @@ -1128,6 +2052,7 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type } )") ("functionName", functionName) + ("panic", panicFunction(PanicCode::ArrayOutOfBounds)) ("stride", to_string(_type.calldataStride())) ("dynamicallySized", _type.isDynamicallySized()) ("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded()) @@ -1227,6 +2152,70 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) }); } +string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to) +{ + solAssert(_from.dataStoredIn(DataLocation::Storage), ""); + solAssert(_to.dataStoredIn(DataLocation::Memory), ""); + solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), ""); + if (!_from.isDynamicallySized()) + solAssert(_from.length() == _to.length(), ""); + + string functionName = "copy_array_from_storage_to_memory_" + _from.identifier(); + + return m_functionCollector.createFunction(functionName, [&]() { + if (_from.baseType()->isValueType()) + { + solAssert(_from.baseType() == _to.baseType(), ""); + ABIFunctions abi(m_evmVersion, m_revertStrings, m_functionCollector); + return Whiskers(R"( + function (slot) -> memptr { + memptr := () + let end := (slot, memptr) + mstore(, end) + } + )") + ("functionName", functionName) + ("allocateTemp", allocationTemporaryMemoryFunction()) + ( + "encode", + abi.abiEncodeAndReturnUpdatedPosFunction(_from, _to, ABIFunctions::EncodingOptions{}) + ) + ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) + .render(); + } + else + { + solAssert(_to.memoryStride() == 32, ""); + solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), ""); + solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), ""); + solAssert(!_from.isByteArray(), ""); + solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, ""); + return Whiskers(R"( + function (slot) -> memptr { + let length := (slot) + memptr := (length) + let mpos := memptr + mpos := add(mpos, 0x20) + let spos := (slot) + for { let i := 0 } lt(i, length) { i := add(i, 1) } { + mstore(mpos, (spos)) + mpos := add(mpos, 0x20) + spos := add(spos, ) + } + } + )") + ("functionName", functionName) + ("lengthFunction", arrayLengthFunction(_from)) + ("allocateArray", allocateMemoryArrayFunction(_to)) + ("arrayDataArea", arrayDataAreaFunction(_from)) + ("dynamic", _to.isDynamicallySized()) + ("convert", conversionFunction(*_from.baseType(), *_to.baseType())) + ("baseStorageSize", _from.baseType()->storageSize().str()) + .render(); + } + }); +} + string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType) { solAssert(_keyType.sizeOnStack() <= 1, ""); @@ -1271,46 +2260,105 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes) { - if (_type.category() == Type::Category::Function) - solUnimplementedAssert(!_splitFunctionTypes, ""); + if (_type.isValueType()) + return readFromStorageValueType(_type, _offset, _splitFunctionTypes); + else + { + solAssert(_offset == 0, ""); + return readFromStorageReferenceType(_type); + } +} + +string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes) +{ + solAssert(_type.isValueType(), ""); + return readFromStorageValueType(_type, {}, _splitFunctionTypes); +} + +string YulUtilFunctions::readFromStorageValueType(Type const& _type, optional _offset, bool _splitFunctionTypes) +{ + solAssert(_type.isValueType(), ""); + string functionName = - "read_from_storage_" + - string(_splitFunctionTypes ? "split_" : "") + - "offset_" + - to_string(_offset) + - "_" + - _type.identifier(); + "read_from_storage_" + + string(_splitFunctionTypes ? "split_" : "") + ( + _offset.has_value() ? + "offset_" + to_string(*_offset) : + "dynamic" + ) + + "_" + + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&] { - solAssert(_type.sizeOnStack() == 1, ""); - return Whiskers(R"( - function (slot) -> value { - value := (sload(slot)) + Whiskers templ(R"( + function (slot, offset) -> addr, selectorvalue { + let value := (sload(slot), offset) + + addr, selector := (value) + } - )") - ("functionName", functionName) - ("extract", extractFromStorageValue(_type, _offset, false)) - .render(); + )"); + templ("functionName", functionName); + templ("dynamic", !_offset.has_value()); + if (_offset.has_value()) + templ("extract", extractFromStorageValue(_type, *_offset)); + else + templ("extract", extractFromStorageValueDynamic(_type)); + auto const* funType = dynamic_cast(&_type); + bool split = _splitFunctionTypes && funType && funType->kind() == FunctionType::Kind::External; + templ("split", split); + if (split) + templ("splitFunction", splitExternalFunctionIdFunction()); + return templ.render(); }); } -string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes) +string YulUtilFunctions::readFromStorageReferenceType(Type const& _type) { - if (_type.category() == Type::Category::Function) - solUnimplementedAssert(!_splitFunctionTypes, ""); - string functionName = - "read_from_storage_dynamic" + - string(_splitFunctionTypes ? "split_" : "") + - "_" + - _type.identifier(); + solUnimplementedAssert(_type.category() == Type::Category::Struct, ""); + + string functionName = "read_from_storage_reference_type_" + _type.identifier(); + + auto const& structType = dynamic_cast(_type); + solAssert(structType.location() == DataLocation::Memory, ""); + MemberList::MemberMap structMembers = structType.nativeMembers(nullptr); + vector> memberSetValues(structMembers.size()); + for (size_t i = 0; i < structMembers.size(); ++i) + { + auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name); + + memberSetValues[i]["setMember"] = Whiskers(R"( + { + let := (add(slot, ), ) + (add(value, ), ) + } + )") + ("memberValues", suffixedVariableNameList("memberValue_", 0, structMembers[i].type->stackItems().size())) + ("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str()) + ("memberSlotDiff", memberSlotDiff.str()) + ("memberStorageOffset", to_string(memberStorageOffset)) + ("readFromStorage", + structMembers[i].type->isValueType() ? + readFromStorageDynamic(*structMembers[i].type, true) : + readFromStorage(*structMembers[i].type, memberStorageOffset, true) + ) + ("writeToMemory", writeToMemoryFunction(*structMembers[i].type)) + ("hasOffset", structMembers[i].type->isValueType()) + .render(); + } + return m_functionCollector.createFunction(functionName, [&] { - solAssert(_type.sizeOnStack() == 1, ""); return Whiskers(R"( - function (slot, offset) -> value { - value := (sload(slot), offset) + function (slot) -> value { + value := () + <#member> + + } )") ("functionName", functionName) - ("extract", extractFromStorageValueDynamic(_type, _splitFunctionTypes)) + ("allocStruct", allocateMemoryStructFunction(structType)) + ("member", memberSetValues) .render(); }); } @@ -1325,52 +2373,196 @@ string YulUtilFunctions::readFromCalldata(Type const& _type) return readFromMemoryOrCalldata(_type, true); } -string YulUtilFunctions::updateStorageValueFunction(Type const& _type, std::optional const& _offset) +string YulUtilFunctions::updateStorageValueFunction( + Type const& _fromType, + Type const& _toType, + std::optional const& _offset +) { string const functionName = "update_storage_value_" + (_offset.has_value() ? ("offset_" + to_string(*_offset)) : "") + - _type.identifier(); + _fromType.identifier() + + "_to_" + + _toType.identifier(); return m_functionCollector.createFunction(functionName, [&] { - if (_type.isValueType()) + if (_toType.isValueType()) { - solAssert(_type.storageBytes() <= 32, "Invalid storage bytes size."); - solAssert(_type.storageBytes() > 0, "Invalid storage bytes size."); + solAssert(_fromType.isImplicitlyConvertibleTo(_toType), ""); + solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size."); + solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size."); return Whiskers(R"( - function (slot, value) { - sstore(slot, (sload(slot), (value))) + function (slot, ) { + let := () + sstore(slot, (sload(slot), ())) } )") ("functionName", functionName) ("update", _offset.has_value() ? - updateByteSliceFunction(_type.storageBytes(), *_offset) : - updateByteSliceFunctionDynamic(_type.storageBytes()) + updateByteSliceFunction(_toType.storageBytes(), *_offset) : + updateByteSliceFunctionDynamic(_toType.storageBytes()) ) ("offset", _offset.has_value() ? "" : "offset, ") - ("prepare", prepareStoreFunction(_type)) + ("convert", conversionFunction(_fromType, _toType)) + ("fromValues", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack())) + ("toValues", suffixedVariableNameList("convertedValue_", 0, _toType.sizeOnStack())) + ("prepare", prepareStoreFunction(_toType)) .render(); } + + auto const* toReferenceType = dynamic_cast(&_toType); + auto const* fromReferenceType = dynamic_cast(&_fromType); + solAssert(fromReferenceType && toReferenceType, ""); + solAssert(*toReferenceType->copyForLocation( + fromReferenceType->location(), + fromReferenceType->isPointer() + ).get() == *fromReferenceType, ""); + solAssert(toReferenceType->category() == fromReferenceType->category(), ""); + + if (_toType.category() == Type::Category::Array) + { + solAssert(_offset.value_or(0) == 0, ""); + + Whiskers templ(R"( + function (slot, offset, ) { + if offset { () } + (slot, ) + } + )"); + templ("functionName", functionName); + templ("dynamicOffset", !_offset.has_value()); + templ("panic", panicFunction(PanicCode::Generic)); + templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack())); + templ("copyArrayToStorage", copyArrayToStorageFunction( + dynamic_cast(_fromType), + dynamic_cast(_toType) + )); + + return templ.render(); + } else { - if (_type.category() == Type::Category::Array) - solUnimplementedAssert(false, ""); - else if (_type.category() == Type::Category::Struct) - solUnimplementedAssert(false, ""); - else - solAssert(false, "Invalid non-value type for assignment."); + solAssert(_toType.category() == Type::Category::Struct, ""); + + auto const& fromStructType = dynamic_cast(_fromType); + auto const& toStructType = dynamic_cast(_toType); + solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); + solAssert(_offset.value_or(0) == 0, ""); + + Whiskers templ(R"( + function (slot, offset, value) { + if offset { () } + if eq(slot, value) { leave } + <#member> + { + + } + + } + )"); + templ("functionName", functionName); + templ("dynamicOffset", !_offset.has_value()); + templ("panic", panicFunction(PanicCode::Generic)); + templ("fromStorage", fromStructType.dataStoredIn(DataLocation::Storage)); + + MemberList::MemberMap structMembers = fromStructType.nativeMembers(nullptr); + MemberList::MemberMap toStructMembers = toStructType.nativeMembers(nullptr); + + vector> memberParams(structMembers.size()); + for (size_t i = 0; i < structMembers.size(); ++i) + { + Type const& memberType = *structMembers[i].type; + solAssert(memberType.memoryHeadSize() == 32, ""); + auto const& [slotDiff, offset] = toStructType.storageOffsetsOfMember(structMembers[i].name); + + Whiskers t(R"( + let memberSlot := add(slot, ) + let memberSrcPtr := add(value, ) + + + let := + + (value, memberSrcPtr) + + memberSrcPtr + + + + := () + + + + + let := (memberSrcPtr) + + + + let := + + (memberSrcPtr) + + memberSrcPtr + + + + (memberSlot, ) + )"); + bool fromCalldata = fromStructType.location() == DataLocation::CallData; + t("fromCalldata", fromCalldata); + bool fromMemory = fromStructType.location() == DataLocation::Memory; + t("fromMemory", fromMemory); + bool fromStorage = fromStructType.location() == DataLocation::Storage; + t("fromStorage", fromStorage); + t("isValueType", memberType.isValueType()); + t("memberValues", suffixedVariableNameList("memberValue_", 0, memberType.stackItems().size())); + + t("memberStorageSlotDiff", slotDiff.str()); + if (fromCalldata) + { + t("memberOffset", to_string(fromStructType.calldataOffsetOfMember(structMembers[i].name))); + t("dynamicallyEncodedMember", memberType.isDynamicallyEncoded()); + if (memberType.isDynamicallyEncoded()) + t("accessCalldataTail", accessCalldataTailFunction(memberType)); + if (memberType.isValueType()) + t("read", readFromCalldata(memberType)); + } + else if (fromMemory) + { + t("memberOffset", fromStructType.memoryOffsetOfMember(structMembers[i].name).str()); + t("read", readFromMemory(memberType)); + } + else if (fromStorage) + { + auto [srcSlotOffset, srcOffset] = fromStructType.storageOffsetsOfMember(structMembers[i].name); + t("memberOffset", formatNumber(srcSlotOffset)); + if (memberType.isValueType()) + t("read", readFromStorageValueType(memberType, srcOffset, false)); + else + solAssert(srcOffset == 0, ""); + + } + t("memberStorageSlotOffset", to_string(offset)); + t("updateStorageValue", updateStorageValueFunction( + memberType, + *toStructMembers[i].type, + optional{offset} + )); + memberParams[i]["updateMemberCall"] = t.render(); + } + templ("member", memberParams); + + return templ.render(); } }); } string YulUtilFunctions::writeToMemoryFunction(Type const& _type) { - string const functionName = - string("write_to_memory_") + - _type.identifier(); + string const functionName = "write_to_memory_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&] { solAssert(!dynamic_cast(&_type), ""); @@ -1424,14 +2616,10 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) }); } -string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes) +string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type) { - if (_type.category() == Type::Category::Function) - solUnimplementedAssert(!_splitFunctionTypes, ""); - string functionName = "extract_from_storage_value_dynamic" + - string(_splitFunctionTypes ? "split_" : "") + _type.identifier(); return m_functionCollector.createFunction(functionName, [&] { return Whiskers(R"( @@ -1441,21 +2629,14 @@ string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool )") ("functionName", functionName) ("shr", shiftRightFunctionDynamic()) - ("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes)) + ("cleanupStorage", cleanupFromStorageFunction(_type)) .render(); }); } -string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes) +string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset) { - solUnimplementedAssert(!_splitFunctionTypes, ""); - - string functionName = - "extract_from_storage_value_" + - string(_splitFunctionTypes ? "split_" : "") + - "offset_" + - to_string(_offset) + - _type.identifier(); + string functionName = "extract_from_storage_value_offset_" + to_string(_offset) + _type.identifier(); return m_functionCollector.createFunction(functionName, [&] { return Whiskers(R"( function (slot_value) -> value { @@ -1464,18 +2645,16 @@ string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offs )") ("functionName", functionName) ("shr", shiftRightFunction(_offset * 8)) - ("cleanupStorage", cleanupFromStorageFunction(_type, _splitFunctionTypes)) + ("cleanupStorage", cleanupFromStorageFunction(_type)) .render(); }); } -string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes) +string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type) { solAssert(_type.isValueType(), ""); - if (_type.category() == Type::Category::Function) - solUnimplementedAssert(!_splitFunctionTypes, ""); - string functionName = string("cleanup_from_storage_") + (_splitFunctionTypes ? "split_" : "") + _type.identifier(); + string functionName = string("cleanup_from_storage_") + _type.identifier(); return m_functionCollector.createFunction(functionName, [&] { Whiskers templ(R"( function (value) -> cleaned { @@ -1492,9 +2671,16 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl return templ.render(); } + bool leftAligned = false; + if ( + _type.category() != Type::Category::Function || + dynamic_cast(_type).kind() == FunctionType::Kind::External + ) + leftAligned = _type.leftAligned(); + if (storageBytes == 32) templ("cleaned", "value"); - else if (_type.leftAligned()) + else if (leftAligned) templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)"); else templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")"); @@ -1505,21 +2691,37 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl string YulUtilFunctions::prepareStoreFunction(Type const& _type) { - solUnimplementedAssert(_type.category() != Type::Category::Function, ""); - string functionName = "prepare_store_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { - Whiskers templ(R"( - function (value) -> ret { - ret := - } - )"); - templ("functionName", functionName); - if (_type.category() == Type::Category::FixedBytes) - templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)"); + solAssert(_type.isValueType(), ""); + auto const* funType = dynamic_cast(&_type); + if (funType && funType->kind() == FunctionType::Kind::External) + { + Whiskers templ(R"( + function (addr, selector) -> ret { + ret := ((addr, selector)) + } + )"); + templ("functionName", functionName); + templ("prepareBytes", prepareStoreFunction(*TypeProvider::fixedBytes(24))); + templ("combine", combineExternalFunctionIdFunction()); + return templ.render(); + } else - templ("actualPrepare", "value"); - return templ.render(); + { + solAssert(_type.sizeOnStack() == 1, ""); + Whiskers templ(R"( + function (value) -> ret { + ret := + } + )"); + templ("functionName", functionName); + if (_type.category() == Type::Category::FixedBytes) + templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)"); + else + templ("actualPrepare", "value"); + return templ.render(); + } }); } @@ -1532,12 +2734,13 @@ string YulUtilFunctions::allocationFunction() memPtr := mload() let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { () } mstore(, newFreePtr) } )") - ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) ("functionName", functionName) + ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) + ("panic", panicFunction(PanicCode::ResourceError)) .render(); }); } @@ -1731,31 +2934,33 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) _to.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( - function (addr, functionId) -> outAddr, outFunctionId { - outAddr := addr + function (addr, functionId) -> outAddr, outFunctionId { + outAddr := addr outFunctionId := functionId } )") ("functionName", functionName) + ("external", fromType.kind() == FunctionType::Kind::External) .render(); }); } - - if (_from.category() == Type::Category::ArraySlice) + else if (_from.category() == Type::Category::ArraySlice) { - solAssert(_from.isDynamicallySized(), ""); - solAssert(_from.dataStoredIn(DataLocation::CallData), ""); solAssert(_to.category() == Type::Category::Array, ""); + auto const& fromType = dynamic_cast(_from); + auto const& targetType = dynamic_cast(_to); - ArraySliceType const& fromType = dynamic_cast(_from); - ArrayType const& targetType = dynamic_cast(_to); - - solAssert(!fromType.arrayType().baseType()->isDynamicallyEncoded(), ""); + solAssert(fromType.arrayType().isImplicitlyConvertibleTo(targetType), ""); solAssert( - *fromType.arrayType().baseType() == *targetType.baseType(), - "Converting arrays of different type is not possible" + fromType.arrayType().dataStoredIn(DataLocation::CallData) && + fromType.arrayType().isDynamicallySized() && + !fromType.arrayType().baseType()->isDynamicallyEncoded(), + "" ); + if (!targetType.dataStoredIn(DataLocation::CallData)) + return arrayConversionFunction(fromType.arrayType(), targetType); + string const functionName = "convert_" + _from.identifier() + @@ -1772,6 +2977,14 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) .render(); }); } + else if (_from.category() == Type::Category::Array) + { + solAssert(_to.category() == Type::Category::Array, ""); + return arrayConversionFunction( + dynamic_cast(_from), + dynamic_cast(_to) + ); + } if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1) return conversionFunctionSpecial(_from, _to); @@ -1878,41 +3091,45 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) case Type::Category::FixedPoint: solUnimplemented("Fixed point types not implemented."); break; - case Type::Category::Array: + case Type::Category::Struct: { - if (_from == _to) + solAssert(toCategory == Type::Category::Struct, ""); + auto const& fromStructType = dynamic_cast(_from); + auto const& toStructType = dynamic_cast(_to); + solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), ""); + + if (fromStructType.location() == toStructType.location() && toStructType.isPointer()) body = "converted := value"; else { - ArrayType const& from = dynamic_cast(_from); - ArrayType const& to = dynamic_cast(_to); - - switch (to.location()) + solUnimplementedAssert(toStructType.location() == DataLocation::Memory, ""); + solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, ""); + + if (fromStructType.location() == DataLocation::CallData) + body = Whiskers(R"( + converted := (value, calldatasize()) + )") + ( + "abiDecode", + ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).abiDecodingFunctionStruct( + toStructType, + false + ) + ).render(); + else { - case DataLocation::Storage: - // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. - solAssert( - (to.isPointer() || (from.isByteArray() && to.isByteArray())) && - from.location() == DataLocation::Storage, - "Invalid conversion to storage type." - ); - body = "converted := value"; - break; - case DataLocation::Memory: - // Copy the array to a free position in memory, unless it is already in memory. - solUnimplementedAssert(from.location() == DataLocation::Memory, "Not implemented yet."); - body = "converted := value"; - break; - case DataLocation::CallData: - solUnimplemented("Conversion of calldata types not yet implemented."); - break; + solAssert(fromStructType.location() == DataLocation::Storage, ""); + + body = Whiskers(R"( + converted := (value) + )") + ("readFromStorage", readFromStorage(toStructType, 0, true)) + .render(); } } + break; } - case Type::Category::Struct: - solUnimplementedAssert(false, "Struct conversion not implemented."); - break; case Type::Category::FixedBytes: { FixedBytesType const& from = dynamic_cast(_from); @@ -1958,8 +3175,27 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) solUnimplementedAssert(false, "Tuple conversion not implemented."); break; } + case Type::Category::TypeType: + { + TypeType const& typeType = dynamic_cast(_from); + if ( + auto const* contractType = dynamic_cast(typeType.actualType()); + contractType->contractDefinition().isLibrary() && + _to == *TypeProvider::address() + ) + body = "converted := value"; + else + solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName()); + break; + } + case Type::Category::Mapping: + { + solAssert(_from == _to, ""); + body = "converted := value"; + break; + } default: - solAssert(false, ""); + solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName()); } solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName()); @@ -1968,6 +3204,78 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) }); } +string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayType const& _to) +{ + solAssert(_to.location() != DataLocation::CallData, ""); + + // Other cases are done explicitly in LValue::storeValue, and only possible by assignment. + if (_to.location() == DataLocation::Storage) + solAssert( + (_to.isPointer() || (_from.isByteArray() && _to.isByteArray())) && + _from.location() == DataLocation::Storage, + "Invalid conversion to storage type." + ); + + string functionName = + "convert_array_" + + _from.identifier() + + "_to_" + + _to.identifier(); + + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers templ(R"( + function (value, length) -> converted { + + } + )"); + templ("functionName", functionName); + templ("fromCalldataDynamic", _from.dataStoredIn(DataLocation::CallData) && _from.isDynamicallySized()); + + if ( + _from == _to || + (_from.dataStoredIn(DataLocation::Memory) && _to.dataStoredIn(DataLocation::Memory)) || + _to.dataStoredIn(DataLocation::Storage) + ) + templ("body", "converted := value"); + else if (_to.dataStoredIn(DataLocation::Memory)) + templ( + "body", + Whiskers(R"( + // Copy the array to a free position in memory + converted := + + (value) + + + (value, , calldatasize()) + + )") + ("fromStorage", _from.dataStoredIn(DataLocation::Storage)) + ("fromCalldata", _from.dataStoredIn(DataLocation::CallData)) + ("length", _from.isDynamicallySized() ? "length" : _from.length().str()) + ( + "abiDecode", + _from.dataStoredIn(DataLocation::CallData) ? + ABIFunctions( + m_evmVersion, + m_revertStrings, + m_functionCollector + ).abiDecodingFunctionArrayAvailableLength(_to, false) : + "" + ) + ( + "arrayStorageToMem", + _from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : "" + ) + .render() + ); + else + solAssert(false, ""); + + return templ.render(); + }); +} + string YulUtilFunctions::cleanupFunction(Type const& _type) { string functionName = string("cleanup_") + _type.identifier(); @@ -2051,7 +3359,7 @@ string YulUtilFunctions::cleanupFunction(Type const& _type) case Type::Category::Enum: { // Out of range enums cannot be truncated unambigiously and therefore it should be an error. - templ("body", "cleaned := value " + validatorFunction(_type) + "(value)"); + templ("body", "cleaned := value " + validatorFunction(_type, false) + "(value)"); break; } case Type::Category::InaccessibleDynamic: @@ -2075,10 +3383,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail } )"); templ("functionName", functionName); - if (_revertOnFailure) - templ("failure", "revert(0, 0)"); - else - templ("failure", "invalid()"); + PanicCode panicCode = PanicCode::Generic; switch (_type.category()) { @@ -2105,6 +3410,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail { size_t members = dynamic_cast(_type).numberOfMembers(); solAssert(members > 0, "empty enum should have caused a parser error."); + panicCode = PanicCode::EnumConversionError; templ("condition", "lt(value, " + to_string(members) + ")"); break; } @@ -2115,6 +3421,11 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail solAssert(false, "Validation of type " + _type.identifier() + " requested."); } + if (_revertOnFailure) + templ("failure", "revert(0, 0)"); + else + templ("failure", panicFunction(panicCode) + "()"); + return templ.render(); }); } @@ -2177,84 +3488,126 @@ string YulUtilFunctions::forwardingRevertFunction() std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type) { + solAssert(_type.category() == Type::Category::Integer, ""); IntegerType const& type = dynamic_cast(_type); string const functionName = "decrement_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { - u256 minintval; - - // Smallest admissible value to decrement - if (type.isSigned()) - minintval = 0 - (u256(1) << (type.numBits() - 1)) + 1; - else - minintval = 1; - return Whiskers(R"( function (value) -> ret { value := (value) - if (value, ) { revert(0,0) } + if eq(value, ) { () } ret := sub(value, 1) } )") ("functionName", functionName) - ("minval", toCompactHexWithPrefix(minintval)) - ("lt", type.isSigned() ? "slt" : "lt") + ("panic", panicFunction(PanicCode::UnderOverflow)) + ("minval", toCompactHexWithPrefix(type.min())) ("cleanupFunction", cleanupFunction(_type)) .render(); }); } -std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type) +std::string YulUtilFunctions::decrementWrappingFunction(Type const& _type) { + solAssert(_type.category() == Type::Category::Integer, ""); IntegerType const& type = dynamic_cast(_type); - string const functionName = "increment_" + _type.identifier(); + string const functionName = "decrement_wrapping_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { - u256 maxintval; + return Whiskers(R"( + function (value) -> ret { + ret := (sub(value, 1)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(type)) + .render(); + }); +} - // Biggest admissible value to increment - if (type.isSigned()) - maxintval = (u256(1) << (type.numBits() - 1)) - 2; - else - maxintval = (u256(1) << type.numBits()) - 2; +std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type) +{ + solAssert(_type.category() == Type::Category::Integer, ""); + IntegerType const& type = dynamic_cast(_type); + + string const functionName = "increment_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value) -> ret { value := (value) - if (value, ) { revert(0,0) } + if eq(value, ) { () } ret := add(value, 1) } )") ("functionName", functionName) - ("maxval", toCompactHexWithPrefix(maxintval)) - ("gt", type.isSigned() ? "sgt" : "gt") + ("maxval", toCompactHexWithPrefix(type.max())) + ("panic", panicFunction(PanicCode::UnderOverflow)) ("cleanupFunction", cleanupFunction(_type)) .render(); }); } +std::string YulUtilFunctions::incrementWrappingFunction(Type const& _type) +{ + solAssert(_type.category() == Type::Category::Integer, ""); + IntegerType const& type = dynamic_cast(_type); + + string const functionName = "increment_wrapping_" + _type.identifier(); + + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (value) -> ret { + ret := (add(value, 1)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(type)) + .render(); + }); +} + string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type) { + solAssert(_type.category() == Type::Category::Integer, ""); IntegerType const& type = dynamic_cast(_type); solAssert(type.isSigned(), "Expected signed type!"); string const functionName = "negate_" + _type.identifier(); - - u256 const minintval = 0 - (u256(1) << (type.numBits() - 1)) + 1; - return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (value) -> ret { value := (value) - if slt(value, ) { revert(0,0) } + if eq(value, ) { () } ret := sub(0, value) } )") ("functionName", functionName) - ("minval", toCompactHexWithPrefix(minintval)) + ("minval", toCompactHexWithPrefix(type.min())) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction(PanicCode::UnderOverflow)) + .render(); + }); +} + +string YulUtilFunctions::negateNumberWrappingFunction(Type const& _type) +{ + solAssert(_type.category() == Type::Category::Integer, ""); + IntegerType const& type = dynamic_cast(_type); + solAssert(type.isSigned(), "Expected signed type!"); + + string const functionName = "negate_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (value) -> ret { + value := (sub(0, value))) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(type)) .render(); }); } @@ -2346,17 +3699,30 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type) } )") ("functionName", functionName) - ("store", updateStorageValueFunction(_type)) + ("store", updateStorageValueFunction(_type, _type)) ("zeroValue", zeroValueFunction(_type)) .render(); else if (_type.category() == Type::Category::Array) return Whiskers(R"( function (slot, offset) { + if iszero(eq(offset, 0)) { () } (slot) } )") ("functionName", functionName) ("clearArray", clearStorageArrayFunction(dynamic_cast(_type))) + ("panic", panicFunction(PanicCode::Generic)) + .render(); + else if (_type.category() == Type::Category::Struct) + return Whiskers(R"( + function (slot, offset) { + if iszero(eq(offset, 0)) { () } + (slot) + } + )") + ("functionName", functionName) + ("clearStruct", clearStorageStructFunction(dynamic_cast(_type))) + ("panic", panicFunction(PanicCode::Generic)) .render(); else solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!"); @@ -2410,29 +3776,6 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const .render(); } - if (_from.category() == Type::Category::Array && _to.category() == Type::Category::Array) - { - auto const& fromArrayType = dynamic_cast(_from); - auto const& toArrayType = dynamic_cast(_to); - - solAssert(!fromArrayType.baseType()->isDynamicallyEncoded(), ""); - solUnimplementedAssert(fromArrayType.isByteArray() && toArrayType.isByteArray(), ""); - solUnimplementedAssert(toArrayType.location() == DataLocation::Memory, ""); - solUnimplementedAssert(fromArrayType.location() == DataLocation::CallData, ""); - solUnimplementedAssert(toArrayType.isDynamicallySized(), ""); - - Whiskers templ(R"( - function (offset, length) -> converted { - converted := (length) - (offset, add(converted, 0x20), length) - } - )"); - templ("functionName", functionName); - templ("allocateMemoryArray", allocateMemoryArrayFunction(toArrayType)); - templ("copyToMemory", copyToMemoryFunction(fromArrayType.location() == DataLocation::CallData)); - return templ.render(); - } - solUnimplementedAssert( _from.category() == Type::Category::StringLiteral, "Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented." @@ -2518,37 +3861,44 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC } solAssert(_type.isValueType(), ""); - if (auto const* funType = dynamic_cast(&_type)) - if (funType->kind() == FunctionType::Kind::External) - return Whiskers(R"( - function (memPtr) -> addr, selector { - let combined := (memPtr) - addr, selector := (combined) - } - )") - ("functionName", functionName) - ("load", _fromCalldata ? "calldataload" : "mload") - ("splitFunction", splitExternalFunctionIdFunction()) - .render(); - - - return Whiskers(R"( - function (ptr) -> value { + Whiskers templ(R"( + function (ptr) -> { - value := calldataload(ptr) + let value := calldataload(ptr) (value) - value := (mload(ptr)) + let value := (mload(ptr)) + + := + + (value) + + value + } - )") - ("functionName", functionName) - ("fromCalldata", _fromCalldata) - ("validate", validatorFunction(_type)) + )"); + templ("functionName", functionName); + templ("fromCalldata", _fromCalldata); + if (_fromCalldata) + templ("validate", validatorFunction(_type, true)); + auto const* funType = dynamic_cast(&_type); + if (funType && funType->kind() == FunctionType::Kind::External) + { + templ("externalFunction", true); + templ("splitFunction", splitExternalFunctionIdFunction()); + templ("returnVariables", "addr, selector"); + } + else + { + templ("externalFunction", false); + templ("returnVariables", "returnValue"); + } + // Byte array elements generally need cleanup. // Other types are cleaned as well to account for dirty memory e.g. due to inline assembly. - ("cleanup", cleanupFunction(_type)) - .render(); + templ("cleanup", cleanupFunction(_type)); + return templ.render(); }); } @@ -2566,7 +3916,7 @@ string YulUtilFunctions::revertReasonIfDebug(RevertStrings revertStrings, string revert(0, add(reasonPos, )) })"); - templ("sig", (u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str()); + templ("sig", util::selectorFromSignature("Error(string)").str()); templ("length", to_string(_message.length())); size_t words = (_message.length() + 31) / 32; @@ -2590,6 +3940,24 @@ string YulUtilFunctions::revertReasonIfDebug(string const& _message) return revertReasonIfDebug(m_revertStrings, _message); } +string YulUtilFunctions::panicFunction(util::PanicCode _code) +{ + string functionName = "panic_error_" + toCompactHexWithPrefix(uint64_t(_code)); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function () { + mstore(0, ) + mstore(4, ) + revert(0, 0x24) + } + )") + ("functionName", functionName) + ("selector", util::selectorFromSignature("Panic(uint256)").str()) + ("code", toCompactHexWithPrefix(_code)) + .render(); + }); +} + string YulUtilFunctions::tryDecodeErrorMessageFunction() { string const functionName = "try_decode_error_message"; @@ -2705,3 +4073,21 @@ string YulUtilFunctions::copyConstructorArgumentsToMemoryFunction( .render(); }); } + +string YulUtilFunctions::externalCodeFunction() +{ + string functionName = "external_code_at"; + + return m_functionCollector.createFunction(functionName, [&]() { + return util::Whiskers(R"( + function (addr) -> mpos { + let length := extcodesize(addr) + mpos := (length) + extcodecopy(addr, add(mpos, 0x20), 0, length) + } + )") + ("functionName", functionName) + ("allocateArray", allocateMemoryArrayFunction(*TypeProvider::bytesMemory())) + .render(); + }); +} diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 19ca72dd0073..5831ba2ff643 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -28,6 +28,8 @@ #include +#include + #include #include #include @@ -86,7 +88,6 @@ class YulUtilFunctions /// @returns the name of a function that performs a left shift and subsequent cleanup /// and, if needed, prior cleanup. - /// If the amount to shift by is signed, a check for negativeness is performed. /// signature: (value, amountToShift) -> result std::string typedShiftLeftFunction(Type const& _type, Type const& _amountType); std::string typedShiftRightFunction(Type const& _type, Type const& _amountType); @@ -100,6 +101,20 @@ class YulUtilFunctions /// signature: (value, shiftBytes, toInsert) -> result std::string updateByteSliceFunctionDynamic(size_t _numBytes); + /// Function that sets all but the first ``bytes`` bytes of ``value`` to zero. + /// @note ``bytes`` has to be small enough not to overflow ``8 * bytes``. + /// signature: (value, bytes) -> result + std::string maskBytesFunctionDynamic(); + + /// Zeroes out all bytes above the first ``_bytes`` lower order bytes. + /// signature: (value) -> result + std::string maskLowerOrderBytesFunction(size_t _bytes); + + /// Zeroes out all bytes above the first ``bytes`` lower order bytes. + /// @note ``bytes`` has to be small enough not to overflow ``8 * bytes``. + /// signature: (value, bytes) -> result + std::string maskLowerOrderBytesFunctionDynamic(); + /// @returns the name of a function that rounds its input to the next multiple /// of 32 or the input if it is a multiple of 32. /// signature: (value) -> result @@ -107,32 +122,81 @@ class YulUtilFunctions /// signature: (x, y) -> sum std::string overflowCheckedIntAddFunction(IntegerType const& _type); + /// signature: (x, y) -> sum + std::string wrappingIntAddFunction(IntegerType const& _type); /// signature: (x, y) -> product std::string overflowCheckedIntMulFunction(IntegerType const& _type); + /// signature: (x, y) -> product + std::string wrappingIntMulFunction(IntegerType const& _type); /// @returns name of function to perform division on integers. /// Checks for division by zero and the special case of /// signed division of the smallest number by -1. std::string overflowCheckedIntDivFunction(IntegerType const& _type); + /// @returns name of function to perform division on integers. + /// Checks for division by zero. + std::string wrappingIntDivFunction(IntegerType const& _type); /// @returns name of function to perform modulo on integers. /// Reverts for modulo by zero. - std::string checkedIntModFunction(IntegerType const& _type); + std::string intModFunction(IntegerType const& _type); /// @returns computes the difference between two values. /// Assumes the input to be in range for the type. /// signature: (x, y) -> diff std::string overflowCheckedIntSubFunction(IntegerType const& _type); + /// @returns computes the difference between two values. + /// signature: (x, y) -> diff + std::string wrappingIntSubFunction(IntegerType const& _type); + + /// @returns the name of the exponentiation function. + /// signature: (base, exponent) -> power + std::string overflowCheckedIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType); + + /// @returns the name of the exponentiation function, specialized for literal base. + /// signature: exponent -> power + std::string overflowCheckedIntLiteralExpFunction( + RationalNumberType const& _baseType, + IntegerType const& _exponentType, + IntegerType const& _commonType + ); + + /// Generic unsigned checked exponentiation function. + /// Reverts if the result is larger than max. + /// signature: (base, exponent, max) -> power + std::string overflowCheckedUnsignedExpFunction(); + + /// Generic signed checked exponentiation function. + /// Reverts if the result is smaller than min or larger than max. + /// The code relies on max <= |min| and min < 0. + /// signature: (base, exponent, min, max) -> power + std::string overflowCheckedSignedExpFunction(); + + /// Helper function for the two checked exponentiation functions. + /// signature: (power, base, exponent, max) -> power + std::string overflowCheckedExpLoopFunction(); + + /// @returns the name of the exponentiation function. + /// signature: (base, exponent) -> power + std::string wrappingIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType); + /// @returns the name of a function that fetches the length of the given /// array /// signature: (array) -> length std::string arrayLengthFunction(ArrayType const& _type); + /// @returns function name that extracts and returns byte array length from the value + /// stored at the slot. + /// Causes a Panic if the length encoding is wrong. + /// signature: (data) -> length + std::string extractByteArrayLengthFunction(); + /// @returns the name of a function that resizes a storage array + /// for statically sized arrays, it will just clean-up elements of array starting from newLen until the end /// signature: (array, newLen) - std::string resizeDynamicArrayFunction(ArrayType const& _type); + std::string resizeArrayFunction(ArrayType const& _type); /// @returns the name of a function that reduces the size of a storage array by one element /// signature: (array) @@ -155,6 +219,18 @@ class YulUtilFunctions /// signature: (slot) -> std::string clearStorageArrayFunction(ArrayType const& _type); + /// @returns the name of a function that will copy an array to storage + /// signature (to_slot, from_ptr) -> + std::string copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); + + /// @returns the name of a function that will copy a byte array to storage + /// signature (to_slot, from_ptr) -> + std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); + + /// @returns the name of a function that will copy an array of value types from storage to storage. + /// signature (to_slot, from_slot) -> + std::string copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType); + /// Returns the name of a function that will convert a given length to the /// size in memory (number of storage slots or calldata/memory bytes) it /// will require. @@ -201,13 +277,16 @@ class YulUtilFunctions /// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots. std::string nextArrayElementFunction(ArrayType const& _type); + /// @returns the name of a function that allocates a memory array and copies the contents + /// of the storage array into it. + std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to); + /// @returns the name of a function that performs index access for mappings. /// @param _mappingType the type of the mapping /// @param _keyType the type of the value provided std::string mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType); - /// @returns a function that reads a value type from storage. - /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. + /// @returns a function that reads a type from storage. /// @param _splitFunctionTypes if false, returns the address and function signature in a /// single variable. std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes); @@ -224,16 +303,21 @@ class YulUtilFunctions /// @returns a function that extracts a value type from storage slot that has been /// retrieved already. /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. - /// @param _splitFunctionTypes if false, returns the address and function signature in a - /// single variable. - std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes); - std::string extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes); + /// + /// For external function types, input and output is in "compressed"/"unsplit" form. + std::string extractFromStorageValue(Type const& _type, size_t _offset); + std::string extractFromStorageValueDynamic(Type const& _type); /// Returns the name of a function will write the given value to /// the specified slot and offset. If offset is not given, it is expected as /// runtime parameter. + /// For reference types, offset is checked to be zero at runtime. /// signature: (slot, [offset,] value) - std::string updateStorageValueFunction(Type const& _type, std::optional const& _offset = std::optional()); + std::string updateStorageValueFunction( + Type const& _fromType, + Type const& _toType, + std::optional const& _offset = std::optional() + ); /// Returns the name of a function that will write the given value to /// the specified address. @@ -246,9 +330,8 @@ class YulUtilFunctions /// higher order bytes or left-aligns (in case of bytesNN). /// The storage cleanup expects the value to be right-aligned with potentially /// dirty higher order bytes. - /// @param _splitFunctionTypes if false, returns the address and function signature in a - /// single variable. - std::string cleanupFromStorageFunction(Type const& _type, bool _splitFunctionTypes); + /// For external functions, input and output is in "compressed"/"unsplit" form. + std::string cleanupFromStorageFunction(Type const& _type); /// @returns the name of a function that prepares a value of the given type /// for being stored in storage. This usually includes cleanup and right-alignment @@ -321,7 +404,7 @@ class YulUtilFunctions /// otherwise an assertion failure. /// /// This is used for data decoded from external sources. - std::string validatorFunction(Type const& _type, bool _revertOnFailure = false); + std::string validatorFunction(Type const& _type, bool _revertOnFailure); std::string packedHashFunction(std::vector const& _givenTypes, std::vector const& _targetTypes); @@ -330,9 +413,12 @@ class YulUtilFunctions std::string forwardingRevertFunction(); std::string incrementCheckedFunction(Type const& _type); + std::string incrementWrappingFunction(Type const& _type); std::string decrementCheckedFunction(Type const& _type); + std::string decrementWrappingFunction(Type const& _type); std::string negateNumberCheckedFunction(Type const& _type); + std::string negateNumberWrappingFunction(Type const& _type); /// @returns the name of a function that returns the zero value for the /// provided type. @@ -351,6 +437,9 @@ class YulUtilFunctions std::string revertReasonIfDebug(std::string const& _message = ""); + /// Reverts with ``Panic(uint256)`` and the given code. + std::string panicFunction(util::PanicCode _code); + /// Returns the name of a function that decodes an error message. /// signature: () -> arrayPtr /// @@ -371,21 +460,67 @@ class YulUtilFunctions std::string const& _creationObjectName ); + /// @returns the name of a function that copies code from a given address to a newly + /// allocated byte array in memory. + /// Signature: (address) -> mpos + std::string externalCodeFunction(); + private: + /// Special case of conversion functions - handles all array conversions. + std::string arrayConversionFunction(ArrayType const& _from, ArrayType const& _to); + /// Special case of conversionFunction - handles everything that does not /// use exactly one variable to hold the value. std::string conversionFunctionSpecial(Type const& _from, Type const& _to); - /// @returns function name that extracts and returns byte array length - /// signature: (data) -> length - std::string extractByteArrayLengthFunction(); - /// @returns the name of a function that reduces the size of a storage byte array by one element /// signature: (byteArray) std::string storageByteArrayPopFunction(ArrayType const& _type); std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata); + /// @returns a function that reads a value type from storage. + /// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation. + /// @param _splitFunctionTypes if false, returns the address and function signature in a + /// single variable. + /// @param _offset if provided, read from static offset, otherwise offset is a parameter of the Yul function. + std::string readFromStorageValueType(Type const& _type, std::optional _offset, bool _splitFunctionTypes); + + /// @returns a function that reads a reference type from storage to memory (performing a deep copy). + std::string readFromStorageReferenceType(Type const& _type); + + /// @returns the name of a function that will clear given storage slot + /// starting with given offset until the end of the slot + /// signature: (slot, offset) + std::string partialClearStorageSlotFunction(); + + /// @returns the name of a function that will clear the given storage struct + /// signature: (slot) -> + std::string clearStorageStructFunction(StructType const& _type); + + /// @returns the name of a function that resizes a storage byte array + /// signature: (array, newLen) + std::string resizeDynamicByteArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that increases size of byte array + /// when we resize byte array frextractUsedSetLenom < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will occur + /// signature: (array, data, oldLen, newLen) + std::string increaseByteArraySizeFunction(ArrayType const& _type); + + /// @returns the name of a function that decreases size of byte array + /// when we resize byte array from >= 32 elements to < 32 elements or we pop from byte array of size 32 copying of data will occur + /// signature: (array, data, oldLen, newLen) + std::string decreaseByteArraySizeFunction(ArrayType const& _type); + + /// @returns the name of a function that sets size of short byte array while copying data + /// should be called when we resize from long byte array (more than 32 elements) to short byte array + /// signature: (array, data, len) + std::string byteArrayTransitLongToShortFunction(ArrayType const& _type); + + /// @returns the name of a function that extracts only used part of slot that represents short byte array + /// signature: (data, len) -> data + std::string shortByteArrayEncodeUsedAreaSetLengthFunction(); + langutil::EVMVersion m_evmVersion; RevertStrings m_revertStrings; MultiUseYulFunctionCollector& m_functionCollector; diff --git a/libsolidity/codegen/ir/Common.cpp b/libsolidity/codegen/ir/Common.cpp index 42a4650dbbf3..41c711c99ba7 100644 --- a/libsolidity/codegen/ir/Common.cpp +++ b/libsolidity/codegen/ir/Common.cpp @@ -28,7 +28,7 @@ using namespace solidity::frontend; YulArity YulArity::fromType(FunctionType const& _functionType) { return YulArity{ - TupleType(_functionType.parameterTypes()).sizeOnStack(), + TupleType(_functionType.parameterTypesIncludingSelf()).sizeOnStack(), TupleType(_functionType.returnParameterTypes()).sizeOnStack() }; } diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 7476dd90e6f9..99e5fb887b2e 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -128,7 +128,7 @@ void IRGenerationContext::initializeInternalDispatch(InternalDispatchMap _intern { solAssert(internalDispatchClean(), ""); - for (set const& functions: _internalDispatch | boost::adaptors::map_values) + for (DispatchSet const& functions: _internalDispatch | boost::adaptors::map_values) for (auto function: functions) enqueueFunctionForCodeGeneration(*function); @@ -137,39 +137,27 @@ void IRGenerationContext::initializeInternalDispatch(InternalDispatchMap _intern InternalDispatchMap IRGenerationContext::consumeInternalDispatchMap() { - m_directInternalFunctionCalls.clear(); - InternalDispatchMap internalDispatch = move(m_internalDispatchMap); m_internalDispatchMap.clear(); return internalDispatch; } -void IRGenerationContext::internalFunctionCalledDirectly(Expression const& _expression) +void IRGenerationContext::addToInternalDispatch(FunctionDefinition const& _function) { - solAssert(m_directInternalFunctionCalls.count(&_expression) == 0, ""); + FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal); + solAssert(functionType, ""); - m_directInternalFunctionCalls.insert(&_expression); -} + YulArity arity = YulArity::fromType(*functionType); -void IRGenerationContext::internalFunctionAccessed(Expression const& _expression, FunctionDefinition const& _function) -{ - solAssert( - IRHelpers::referencedFunctionDeclaration(_expression) && - _function.resolveVirtual(mostDerivedContract()) == - IRHelpers::referencedFunctionDeclaration(_expression)->resolveVirtual(mostDerivedContract()), - "Function definition does not match the expression" - ); - - if (m_directInternalFunctionCalls.count(&_expression) == 0) - { - FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal); - solAssert(functionType, ""); + if (m_internalDispatchMap.count(arity) != 0 && m_internalDispatchMap[arity].count(&_function) != 0) + // Note that m_internalDispatchMap[arity] is a set with a custom comparator, which looks at function IDs not definitions + solAssert(*m_internalDispatchMap[arity].find(&_function) == &_function, "Different definitions with the same function ID"); - m_internalDispatchMap[YulArity::fromType(*functionType)].insert(&_function); - enqueueFunctionForCodeGeneration(_function); - } + m_internalDispatchMap[arity].insert(&_function); + enqueueFunctionForCodeGeneration(_function); } + void IRGenerationContext::internalFunctionCalledThroughDispatch(YulArity const& _arity) { m_internalDispatchMap.try_emplace(_arity); diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 469beea2fe92..5a36041bece4 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -44,7 +44,20 @@ namespace solidity::frontend class YulUtilFunctions; class ABIFunctions; -using InternalDispatchMap = std::map>; +struct AscendingFunctionIDCompare +{ + bool operator()(FunctionDefinition const* _f1, FunctionDefinition const* _f2) const + { + // NULLs always first. + if (_f1 != nullptr && _f2 != nullptr) + return _f1->id() < _f2->id(); + else + return _f1 == nullptr; + } +}; + +using DispatchSet = std::set; +using InternalDispatchMap = std::map; /** * Class that contains contextual information during IR generation. @@ -98,8 +111,9 @@ class IRGenerationContext void addStateVariable(VariableDeclaration const& _varDecl, u256 _storageOffset, unsigned _byteOffset); bool isStateVariable(VariableDeclaration const& _varDecl) const { return m_stateVariables.count(&_varDecl); } - std::pair storageLocationOfVariable(VariableDeclaration const& _varDecl) const + std::pair storageLocationOfStateVariable(VariableDeclaration const& _varDecl) const { + solAssert(isStateVariable(_varDecl), ""); return m_stateVariables.at(&_varDecl); } @@ -107,7 +121,7 @@ class IRGenerationContext void initializeInternalDispatch(InternalDispatchMap _internalDispatchMap); InternalDispatchMap consumeInternalDispatchMap(); - bool internalDispatchClean() const { return m_internalDispatchMap.empty() && m_directInternalFunctionCalls.empty(); } + bool internalDispatchClean() const { return m_internalDispatchMap.empty(); } /// Notifies the context that a function call that needs to go through internal dispatch was /// encountered while visiting the AST. This ensures that the corresponding dispatch function @@ -115,21 +129,16 @@ class IRGenerationContext /// the code contains a call to an uninitialized function variable). void internalFunctionCalledThroughDispatch(YulArity const& _arity); - /// Notifies the context that a direct function call (i.e. not through internal dispatch) was - /// encountered while visiting the AST. This lets the context know that the function should - /// not be added to the dispatch (unless there are also indirect calls to it elsewhere else). - void internalFunctionCalledDirectly(Expression const& _expression); - - /// Notifies the context that a name representing an internal function has been found while - /// visiting the AST. If the name has not been reported as a direct call using - /// @a internalFunctionCalledDirectly(), it's assumed to represent function variable access - /// and the function gets added to internal dispatch. - void internalFunctionAccessed(Expression const& _expression, FunctionDefinition const& _function); + /// Adds a function to the internal dispatch. + void addToInternalDispatch(FunctionDefinition const& _function); /// @returns a new copy of the utility function generator (but using the same function set). YulUtilFunctions utils(); - langutil::EVMVersion evmVersion() const { return m_evmVersion; }; + langutil::EVMVersion evmVersion() const { return m_evmVersion; } + + void setArithmetic(Arithmetic _value) { m_arithmetic = _value; } + Arithmetic arithmetic() const { return m_arithmetic; } ABIFunctions abiFunctions(); @@ -141,6 +150,9 @@ class IRGenerationContext std::set& subObjectsCreated() { return m_subObjects; } + bool inlineAssemblySeen() const { return m_inlineAssemblySeen; } + void setInlineAssemblySeen() { m_inlineAssemblySeen = true; } + private: langutil::EVMVersion m_evmVersion; RevertStrings m_revertStrings; @@ -157,6 +169,11 @@ class IRGenerationContext std::map> m_stateVariables; MultiUseYulFunctionCollector m_functions; size_t m_varCounter = 0; + /// Whether to use checked or wrapping arithmetic. + Arithmetic m_arithmetic = Arithmetic::Checked; + + /// Flag indicating whether any inline assembly block was seen. + bool m_inlineAssemblySeen = false; /// Function definitions queued for code generation. They're the Solidity functions whose calls /// were discovered by the IR generator during AST traversal. @@ -165,14 +182,13 @@ class IRGenerationContext /// The order and duplicates are irrelevant here (hence std::set rather than std::queue) as /// long as the order of Yul functions in the generated code is deterministic and the same on /// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector. - std::set m_functionGenerationQueue; + DispatchSet m_functionGenerationQueue; /// Collection of functions that need to be callable via internal dispatch. /// Note that having a key with an empty set of functions is a valid situation. It means that /// the code contains a call via a pointer even though a specific function is never assigned to it. /// It will fail at runtime but the code must still compile. InternalDispatchMap m_internalDispatchMap; - std::set m_directInternalFunctionCalls; std::set m_subObjects; }; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index f7f66b4f58a1..56df101f8247 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -50,7 +50,7 @@ using namespace solidity::frontend; pair IRGenerator::run( ContractDefinition const& _contract, - map const& _otherYulSources + map const& _otherYulSources ) { string const ir = yul::reindent(generate(_contract, _otherYulSources)); @@ -78,7 +78,7 @@ pair IRGenerator::run( string IRGenerator::generate( ContractDefinition const& _contract, - map const& _otherYulSources + map const& _otherYulSources ) { auto subObjectSources = [&_otherYulSources](std::set const& subObjects) -> string @@ -92,7 +92,7 @@ string IRGenerator::generate( Whiskers t(R"( object "" { code { - + let := () @@ -103,7 +103,7 @@ string IRGenerator::generate( } object "" { code { - + } @@ -118,7 +118,6 @@ string IRGenerator::generate( m_context.registerImmutableVariable(*var); t("CreationObject", IRNames::creationObject(_contract)); - t("memoryInit", memoryInit()); t("notLibrary", !_contract.isLibrary()); FunctionDefinition const* constructor = _contract.constructor(); @@ -144,6 +143,10 @@ string IRGenerator::generate( t("functions", m_context.functionCollector().requestedFunctions()); t("subObjects", subObjectSources(m_context.subObjectsCreated())); + // This has to be called only after all other code generation for the creation object is complete. + bool creationInvolvesAssembly = m_context.inlineAssemblySeen(); + t("memoryInitCreation", memoryInit(!creationInvolvesAssembly)); + resetContext(_contract); // NOTE: Function pointers can be passed from creation code via storage variables. We need to @@ -158,13 +161,17 @@ string IRGenerator::generate( generateInternalDispatchFunctions(); t("runtimeFunctions", m_context.functionCollector().requestedFunctions()); t("runtimeSubObjects", subObjectSources(m_context.subObjectsCreated())); + + // This has to be called only after all other code generation for the runtime object is complete. + bool runtimeInvolvesAssembly = m_context.inlineAssemblySeen(); + t("memoryInitRuntime", memoryInit(!runtimeInvolvesAssembly)); return t.render(); } string IRGenerator::generate(Block const& _block) { IRGeneratorForStatements generator(m_context, m_utils); - _block.accept(generator); + generator.generate(_block); return generator.code(); } @@ -197,10 +204,11 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions() := () } - default { invalid() } + default { () } } )"); templ("functionName", funName); + templ("panic", m_utils.panicFunction(PanicCode::InvalidInternalFunction)); templ("in", suffixedVariableNameList("in_", 0, arity.in)); templ("out", suffixedVariableNameList("out_", 0, arity.out)); @@ -241,6 +249,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) { string functionName = IRNames::function(_function); return m_context.functionCollector().createFunction(functionName, [&]() { + solUnimplementedAssert(_function.modifiers().empty(), "Modifiers not implemented yet."); Whiskers t(R"( function () -> { @@ -305,7 +314,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) string code; - auto const& location = m_context.storageLocationOfVariable(_varDecl); + auto const& location = m_context.storageLocationOfStateVariable(_varDecl); code += Whiskers(R"( let slot := let offset := @@ -340,18 +349,25 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) mappingType ? *mappingType->keyType() : *TypeProvider::uint256() ).stackSlots(); parameters += keys; - code += Whiskers(R"( + + Whiskers templ(R"( + + if iszero(lt(, (slot))) { revert(0, 0) } + slot, offset := (slot, ) - )") - ( + )"); + templ( "indexAccess", mappingType ? m_utils.mappingIndexAccessFunction(*mappingType, *mappingType->keyType()) : m_utils.storageArrayIndexAccessFunction(*arrayType) ) ("array", arrayType != nullptr) - ("keys", joinHumanReadable(keys)) - .render(); + ("keys", joinHumanReadable(keys)); + if (arrayType) + templ("length", m_utils.arrayLengthFunction(*arrayType)); + + code += templ.render(); currentType = mappingType ? mappingType->valueType() : arrayType->baseType(); } @@ -421,31 +437,46 @@ pair>> IRGenerator::evalua ContractDefinition const& _contract ) { + struct InheritanceOrder + { + bool operator()(ContractDefinition const* _c1, ContractDefinition const* _c2) const + { + solAssert(contains(linearizedBaseContracts, _c1) && contains(linearizedBaseContracts, _c2), ""); + auto it1 = find(linearizedBaseContracts.begin(), linearizedBaseContracts.end(), _c1); + auto it2 = find(linearizedBaseContracts.begin(), linearizedBaseContracts.end(), _c2); + return it1 < it2; + } + vector const& linearizedBaseContracts; + } inheritanceOrder{_contract.annotation().linearizedBaseContracts}; + map> constructorParams; - vector>const *>> baseConstructorArguments; + + map>const *, InheritanceOrder> + baseConstructorArguments(inheritanceOrder); + ; for (ASTPointer const& base: _contract.baseContracts()) if (FunctionDefinition const* baseConstructor = dynamic_cast( base->name().annotation().referencedDeclaration )->constructor(); baseConstructor && base->arguments()) - baseConstructorArguments.emplace_back( + solAssert(baseConstructorArguments.emplace( dynamic_cast(baseConstructor->scope()), base->arguments() - ); + ).second, ""); if (FunctionDefinition const* constructor = _contract.constructor()) for (ASTPointer const& modifier: constructor->modifiers()) if (auto const* baseContract = dynamic_cast( - modifier->name()->annotation().referencedDeclaration + modifier->name().annotation().referencedDeclaration )) if ( FunctionDefinition const* baseConstructor = baseContract->constructor(); baseConstructor && modifier->arguments() ) - baseConstructorArguments.emplace_back( + solAssert(baseConstructorArguments.emplace( dynamic_cast(baseConstructor->scope()), modifier->arguments() - ); + ).second, ""); IRGeneratorForStatements generator{m_context, m_utils}; for (auto&& [baseContract, arguments]: baseConstructorArguments) @@ -506,8 +537,16 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra )"); vector params; if (contract->constructor()) + { + for (auto const& modifierInvocation: contract->constructor()->modifiers()) + // This can be ContractDefinition too for super arguments. That is supported. + solUnimplementedAssert( + !dynamic_cast(modifierInvocation->name().annotation().referencedDeclaration), + "Modifiers not implemented yet." + ); for (ASTPointer const& varDecl: contract->constructor()->parameters()) params += m_context.addLocalVariable(*varDecl).stackSlots(); + } t("params", joinHumanReadable(params)); vector baseParams = listAllParams(baseConstructorParams); t("baseParams", joinHumanReadable(baseParams)); @@ -543,7 +582,7 @@ string IRGenerator::deployCode(ContractDefinition const& _contract) codecopy(0, dataoffset(""), datasize("")) <#storeImmutables> - setimmutable("", ) + setimmutable(0, "", ) return(0, datasize("")) @@ -637,8 +676,16 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) { string fallbackCode; if (!fallback->isPayable()) - fallbackCode += callValueCheck(); - fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()"; + fallbackCode += callValueCheck() + "\n"; + if (fallback->parameters().empty()) + fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()"; + else + { + solAssert(fallback->parameters().size() == 1 && fallback->returnParameters().size() == 1, ""); + fallbackCode += "let retval := " + m_context.enqueueFunctionForCodeGeneration(*fallback) + "(0, calldatasize())\n"; + fallbackCode += "return(add(retval, 0x20), mload(retval))\n"; + + } t("fallback", fallbackCode); } @@ -651,16 +698,25 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) return t.render(); } -string IRGenerator::memoryInit() +string IRGenerator::memoryInit(bool _useMemoryGuard) { + // TODO: Remove once we have made sure it is safe, i.e. after "Yul memory objects lite". + // Also restore the tests removed in the commit that adds this comment. + _useMemoryGuard = false; // This function should be called at the beginning of the EVM call frame // and thus can assume all memory to be zero, including the contents of // the "zero memory area" (the position CompilerUtils::zeroPointer points to). return - Whiskers{"mstore(, )"} + Whiskers{ + _useMemoryGuard ? + "mstore(, memoryguard())" : + "mstore(, )" + } ("memPtr", to_string(CompilerUtils::freeMemoryPointer)) - ("freeMemoryStart", to_string(CompilerUtils::generalPurposeMemoryStart + m_context.reservedMemory())) - .render(); + ( + "freeMemoryStart", + to_string(CompilerUtils::generalPurposeMemoryStart + m_context.reservedMemory()) + ).render(); } void IRGenerator::resetContext(ContractDefinition const& _contract) diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index e57b8535e6eb..cdb431e83ae4 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -53,13 +53,13 @@ class IRGenerator /// (or just pretty-printed, depending on the optimizer settings). std::pair run( ContractDefinition const& _contract, - std::map const& _otherYulSources + std::map const& _otherYulSources ); private: std::string generate( ContractDefinition const& _contract, - std::map const& _otherYulSources + std::map const& _otherYulSources ); std::string generate(Block const& _block); @@ -100,7 +100,9 @@ class IRGenerator std::string dispatchRoutine(ContractDefinition const& _contract); - std::string memoryInit(); + /// @a _useMemoryGuard If true, use a memory guard, allowing the optimiser + /// to perform memory optimizations. + std::string memoryInit(bool _useMemoryGuard); void resetContext(ContractDefinition const& _contract); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 8c832b051cfe..711033ea3a7e 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -30,17 +30,21 @@ #include #include #include +#include #include #include -#include +#include #include #include +#include + #include #include #include +#include #include #include @@ -65,31 +69,12 @@ struct CopyTranslate: public yul::ASTCopier yul::Expression operator()(yul::Identifier const& _identifier) override { + // The operator() function is only called in lvalue context. In rvalue context, + // only translate(yul::Identifier) is called. if (m_references.count(&_identifier)) - { - auto const& reference = m_references.at(&_identifier); - auto const varDecl = dynamic_cast(reference.declaration); - solUnimplementedAssert(varDecl, ""); - - if (reference.isOffset || reference.isSlot) - { - solAssert(reference.isOffset != reference.isSlot, ""); - - pair slot_offset = m_context.storageLocationOfVariable(*varDecl); - - string const value = reference.isSlot ? - slot_offset.first.str() : - to_string(slot_offset.second); - - return yul::Literal{ - _identifier.location, - yul::LiteralKind::Number, - yul::YulString{value}, - {} - }; - } - } - return ASTCopier::operator()(_identifier); + return translateReference(_identifier); + else + return ASTCopier::operator()(_identifier); } yul::YulString translateIdentifier(yul::YulString _name) override @@ -109,24 +94,114 @@ struct CopyTranslate: public yul::ASTCopier if (!m_references.count(&_identifier)) return ASTCopier::translate(_identifier); + yul::Expression translated = translateReference(_identifier); + solAssert(holds_alternative(translated), ""); + return get(std::move(translated)); + } + +private: + + /// Translates a reference to a local variable, potentially including + /// a suffix. Might return a literal, which causes this to be invalid in + /// lvalue-context. + yul::Expression translateReference(yul::Identifier const& _identifier) + { auto const& reference = m_references.at(&_identifier); auto const varDecl = dynamic_cast(reference.declaration); solUnimplementedAssert(varDecl, ""); + string const& suffix = reference.suffix; - solAssert( - reference.isOffset == false && reference.isSlot == false, - "Should not be called for offset/slot" - ); - auto const& var = m_context.localVariable(*varDecl); - solAssert(var.type().sizeOnStack() == 1, ""); + if (suffix.empty() && !varDecl->isStateVariable()) + { + auto const& var = m_context.localVariable(*varDecl); + solAssert(var.type().sizeOnStack() == 1, ""); - return yul::Identifier{ - _identifier.location, - yul::YulString{var.commaSeparatedList()} - }; + return yul::Identifier{ + _identifier.location, + yul::YulString{var.commaSeparatedList()} + }; + } + + string value; + if (varDecl->isConstant()) + { + VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl); + solAssert(variable, ""); + + if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) + { + u256 intValue = dynamic_cast(*variable->value()->annotation().type).literalValue(nullptr); + if (auto const* bytesType = dynamic_cast(variable->type())) + intValue <<= 256 - 8 * bytesType->numBytes(); + else + solAssert(variable->type()->category() == Type::Category::Integer, ""); + value = intValue.str(); + } + else if (auto const* literal = dynamic_cast(variable->value().get())) + { + TypePointer type = literal->annotation().type; + + switch (type->category()) + { + case Type::Category::Bool: + case Type::Category::Address: + solAssert(type->category() == variable->annotation().type->category(), ""); + value = toCompactHexWithPrefix(type->literalValue(literal)); + break; + case Type::Category::StringLiteral: + { + auto const& stringLiteral = dynamic_cast(*type); + solAssert(variable->type()->category() == Type::Category::FixedBytes, ""); + unsigned const numBytes = dynamic_cast(*variable->type()).numBytes(); + solAssert(stringLiteral.value().size() <= numBytes, ""); + value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft))); + break; + } + default: + solAssert(false, ""); + } + } + else + solAssert(false, "Invalid constant in inline assembly."); + } + else if (varDecl->isStateVariable()) + { + if (suffix == "slot") + value = m_context.storageLocationOfStateVariable(*varDecl).first.str(); + else if (suffix == "offset") + value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second); + else + solAssert(false, ""); + } + else if (varDecl->type()->dataStoredIn(DataLocation::Storage)) + { + solAssert(suffix == "slot" || suffix == "offset", ""); + solAssert(varDecl->isLocalVariable(), ""); + if (suffix == "slot") + value = IRVariable{*varDecl}.part("slot").name(); + else if (varDecl->type()->isValueType()) + value = IRVariable{*varDecl}.part("offset").name(); + else + { + solAssert(!IRVariable{*varDecl}.hasPart("offset"), ""); + value = "0"; + } + } + else if (varDecl->type()->dataStoredIn(DataLocation::CallData)) + { + solAssert(suffix == "offset" || suffix == "length", ""); + value = IRVariable{*varDecl}.part(suffix).name(); + } + else + solAssert(false, ""); + + if (isdigit(value.front())) + return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}}; + else + return yul::Identifier{_identifier.location, yul::YulString{value}}; } -private: + yul::Dialect const& m_dialect; IRGenerationContext& m_context; ExternalRefsMap const& m_references; @@ -140,70 +215,136 @@ string IRGeneratorForStatements::code() const return m_code.str(); } +void IRGeneratorForStatements::generate(Block const& _block) +{ + try + { + _block.accept(*this); + } + catch (langutil::UnimplementedFeatureError const& _error) + { + if (!boost::get_error_info(_error)) + _error << langutil::errinfo_sourceLocation(m_currentLocation); + throw _error; + } +} + void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl) { - solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); - solAssert(!_varDecl.isConstant(), ""); - if (!_varDecl.value()) - return; + try + { + setLocation(_varDecl); - _varDecl.value()->accept(*this); - writeToLValue( - _varDecl.immutable() ? - IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : - IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ - util::toCompactHexWithPrefix(m_context.storageLocationOfVariable(_varDecl).first), - m_context.storageLocationOfVariable(_varDecl).second - }}, - *_varDecl.value() - ); + solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); + solAssert(!_varDecl.isConstant(), ""); + if (!_varDecl.value()) + return; + + _varDecl.value()->accept(*this); + + Type const* rightIntermediateType = _varDecl.value()->annotation().type->closestTemporaryType(_varDecl.type()); + solAssert(rightIntermediateType, ""); + IRVariable value = convert(*_varDecl.value(), *rightIntermediateType); + writeToLValue( + _varDecl.immutable() ? + IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : + IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ + util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), + m_context.storageLocationOfStateVariable(_varDecl).second + }}, + value + ); + } + catch (langutil::UnimplementedFeatureError const& _error) + { + if (!boost::get_error_info(_error)) + _error << langutil::errinfo_sourceLocation(m_currentLocation); + throw _error; + } } void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) { - solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); + try + { + setLocation(_varDecl); + + solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); - auto const* type = _varDecl.type(); - if (auto const* refType = dynamic_cast(type)) - if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) + auto const* type = _varDecl.type(); + if (dynamic_cast(type)) return; + else if (auto const* refType = dynamic_cast(type)) + if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) + return; - IRVariable zero = zeroValue(*type); - assign(m_context.localVariable(_varDecl), zero); + IRVariable zero = zeroValue(*type); + assign(m_context.localVariable(_varDecl), zero); + } + catch (langutil::UnimplementedFeatureError const& _error) + { + if (!boost::get_error_info(_error)) + _error << langutil::errinfo_sourceLocation(m_currentLocation); + throw _error; + } } IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType) { - _expression.accept(*this); - IRVariable variable{m_context.newYulVariable(), _targetType}; - define(variable, _expression); - return variable; + try + { + setLocation(_expression); + + _expression.accept(*this); + IRVariable variable{m_context.newYulVariable(), _targetType}; + define(variable, _expression); + return variable; + } + catch (langutil::UnimplementedFeatureError const& _error) + { + if (!boost::get_error_info(_error)) + _error << langutil::errinfo_sourceLocation(m_currentLocation); + throw _error; + } } string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant) { - string functionName = IRNames::constantValueFunction(_constant); - return m_context.functionCollector().createFunction(functionName, [&] { - Whiskers templ(R"( - function () -> { - - := - } - )"); - templ("functionName", functionName); - IRGeneratorForStatements generator(m_context, m_utils); - solAssert(_constant.value(), ""); - Type const& constantType = *_constant.type(); - templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); - templ("code", generator.code()); - templ("ret", IRVariable("ret", constantType).commaSeparatedList()); - - return templ.render(); - }); + try + { + setLocation(_constant); + + string functionName = IRNames::constantValueFunction(_constant); + return m_context.functionCollector().createFunction(functionName, [&] { + Whiskers templ(R"( + function () -> { + + := + } + )"); + templ("functionName", functionName); + IRGeneratorForStatements generator(m_context, m_utils); + solAssert(_constant.value(), ""); + Type const& constantType = *_constant.type(); + templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); + templ("code", generator.code()); + templ("ret", IRVariable("ret", constantType).commaSeparatedList()); + + return templ.render(); + }); + } + catch (langutil::UnimplementedFeatureError const& _error) + { + if (!boost::get_error_info(_error)) + _error << langutil::errinfo_sourceLocation(m_currentLocation); + throw _error; + } } void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) { + setLocation(_varDeclStatement); + if (Expression const* expression = _varDeclStatement.initialValue()) { if (_varDeclStatement.declarations().size() > 1) @@ -237,14 +378,22 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional) { _conditional.condition().accept(*this); + setLocation(_conditional); + string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean()); declare(_conditional); m_code << "switch " << condition << "\n" "case 0 {\n"; + _conditional.falseExpression().accept(*this); + setLocation(_conditional); + assign(_conditional, _conditional.falseExpression()); m_code << "}\n" "default {\n"; + _conditional.trueExpression().accept(*this); + setLocation(_conditional); + assign(_conditional, _conditional.trueExpression()); m_code << "}\n"; @@ -254,6 +403,7 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional) bool IRGeneratorForStatements::visit(Assignment const& _assignment) { _assignment.rightHandSide().accept(*this); + setLocation(_assignment); Token assignmentOperator = _assignment.assignmentOperator(); Token binaryOperator = @@ -271,17 +421,19 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType); _assignment.leftHandSide().accept(*this); solAssert(!!m_currentLValue, "LValue not retrieved."); + setLocation(_assignment); if (assignmentOperator != Token::Assign) { solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types."); IRVariable leftIntermediate = readFromLValue(*m_currentLValue); + solAssert(binaryOperator != Token::Exp, ""); if (TokenTraits::isShiftOp(binaryOperator)) { solAssert(type(_assignment) == leftIntermediate.type(), ""); solAssert(type(_assignment) == type(_assignment.leftHandSide()), ""); - define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value); + define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value) << "\n"; writeToLValue(*m_currentLValue, IRVariable(_assignment)); m_currentLValue.reset(); @@ -301,15 +453,21 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) writeToLValue(*m_currentLValue, value); - m_currentLValue.reset(); - if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) + if ( + m_currentLValue->type.category() != Type::Category::Struct && + m_currentLValue->type.category() != Type::Category::Array && + *_assignment.annotation().type != *TypeProvider::emptyTuple() + ) define(_assignment, value); + m_currentLValue.reset(); return false; } bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) { + setLocation(_tuple); + if (_tuple.isInlineArray()) { auto const& arrayType = dynamic_cast(*_tuple.annotation().type); @@ -326,13 +484,14 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) { Expression const& component = *_tuple.components()[i]; component.accept(*this); + setLocation(_tuple); IRVariable converted = convert(component, baseType); m_code << m_utils.writeToMemoryFunction(baseType) << "(" << ("add(" + mpos + ", " + to_string(i * arrayType.memoryStride()) + ")") << ", " << - converted.name() << + converted.commaSeparatedList() << ")\n"; } } @@ -345,6 +504,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) { solAssert(_tuple.components().front(), ""); _tuple.components().front()->accept(*this); + setLocation(_tuple); if (willBeWrittenTo) solAssert(!!m_currentLValue, ""); else @@ -357,6 +517,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) if (auto const& component = _tuple.components()[i]) { component->accept(*this); + setLocation(_tuple); if (willBeWrittenTo) { solAssert(!!m_currentLValue, ""); @@ -379,20 +540,42 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) return false; } +bool IRGeneratorForStatements::visit(Block const& _block) +{ + if (_block.unchecked()) + { + solAssert(m_context.arithmetic() == Arithmetic::Checked, ""); + m_context.setArithmetic(Arithmetic::Wrapping); + } + return true; +} + +void IRGeneratorForStatements::endVisit(Block const& _block) +{ + if (_block.unchecked()) + { + solAssert(m_context.arithmetic() == Arithmetic::Wrapping, ""); + m_context.setArithmetic(Arithmetic::Checked); + } +} + bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) { _ifStatement.condition().accept(*this); + setLocation(_ifStatement); string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean()); if (_ifStatement.falseStatement()) { m_code << "switch " << condition << "\n" "case 0 {\n"; _ifStatement.falseStatement()->accept(*this); + setLocation(_ifStatement); m_code << "}\n" "default {\n"; } else m_code << "if " << condition << " {\n"; _ifStatement.trueStatement().accept(*this); + setLocation(_ifStatement); m_code << "}\n"; return false; @@ -400,6 +583,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) { + setLocation(_forStatement); generateLoop( _forStatement.body(), _forStatement.condition(), @@ -412,6 +596,7 @@ bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) { + setLocation(_whileStatement); generateLoop( _whileStatement.body(), &_whileStatement.condition(), @@ -423,20 +608,23 @@ bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) return false; } -bool IRGeneratorForStatements::visit(Continue const&) +bool IRGeneratorForStatements::visit(Continue const& _continue) { + setLocation(_continue); m_code << "continue\n"; return false; } -bool IRGeneratorForStatements::visit(Break const&) +bool IRGeneratorForStatements::visit(Break const& _break) { + setLocation(_break); m_code << "break\n"; return false; } void IRGeneratorForStatements::endVisit(Return const& _return) { + setLocation(_return); if (Expression const* value = _return.expression()) { solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); @@ -453,6 +641,7 @@ void IRGeneratorForStatements::endVisit(Return const& _return) void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) { + setLocation(_unaryOperation); Type const& resultType = type(_unaryOperation); Token const op = _unaryOperation.getOperator(); @@ -514,19 +703,25 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) else if (op == Token::Sub) { IntegerType const& intType = *dynamic_cast(&resultType); - define(_unaryOperation) << - m_utils.negateNumberCheckedFunction(intType) << - "(" << - IRVariable(_unaryOperation.subExpression()).name() << - ")\n"; + define(_unaryOperation) << ( + m_context.arithmetic() == Arithmetic::Checked ? + m_utils.negateNumberCheckedFunction(intType) : + m_utils.negateNumberWrappingFunction(intType) + ) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n"; } else solUnimplementedAssert(false, "Unary operator not yet implemented"); } + else if (resultType.category() == Type::Category::FixedBytes) + { + solAssert(op == Token::BitNot, "Only bitwise negation is allowed for FixedBytes"); + solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!"); + appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression()); + } else if (resultType.category() == Type::Category::Bool) { solAssert( - _unaryOperation.getOperator() != Token::BitNot, + op != Token::BitNot, "Bitwise Negation can't be done on bool!" ); @@ -538,6 +733,8 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) { + setLocation(_binOp); + solAssert(!!_binOp.annotation().commonType, ""); TypePointer commonType = _binOp.annotation().commonType; langutil::Token op = _binOp.getOperator(); @@ -557,6 +754,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) _binOp.leftExpression().accept(*this); _binOp.rightExpression().accept(*this); + setLocation(_binOp); if (TokenTraits::isCompareOp(op)) { @@ -593,6 +791,29 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) solAssert(false, "Unknown comparison operator."); define(_binOp) << expr << "\n"; } + else if (op == Token::Exp) + { + IRVariable left = convert(_binOp.leftExpression(), *commonType); + IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType()); + + if (auto rationalNumberType = dynamic_cast(_binOp.leftExpression().annotation().type)) + { + solAssert(rationalNumberType->integerType(), "Invalid literal as the base for exponentiation."); + solAssert(dynamic_cast(commonType), ""); + + define(_binOp) << m_utils.overflowCheckedIntLiteralExpFunction( + *rationalNumberType, + dynamic_cast(right.type()), + dynamic_cast(*commonType) + ) << "(" << right.name() << ")\n"; + } + else + define(_binOp) << m_utils.overflowCheckedIntExpFunction( + dynamic_cast(left.type()), + dynamic_cast(right.type()) + ) << "(" << left.name() << ", " << right.name() << ")\n"; + + } else if (TokenTraits::isShiftOp(op)) { IRVariable left = convert(_binOp.leftExpression(), *commonType); @@ -608,28 +829,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) return false; } -bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) -{ - FunctionTypePointer functionType = dynamic_cast(&type(_functionCall.expression())); - if ( - functionType && - functionType->kind() == FunctionType::Kind::Internal && - !functionType->bound() && - IRHelpers::referencedFunctionDeclaration(_functionCall.expression()) - ) - m_context.internalFunctionCalledDirectly(_functionCall.expression()); - - return true; -} - void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { - solUnimplementedAssert( - _functionCall.annotation().kind != FunctionCallKind::Unset, - "This type of function call is not yet implemented" - ); + setLocation(_functionCall); + auto functionCallKind = *_functionCall.annotation().kind; - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { solAssert( _functionCall.expression().annotation().type->category() == Type::Category::TypeType, @@ -641,7 +846,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } FunctionTypePointer functionType = nullptr; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { auto const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -651,28 +856,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) functionType = dynamic_cast(_functionCall.expression().annotation().type); TypePointers parameterTypes = functionType->parameterTypes(); - vector> const& callArguments = _functionCall.arguments(); - vector> const& callArgumentNames = _functionCall.names(); - if (!functionType->takesArbitraryParameters()) - solAssert(callArguments.size() == parameterTypes.size(), ""); - - vector> arguments; - if (callArgumentNames.empty()) - // normal arguments - arguments = callArguments; - else - // named arguments - for (auto const& parameterName: functionType->parameterNames()) - { - auto const it = std::find_if(callArgumentNames.cbegin(), callArgumentNames.cend(), [&](ASTPointer const& _argName) { - return *_argName == parameterName; - }); - solAssert(it != callArgumentNames.cend(), ""); - arguments.push_back(callArguments[static_cast(std::distance(callArgumentNames.begin(), it))]); - } + vector> const& arguments = _functionCall.sortedArguments(); - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { TypeType const& type = dynamic_cast(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast(*type.actualType()); @@ -707,14 +894,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { solAssert(!functionType->bound(), ""); if (auto contractType = dynamic_cast(expressionType->actualType())) - solUnimplementedAssert( - !contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal, - "Only internal function calls implemented for libraries" - ); + if (contractType->contractDefinition().isLibrary()) + solAssert(functionType->kind() == FunctionType::Kind::Internal || functionType->kind() == FunctionType::Kind::DelegateCall, ""); } } - else - solAssert(!functionType->bound(), ""); switch (functionType->kind()) { @@ -732,22 +915,34 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(functionType->declaration() == *functionDef, ""); if (identifier) + { + solAssert(*identifier->annotation().requiredLookup == VirtualLookup::Virtual, ""); functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); + } + else if (auto typeType = dynamic_cast(memberAccess->expression().annotation().type)) + if ( + auto contractType = dynamic_cast(typeType->actualType()); + contractType->isSuper() + ) + { + ContractDefinition const* super = contractType->contractDefinition().superContract(m_context.mostDerivedContract()); + solAssert(super, "Super contract not available."); + solAssert(*memberAccess->annotation().requiredLookup == VirtualLookup::Super, ""); + functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract(), super); + } - solAssert(functionDef->isImplemented(), ""); + solAssert(functionDef && functionDef->isImplemented(), ""); + solAssert( + functionDef->parameters().size() == arguments.size() + (functionType->bound() ? 1 : 0), + "" + ); } solAssert(!functionType->takesArbitraryParameters(), ""); vector args; if (functionType->bound()) - { - solAssert(memberAccess && functionDef, ""); - solAssert(functionDef->parameters().size() == arguments.size() + 1, ""); - args += convert(memberAccess->expression(), *functionDef->parameters()[0]->type()).stackSlots(); - } - else - solAssert(!functionDef || functionDef->parameters().size() == arguments.size(), ""); + args += IRVariable(_functionCall.expression()).part("self").stackSlots(); for (size_t i = 0; i < arguments.size(); ++i) args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); @@ -807,7 +1002,21 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) m_utils.packedHashFunction({arg.annotation().type}, {referenceType}) << "(" << IRVariable(arg).commaSeparatedList() << - ")"; + ")\n"; + else if (auto functionType = dynamic_cast(paramTypes[i])) + { + solAssert( + IRVariable(arg).type() == *functionType && + functionType->kind() == FunctionType::Kind::External && + !functionType->bound(), + "" + ); + define(indexedArgs.emplace_back(m_context.newYulVariable(), *TypeProvider::fixedBytes(32))) << + m_utils.combineExternalFunctionIdFunction() << + "(" << + IRVariable(arg).commaSeparatedList() << + ")\n"; + } else indexedArgs.emplace_back(convert(arg, *paramTypes[i])); } @@ -885,10 +1094,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) // hash the signature Type const& selectorType = type(*arguments.front()); if (auto const* stringType = dynamic_cast(&selectorType)) - { - FixedHash<4> hash(keccak256(stringType->value())); - selector = formatNumber(u256(FixedHash<4>::Arith(hash)) << (256 - 32)); - } + selector = formatNumber(util::selectorFromSignature(stringType->value())); else { // Used to reset the free memory pointer later. @@ -1005,10 +1211,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) })"); templ("pos", m_context.newYulVariable()); templ("end", m_context.newYulVariable()); - templ( - "hash", - (u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str() - ); + templ("hash", util::selectorFromSignature("Error(string)").str()); templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction()); templ( "argumentVars", @@ -1130,8 +1333,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2])); define(modulus, *arguments[2]); - Whiskers templ("if iszero() { invalid() }\n"); - m_code << templ("modulus", modulus.name()).render(); + Whiskers templ("if iszero() { () }\n"); + templ("modulus", modulus.name()); + templ("panic", m_utils.panicFunction(PanicCode::DivisionByZero)); + m_code << templ.render(); string args; for (size_t i = 0; i < 2; ++i) @@ -1157,34 +1362,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) define(_functionCall) << functions[functionType->kind()] << "(" << args << ")\n"; break; } - case FunctionType::Kind::Log0: - case FunctionType::Kind::Log1: - case FunctionType::Kind::Log2: - case FunctionType::Kind::Log3: - case FunctionType::Kind::Log4: - { - unsigned logNumber = static_cast(functionType->kind()) - static_cast(FunctionType::Kind::Log0); - solAssert(arguments.size() == logNumber + 1, ""); - ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); - string indexedArgs; - for (unsigned arg = 0; arg < logNumber; ++arg) - indexedArgs += ", " + expressionAsType(*arguments[arg + 1], *(parameterTypes[arg + 1])); - Whiskers templ(R"({ - let := - let := (, ) - (, sub(, ) ) - })"); - templ("pos", m_context.newYulVariable()); - templ("end", m_context.newYulVariable()); - templ("freeMemory", freeMemory()); - templ("encode", abi.tupleEncoder({arguments.front()->annotation().type}, {parameterTypes.front()})); - templ("nonIndexedArgs", IRVariable(*arguments.front()).commaSeparatedList()); - templ("log", "log" + to_string(logNumber)); - templ("indexedArgs", indexedArgs); - m_code << templ.render(); - - break; - } case FunctionType::Kind::Creation: { solAssert(!functionType->gasSet(), "Gas limit set for contract creation."); @@ -1208,14 +1385,19 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) Whiskers t(R"( let := () let := add(, datasize("")) - if or(gt(, 0xffffffffffffffff), lt(, )) { revert(0, 0) } + if or(gt(, 0xffffffffffffffff), lt(, )) { () } datacopy(, dataoffset(""), datasize("")) := () - let := create2(, , sub(, ), ) + let
:= create2(, , sub(, ), ) - let := create(, , sub(, )) + let
:= create(, , sub(, )) + + let := iszero(iszero(
)) + + if iszero(
) { () } + () )"); t("memPos", m_context.newYulVariable()); @@ -1223,6 +1405,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction()); t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction()); t("object", IRNames::creationObject(*contract)); + t("panic", m_utils.panicFunction(PanicCode::ResourceError)); t("abiEncode", m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false) ); @@ -1231,7 +1414,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) t("saltSet", functionType->saltSet()); if (functionType->saltSet()) t("salt", IRVariable(_functionCall.expression()).part("salt").name()); - t("retVars", IRVariable(_functionCall).commaSeparatedList()); + solAssert(IRVariable(_functionCall).stackSlots().size() == 1, ""); + t("address", IRVariable(_functionCall).commaSeparatedList()); + t("isTryCall", _functionCall.annotation().tryCall); + if (_functionCall.annotation().tryCall) + t("success", IRNames::trySuccessConditionVariable(_functionCall)); + else + t("forwardingRevert", m_utils.forwardingRevertFunction()); m_code << t.render(); break; @@ -1273,7 +1462,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(!functionType->gasSet(), ""); solAssert(!functionType->bound(), ""); - static map> precompiles = { + static map> precompiles = { {FunctionType::Kind::ECRecover, std::make_tuple(1, 0)}, {FunctionType::Kind::SHA256, std::make_tuple(2, 0)}, {FunctionType::Kind::RIPEMD160, std::make_tuple(3, 12)}, @@ -1334,6 +1523,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) { + setLocation(_options); FunctionType const& previousType = dynamic_cast(*_options.expression().annotation().type); solUnimplementedAssert(!previousType.bound(), ""); @@ -1353,6 +1543,8 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { + setLocation(_memberAccess); + ASTString const& member = _memberAccess.memberName(); auto memberFunctionType = dynamic_cast(_memberAccess.annotation().type); Type::Category objectCategory = _memberAccess.expression().annotation().type->category(); @@ -1371,6 +1563,24 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) Type::Category::Array, Type::Category::FixedBytes, }).count(objectCategory) > 0, ""); + + define(IRVariable(_memberAccess).part("self"), _memberAccess.expression()); + auto const& functionDefinition = dynamic_cast(memberFunctionType->declaration()); + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); + if (memberFunctionType->kind() == FunctionType::Kind::Internal) + { + define(IRVariable(_memberAccess).part("functionIdentifier")) << to_string(functionDefinition.id()) << "\n"; + if (!_memberAccess.annotation().calledDirectly) + m_context.addToInternalDispatch(functionDefinition); + } + else + { + solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall, ""); + auto contract = dynamic_cast(functionDefinition.scope()); + solAssert(contract && contract->isLibrary(), ""); + define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n"; + define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier() << "\n"; + } return; } @@ -1380,9 +1590,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { ContractType const& type = dynamic_cast(*_memberAccess.expression().annotation().type); if (type.isSuper()) - { - solUnimplementedAssert(false, ""); - } + solAssert(false, ""); + // ordinary contract type else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) { @@ -1413,11 +1622,22 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) "balance(" << expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << ")\n"; - else if (member == "isContract") + else if (member == "code") + define(_memberAccess) << + m_utils.externalCodeFunction() << + "(" << + expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << + ")\n"; + else if (member == "codehash") define(_memberAccess) << - "isContract(" << + "extcodehash(" << expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << ")\n"; + else if (member == "isContract") + define(_memberAccess) << + "isContract(" << + expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << + ")\n"; else if (set{"send", "transfer"}.count(member)) { solAssert(dynamic_cast(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); @@ -1479,6 +1699,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) define(_memberAccess) << "origin()\n"; else if (member == "gasprice") define(_memberAccess) << "gasprice()\n"; + else if (member == "chainid") + define(_memberAccess) << "chainid()\n"; else if (member == "data") { IRVariable var(_memberAccess); @@ -1497,7 +1719,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else if (member == "creationCode" || member == "runtimeCode") { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + auto const& contractType = dynamic_cast(*arg); + solAssert(!contractType.isSuper(), ""); + ContractDefinition const& contract = contractType.contractDefinition(); m_context.subObjectsCreated().insert(&contract); m_code << Whiskers(R"( let := datasize("") @@ -1517,11 +1741,10 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else if (member == "interfaceId") { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); - uint64_t result{0}; - for (auto const& function: contract.interfaceFunctionList(false)) - result ^= fromBigEndian(function.first.ref()); - define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n"; + auto const& contractType = dynamic_cast(*arg); + solAssert(!contractType.isSuper(), ""); + ContractDefinition const& contract = contractType.contractDefinition(); + define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n"; } else if (member == "min" || member == "max") { @@ -1582,15 +1805,18 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) baseRef << ", " << offset << - ")" << - std::endl; + ")\n"; + else if ( + dynamic_cast(_memberAccess.annotation().type) || + dynamic_cast(_memberAccess.annotation().type) + ) + define(_memberAccess) << offset << "\n"; else define(_memberAccess) << m_utils.readFromCalldata(*_memberAccess.annotation().type) << "(" << offset << - ")" << - std::endl; + ")\n"; break; } default: @@ -1609,32 +1835,11 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) auto const& type = dynamic_cast(*_memberAccess.expression().annotation().type); if (member == "length") - { - if (!type.isDynamicallySized()) - define(_memberAccess) << type.length() << "\n"; - else - switch (type.location()) - { - case DataLocation::CallData: - define(_memberAccess, IRVariable(_memberAccess.expression()).part("length")); - break; - case DataLocation::Storage: - { - define(_memberAccess) << - m_utils.arrayLengthFunction(type) << - "(" << - IRVariable(_memberAccess.expression()).commaSeparatedList() << - ")\n"; - break; - } - case DataLocation::Memory: - define(_memberAccess) << - "mload(" << - IRVariable(_memberAccess.expression()).commaSeparatedList() << - ")\n"; - break; - } - } + define(_memberAccess) << + m_utils.arrayLengthFunction(type) << + "(" << + IRVariable(_memberAccess.expression()).commaSeparatedList() << + ")\n"; else if (member == "pop" || member == "push") { solAssert(type.location() == DataLocation::Storage, ""); @@ -1662,8 +1867,25 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) if (actualType.category() == Type::Category::Contract) { - if (auto const* variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - handleVariableReference(*variable, _memberAccess); + ContractType const& contractType = dynamic_cast(actualType); + if (contractType.isSuper()) + { + solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); + ContractDefinition const* super = contractType.contractDefinition().superContract(m_context.mostDerivedContract()); + solAssert(super, "Super contract not available."); + FunctionDefinition const& resolvedFunctionDef = + dynamic_cast( + *_memberAccess.annotation().referencedDeclaration + ).resolveVirtual(m_context.mostDerivedContract(), super); + + define(_memberAccess) << to_string(resolvedFunctionDef.id()) << "\n"; + solAssert(resolvedFunctionDef.functionType(true), ""); + solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, ""); + if (!_memberAccess.annotation().calledDirectly) + m_context.addToInternalDispatch(resolvedFunctionDef); + } + else if (auto const* variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + handleVariableReference(*variable, _memberAccess); else if (memberFunctionType) { switch (memberFunctionType->kind()) @@ -1674,7 +1896,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) if (auto const* function = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) { define(_memberAccess) << to_string(function->id()) << "\n"; - m_context.internalFunctionAccessed(_memberAccess, *function); + if (!_memberAccess.annotation().calledDirectly) + m_context.addToInternalDispatch(*function); } else solAssert(false, "Function not found in member access"); @@ -1684,7 +1907,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) dynamic_cast(_memberAccess.annotation().referencedDeclaration), "Event not found" ); - // the call will do the resolving + // the call will do the resolving break; case FunctionType::Kind::DelegateCall: define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); @@ -1698,11 +1921,6 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareStaticCall: case FunctionType::Kind::Transfer: - case FunctionType::Kind::Log0: - case FunctionType::Kind::Log1: - case FunctionType::Kind::Log2: - case FunctionType::Kind::Log3: - case FunctionType::Kind::Log4: case FunctionType::Kind::ECRecover: case FunctionType::Kind::SHA256: case FunctionType::Kind::RIPEMD160: @@ -1712,7 +1930,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) } else if (dynamic_cast(_memberAccess.annotation().type)) { - // no-op + // no-op } else // The old code generator had a generic "else" case here @@ -1729,6 +1947,37 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) solAssert(false, ""); break; } + case Type::Category::Module: + { + Type::Category category = _memberAccess.annotation().type->category(); + solAssert( + dynamic_cast(_memberAccess.annotation().referencedDeclaration) || + dynamic_cast(_memberAccess.annotation().referencedDeclaration) || + category == Type::Category::TypeType || + category == Type::Category::Module, + "" + ); + if (auto variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + solAssert(variable->isConstant(), ""); + handleVariableReference(*variable, static_cast(_memberAccess)); + } + else if (auto const* function = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + auto funType = dynamic_cast(_memberAccess.annotation().type); + solAssert(function && function->isFree(), ""); + solAssert(function->functionType(true), ""); + solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal, ""); + solAssert(funType->kind() == FunctionType::Kind::Internal, ""); + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); + + define(_memberAccess) << to_string(function->id()) << "\n"; + + if (!_memberAccess.annotation().calledDirectly) + m_context.addToInternalDispatch(*function); + } + break; + } default: solAssert(false, "Member access to unknown type."); } @@ -1736,6 +1985,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) { + setLocation(_inlineAsm); + m_context.setInlineAssemblySeen(); CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; yul::Statement modified = bodyCopier(_inlineAsm.operations()); @@ -1750,6 +2001,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) { + setLocation(_indexAccess); Type const& baseType = *_indexAccess.baseExpression().annotation().type; if (baseType.category() == Type::Category::Mapping) @@ -1829,28 +2081,27 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) } case DataLocation::CallData: { - IRVariable var(m_context.newYulVariable(), *arrayType.baseType()); - define(var) << - m_utils.calldataArrayIndexAccessFunction(arrayType) << - "(" << - IRVariable(_indexAccess.baseExpression()).commaSeparatedList() << - ", " << - expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) << - ")\n"; + string const indexAccessFunctionCall = + m_utils.calldataArrayIndexAccessFunction(arrayType) + + "(" + + IRVariable(_indexAccess.baseExpression()).commaSeparatedList() + + ", " + + expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) + + ")"; if (arrayType.isByteArray()) define(_indexAccess) << m_utils.cleanupFunction(*arrayType.baseType()) << "(calldataload(" << - var.name() << + indexAccessFunctionCall << "))\n"; else if (arrayType.baseType()->isValueType()) define(_indexAccess) << m_utils.readFromCalldata(*arrayType.baseType()) << "(" << - var.commaSeparatedList() << + indexAccessFunctionCall << ")\n"; else - define(_indexAccess, var); + define(_indexAccess) << indexAccessFunctionCall << "\n"; break; } } @@ -1863,11 +2114,12 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()}; define(index, *_indexAccess.indexExpression()); m_code << Whiskers(R"( - if iszero(lt(, )) { invalid() } + if iszero(lt(, )) { () } let := (byte(, )) )") ("index", index.name()) ("length", to_string(fixedBytesType.numBytes())) + ("panic", m_utils.panicFunction(PanicCode::ArrayOutOfBounds)) ("array", IRVariable(_indexAccess.baseExpression()).name()) ("shl248", m_utils.shiftLeftFunction(256 - 8)) ("result", IRVariable(_indexAccess).name()) @@ -1885,6 +2137,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) { + setLocation(_indexRangeAccess); Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; solAssert( baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice, @@ -1931,24 +2184,27 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces void IRGeneratorForStatements::endVisit(Identifier const& _identifier) { + setLocation(_identifier); Declaration const* declaration = _identifier.annotation().referencedDeclaration; if (MagicVariableDeclaration const* magicVar = dynamic_cast(declaration)) { switch (magicVar->type()->category()) { case Type::Category::Contract: - if (dynamic_cast(*magicVar->type()).isSuper()) - solAssert(_identifier.name() == "super", ""); - else - { - solAssert(_identifier.name() == "this", ""); - define(_identifier) << "address()\n"; - } + solAssert(_identifier.name() == "this", ""); + define(_identifier) << "address()\n"; break; case Type::Category::Integer: solAssert(_identifier.name() == "now", ""); define(_identifier) << "timestamp()\n"; break; + case Type::Category::TypeType: + { + auto typeType = dynamic_cast(magicVar->type()); + if (auto contractType = dynamic_cast(typeType->actualType())) + solAssert(!contractType->isSuper() || _identifier.name() == "super", ""); + break; + } default: break; } @@ -1956,18 +2212,21 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) { + solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, ""); FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract()); define(_identifier) << to_string(resolvedFunctionDef.id()) << "\n"; solAssert(resolvedFunctionDef.functionType(true), ""); solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, ""); - m_context.internalFunctionAccessed(_identifier, resolvedFunctionDef); + if (!_identifier.annotation().calledDirectly) + m_context.addToInternalDispatch(resolvedFunctionDef); } else if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) handleVariableReference(*varDecl, _identifier); - else if (dynamic_cast(declaration)) + else if (auto const* contract = dynamic_cast(declaration)) { - // no-op + if (contract->isLibrary()) + define(IRVariable(_identifier).part("address")) << linkerSymbol(*contract) << "\n"; } else if (dynamic_cast(declaration)) { @@ -1981,6 +2240,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) { // no-op } + else if (dynamic_cast(declaration)) + { + // no-op + } else { solAssert(false, "Identifier type not expected in expression context."); @@ -1989,6 +2252,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) bool IRGeneratorForStatements::visit(Literal const& _literal) { + setLocation(_literal); Type const& literalType = type(_literal); switch (literalType.category()) @@ -2011,7 +2275,8 @@ void IRGeneratorForStatements::handleVariableReference( Expression const& _referencingExpression ) { - if (_variable.isStateVariable() && _variable.isConstant()) + setLocation(_referencingExpression); + if ((_variable.isStateVariable() || _variable.isFileLevelVariable()) && _variable.isConstant()) define(_referencingExpression) << constantValueFunction(_variable) << "()\n"; else if (_variable.isStateVariable() && _variable.immutable()) setLValue(_referencingExpression, IRLValue{ @@ -2027,8 +2292,8 @@ void IRGeneratorForStatements::handleVariableReference( setLValue(_referencingExpression, IRLValue{ *_variable.annotation().type, IRLValue::Storage{ - toCompactHexWithPrefix(m_context.storageLocationOfVariable(_variable).first), - m_context.storageLocationOfVariable(_variable).second + toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_variable).first), + m_context.storageLocationOfStateVariable(_variable).second } }); else @@ -2051,15 +2316,21 @@ void IRGeneratorForStatements::appendExternalFunctionCall( "Can only be used for regular external calls." ); - solUnimplementedAssert(!funType.bound(), ""); - bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall; bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall(); ReturnInfo const returnInfo{m_context.evmVersion(), funType}; + TypePointers parameterTypes = funType.parameterTypes(); TypePointers argumentTypes; vector argumentStrings; + if (funType.bound()) + { + parameterTypes.insert(parameterTypes.begin(), funType.selfType()); + argumentTypes.emplace_back(funType.selfType()); + argumentStrings += IRVariable(_functionCall.expression()).part("self").stackSlots(); + } + for (auto const& arg: _arguments) { argumentTypes.emplace_back(&type(*arg)); @@ -2138,7 +2409,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; solAssert(funType.padArguments(), ""); - templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall)); + templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall)); templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall"); @@ -2378,47 +2649,50 @@ string IRGeneratorForStatements::binaryOperation( !TokenTraits::isShiftOp(_operator), "Have to use specific shift operation function for shifts." ); - if (IntegerType const* type = dynamic_cast(&_type)) + string fun; + if (TokenTraits::isBitOp(_operator)) { - string fun; - // TODO: Implement all operations for signed and unsigned types. + solAssert( + _type.category() == Type::Category::Integer || + _type.category() == Type::Category::FixedBytes, + ""); switch (_operator) { - case Token::Add: - fun = m_utils.overflowCheckedIntAddFunction(*type); - break; - case Token::Sub: - fun = m_utils.overflowCheckedIntSubFunction(*type); - break; - case Token::Mul: - fun = m_utils.overflowCheckedIntMulFunction(*type); - break; - case Token::Div: - fun = m_utils.overflowCheckedIntDivFunction(*type); - break; - case Token::Mod: - fun = m_utils.checkedIntModFunction(*type); - break; - case Token::BitOr: - fun = "or"; - break; - case Token::BitXor: - fun = "xor"; - break; - case Token::BitAnd: - fun = "and"; - break; - default: - break; + case Token::BitOr: fun = "or"; break; + case Token::BitXor: fun = "xor"; break; + case Token::BitAnd: fun = "and"; break; + default: break; + } + } + else if (TokenTraits::isArithmeticOp(_operator)) + { + IntegerType const* type = dynamic_cast(&_type); + solAssert(type, ""); + bool checked = m_context.arithmetic() == Arithmetic::Checked; + switch (_operator) + { + case Token::Add: + fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type); + break; + case Token::Sub: + fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type); + break; + case Token::Mul: + fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type); + break; + case Token::Div: + fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type); + break; + case Token::Mod: + fun = m_utils.intModFunction(*type); + break; + default: + break; } - - solUnimplementedAssert(!fun.empty(), ""); - return fun + "(" + _left + ", " + _right + ")\n"; } - else - solUnimplementedAssert(false, ""); - return {}; + solUnimplementedAssert(!fun.empty(), "Type: " + _type.toString()); + return fun + "(" + _left + ", " + _right + ")\n"; } std::string IRGeneratorForStatements::shiftOperation( @@ -2452,6 +2726,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b solAssert(op == Token::Or || op == Token::And, ""); _binOp.leftExpression().accept(*this); + setLocation(_binOp); IRVariable value(_binOp); define(value, _binOp.leftExpression()); @@ -2460,6 +2735,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b else m_code << "if " << value.name() << " {\n"; _binOp.rightExpression().accept(*this); + setLocation(_binOp); assign(value, _binOp.rightExpression()); m_code << "}\n"; } @@ -2469,22 +2745,22 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable std::visit( util::GenericVisitor{ [&](IRLValue::Storage const& _storage) { - std::optional offset; + string offsetArgument; + optional offsetStatic; - if (std::holds_alternative(_storage.offset)) - offset = std::get(_storage.offset); + std::visit(GenericVisitor{ + [&](unsigned _offset) { offsetStatic = _offset; }, + [&](string const& _offset) { offsetArgument = ", " + _offset; } + }, _storage.offset); m_code << - m_utils.updateStorageValueFunction(_lvalue.type, offset) << + m_utils.updateStorageValueFunction(_value.type(), _lvalue.type, offsetStatic) << "(" << _storage.slot << - ( - std::holds_alternative(_storage.offset) ? - (", " + std::get(_storage.offset)) : - "" - ) << + offsetArgument << _value.commaSeparatedListPrefixed() << ")\n"; + }, [&](IRLValue::Memory const& _memory) { if (_lvalue.type.isValueType()) @@ -2550,7 +2826,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) define(result) << _storage.slot << "\n"; else if (std::holds_alternative(_storage.offset)) define(result) << - m_utils.readFromStorageDynamic(_lvalue.type, false) << + m_utils.readFromStorageDynamic(_lvalue.type, true) << "(" << _storage.slot << ", " << @@ -2558,7 +2834,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) ")\n"; else define(result) << - m_utils.readFromStorage(_lvalue.type, std::get(_storage.offset), false) << + m_utils.readFromStorage(_lvalue.type, std::get(_storage.offset), true) << "(" << _storage.slot << ")\n"; @@ -2659,6 +2935,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) { Expression const& externalCall = _tryStatement.externalCall(); externalCall.accept(*this); + setLocation(_tryStatement); m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n"; @@ -2679,6 +2956,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) } successClause.block().accept(*this); + setLocation(_tryStatement); m_code << "}\n"; m_code << "default { // failure case\n"; @@ -2769,3 +3047,14 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause) _clause.block().accept(*this); return false; } + +void IRGeneratorForStatements::setLocation(ASTNode const& _node) +{ + m_currentLocation = _node.location(); +} + +string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const +{ + solAssert(_library.isLibrary(), ""); + return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")"; +} diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 09c9c7d278bf..24ac8a61e976 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -47,6 +47,9 @@ class IRGeneratorForStatements: public ASTConstVisitor std::string code() const; + /// Generate the code for the statements in the block; + void generate(Block const& _block); + /// Generates code to initialize the given state variable. void initializeStateVar(VariableDeclaration const& _varDecl); /// Generates code to initialize the given local variable. @@ -63,6 +66,8 @@ class IRGeneratorForStatements: public ASTConstVisitor bool visit(Conditional const& _conditional) override; bool visit(Assignment const& _assignment) override; bool visit(TupleExpression const& _tuple) override; + bool visit(Block const& _block) override; + void endVisit(Block const& _block) override; bool visit(IfStatement const& _ifStatement) override; bool visit(ForStatement const& _forStatement) override; bool visit(WhileStatement const& _whileStatement) override; @@ -71,7 +76,6 @@ class IRGeneratorForStatements: public ASTConstVisitor void endVisit(Return const& _return) override; void endVisit(UnaryOperation const& _unaryOperation) override; bool visit(BinaryOperation const& _binOp) override; - bool visit(FunctionCall const& _funCall) override; void endVisit(FunctionCall const& _funCall) override; void endVisit(FunctionCallOptions const& _funCallOptions) override; void endVisit(MemberAccess const& _memberAccess) override; @@ -179,10 +183,15 @@ class IRGeneratorForStatements: public ASTConstVisitor static Type const& type(Expression const& _expression); + void setLocation(ASTNode const& _node); + + std::string linkerSymbol(ContractDefinition const& _library) const; + std::ostringstream m_code; IRGenerationContext& m_context; YulUtilFunctions& m_utils; std::optional m_currentLValue; + langutil::SourceLocation m_currentLocation; }; } diff --git a/libsolidity/codegen/ir/IRVariable.cpp b/libsolidity/codegen/ir/IRVariable.cpp index 3fce5288a159..92ec87da61e7 100644 --- a/libsolidity/codegen/ir/IRVariable.cpp +++ b/libsolidity/codegen/ir/IRVariable.cpp @@ -53,6 +53,17 @@ IRVariable IRVariable::part(string const& _name) const solAssert(false, "Invalid stack item name: " + _name); } +bool IRVariable::hasPart(std::string const& _name) const +{ + for (auto const& [itemName, itemType]: m_type.stackItems()) + if (itemName == _name) + { + solAssert(itemName.empty() || itemType, ""); + return true; + } + return false; +} + vector IRVariable::stackSlots() const { vector result; diff --git a/libsolidity/codegen/ir/IRVariable.h b/libsolidity/codegen/ir/IRVariable.h index 71413cc38907..1f403a07da92 100644 --- a/libsolidity/codegen/ir/IRVariable.h +++ b/libsolidity/codegen/ir/IRVariable.h @@ -71,6 +71,10 @@ class IRVariable /// in ``m_type.stackItems()`` and may again occupy multiple stack slots. IRVariable part(std::string const& _slot) const; + /// @returns true if variable contains @a _name component + /// @a _name name of the component that is being checked + bool hasPart(std::string const& _name) const; + /// @returns a vector containing the names of the stack slots of the variable. std::vector stackSlots() const; diff --git a/libsolidity/formal/ArraySlicePredicate.cpp b/libsolidity/formal/ArraySlicePredicate.cpp new file mode 100644 index 000000000000..85fced906216 --- /dev/null +++ b/libsolidity/formal/ArraySlicePredicate.cpp @@ -0,0 +1,91 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::smtutil; +using namespace solidity::frontend; +using namespace solidity::frontend::smt; + +map ArraySlicePredicate::m_slicePredicates; + +pair ArraySlicePredicate::create(SortPointer _sort, EncodingContext& _context) +{ + solAssert(_sort->kind == Kind::Tuple, ""); + auto tupleSort = dynamic_pointer_cast(_sort); + solAssert(tupleSort, ""); + + auto tupleName = tupleSort->name; + if (m_slicePredicates.count(tupleName)) + return {true, m_slicePredicates.at(tupleName)}; + + auto sort = tupleSort->components.at(0); + solAssert(sort->kind == Kind::Array, ""); + + smt::SymbolicArrayVariable aVar{sort, "a_" + tupleName, _context }; + smt::SymbolicArrayVariable bVar{sort, "b_" + tupleName, _context}; + smt::SymbolicIntVariable startVar{TypeProvider::uint256(), TypeProvider::uint256(), "start_" + tupleName, _context}; + smt::SymbolicIntVariable endVar{TypeProvider::uint256(), TypeProvider::uint256(), "end_" + tupleName, _context }; + smt::SymbolicIntVariable iVar{TypeProvider::uint256(), TypeProvider::uint256(), "i_" + tupleName, _context}; + + vector domain{sort, sort, startVar.sort(), endVar.sort()}; + auto sliceSort = make_shared(domain, SortProvider::boolSort); + Predicate const& slice = *Predicate::create(sliceSort, "array_slice_" + tupleName, PredicateType::Custom, _context); + + domain.emplace_back(iVar.sort()); + auto predSort = make_shared(domain, SortProvider::boolSort); + Predicate const& header = *Predicate::create(predSort, "array_slice_header_" + tupleName, PredicateType::Custom, _context); + Predicate const& loop = *Predicate::create(predSort, "array_slice_loop_" + tupleName, PredicateType::Custom, _context); + + auto a = aVar.elements(); + auto b = bVar.elements(); + auto start = startVar.currentValue(); + auto end = endVar.currentValue(); + auto i = iVar.currentValue(); + + auto rule1 = smtutil::Expression::implies( + end > start, + header({a, b, start, end, 0}) + ); + + auto rule2 = smtutil::Expression::implies( + header({a, b, start, end, i}) && i >= (end - start), + slice({a, b, start, end}) + ); + + auto rule3 = smtutil::Expression::implies( + header({a, b, start, end, i}) && i >= 0 && i < (end - start), + loop({a, b, start, end, i}) + ); + + auto b_i = smtutil::Expression::select(b, i); + auto a_start_i = smtutil::Expression::select(a, start + i); + auto rule4 = smtutil::Expression::implies( + loop({a, b, start, end, i}) && b_i == a_start_i, + header({a, b, start, end, i + 1}) + ); + + return {false, m_slicePredicates[tupleName] = { + {&slice, &header, &loop}, + {move(rule1), move(rule2), move(rule3), move(rule4)} + }}; +} diff --git a/libsolidity/formal/ArraySlicePredicate.h b/libsolidity/formal/ArraySlicePredicate.h new file mode 100644 index 000000000000..180a9d1898ed --- /dev/null +++ b/libsolidity/formal/ArraySlicePredicate.h @@ -0,0 +1,62 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include +#include + +#include + +#include + +namespace solidity::frontend +{ + +/** + * Contains the set of rules to compute an array slice. + * Rules: + * 1. end > start => ArraySliceHeader(a, b, start, end, 0) + * 2. ArraySliceHeader(a, b, start, end, i) && i >= (end - start) => ArraySlice(a, b, start, end) + * 3. ArraySliceHeader(a, b, start, end, i) && i >= 0 && i < (end - start) => ArraySliceLoop(a, b, start, end, i) + * 4. ArraySliceLoop(a, b, start, end, i) && b[i] = a[start + i] => ArraySliceHeader(a, b, start, end, i + 1) + * + * The rule to be used by CHC is ArraySlice(a, b, start, end). + */ + +struct ArraySlicePredicate +{ + /// Contains the predicates and rules created to compute + /// array slices for a given sort. + struct SliceData + { + std::vector predicates; + std::vector rules; + }; + + /// @returns a flag representing whether the array slice predicates had already been created before for this sort, + /// and the corresponding slice data. + static std::pair create(smtutil::SortPointer _sort, smt::EncodingContext& _context); + + static void reset() { m_slicePredicates.clear(); } + +private: + /// Maps a unique sort name to its slice data. + static std::map m_slicePredicates; +}; + +} diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 363910edd1e5..393aa1022573 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -23,6 +23,10 @@ #include +#ifdef HAVE_Z3_DLOPEN +#include +#endif + using namespace std; using namespace solidity; using namespace solidity::util; @@ -34,10 +38,11 @@ BMC::BMC( ErrorReporter& _errorReporter, map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers + smtutil::SMTSolverChoice _enabledSolvers, + optional _timeout ): SMTEncoder(_context), - m_interface(make_unique(_smtlib2Responses, _smtCallback, _enabledSolvers)), + m_interface(make_unique(_smtlib2Responses, _smtCallback, _enabledSolvers, _timeout)), m_outerErrorReporter(_errorReporter) { #if defined (HAVE_Z3) || defined (HAVE_CVC4) @@ -57,13 +62,18 @@ void BMC::analyze(SourceUnit const& _source, mapsolvers() > 0, ""); // If this check is true, Z3 and CVC4 are not available @@ -77,7 +87,10 @@ void BMC::analyze(SourceUnit const& _source, map(*_funCall.expression().annotation().type); if (funType.kind() == FunctionType::Kind::External) - { - auto memberAccess = dynamic_cast(&_funCall.expression()); - if (!memberAccess) - return false; - - auto identifier = dynamic_cast(&memberAccess->expression()); - if (!( - identifier && - identifier->name() == "this" && - identifier->annotation().referencedDeclaration && - dynamic_cast(identifier->annotation().referencedDeclaration) - )) - return false; - } + return isTrustedExternalCall(&_funCall.expression()); else if (funType.kind() != FunctionType::Kind::Internal) return false; @@ -132,7 +132,10 @@ void BMC::endVisit(ContractDefinition const& _contract) constructor->accept(*this); else { + /// Visiting implicit constructor - we need a dummy callstack frame + pushCallStack({nullptr, nullptr}); inlineConstructorHierarchy(_contract); + popCallStack(); /// Check targets created by state variable initialization. smtutil::Expression constraints = m_context.assertions(); checkVerificationTargets(constraints); @@ -158,6 +161,9 @@ bool BMC::visit(FunctionDefinition const& _function) resetStateVariables(); } + if (_function.isConstructor()) + inlineConstructorHierarchy(dynamic_cast(*_function.scope())); + /// Already visits the children. SMTEncoder::visit(_function); @@ -171,6 +177,7 @@ void BMC::endVisit(FunctionDefinition const& _function) smtutil::Expression constraints = m_context.assertions(); checkVerificationTargets(constraints); m_verificationTargets.clear(); + m_pathConditions.clear(); } SMTEncoder::endVisit(_function); @@ -193,7 +200,44 @@ bool BMC::visit(IfStatement const& _node) ); m_context.popSolver(); - SMTEncoder::visit(_node); + _node.condition().accept(*this); + auto conditionExpr = expr(_node.condition()); + // visit true branch + auto [indicesEndTrue, trueEndPathCondition] = visitBranch(&_node.trueStatement(), conditionExpr); + auto touchedVars = touchedVariables(_node.trueStatement()); + + // visit false branch + decltype(indicesEndTrue) indicesEndFalse; + auto falseEndPathCondition = currentPathConditions() && !conditionExpr; + if (_node.falseStatement()) + { + std::tie(indicesEndFalse, falseEndPathCondition) = visitBranch(_node.falseStatement(), !conditionExpr); + touchedVars += touchedVariables(*_node.falseStatement()); + } + else + indicesEndFalse = copyVariableIndices(); + + // merge the information from branches + setPathCondition(trueEndPathCondition || falseEndPathCondition); + mergeVariables(touchedVars, expr(_node.condition()), indicesEndTrue, indicesEndFalse); + + return false; +} + +bool BMC::visit(Conditional const& _op) +{ + m_context.pushSolver(); + _op.condition().accept(*this); + + if (isRootFunction()) + addVerificationTarget( + VerificationTarget::Type::ConstantCondition, + expr(_op.condition()), + &_op.condition() + ); + m_context.popSolver(); + + SMTEncoder::visit(_op); return false; } @@ -215,7 +259,7 @@ bool BMC::visit(WhileStatement const& _node) decltype(indicesBeforeLoop) indicesAfterLoop; if (_node.isDoWhile()) { - indicesAfterLoop = visitBranch(&_node.body()); + indicesAfterLoop = visitBranch(&_node.body()).first; // TODO the assertions generated in the body should still be active in the condition _node.condition().accept(*this); if (isRootFunction()) @@ -235,7 +279,7 @@ bool BMC::visit(WhileStatement const& _node) &_node.condition() ); - indicesAfterLoop = visitBranch(&_node.body(), expr(_node.condition())); + indicesAfterLoop = visitBranch(&_node.body(), expr(_node.condition())).first; } // We reset the execution to before the loop @@ -312,33 +356,19 @@ void BMC::endVisit(UnaryOperation const& _op) ) return; - switch (_op.getOperator()) - { - case Token::Inc: // ++ (pre- or postfix) - case Token::Dec: // -- (pre- or postfix) + if (_op.getOperator() == Token::Sub && smt::isInteger(*_op.annotation().type)) addVerificationTarget( VerificationTarget::Type::UnderOverflow, expr(_op), &_op ); - break; - case Token::Sub: // - - if (_op.annotation().type->category() == Type::Category::Integer) - addVerificationTarget( - VerificationTarget::Type::UnderOverflow, - expr(_op), - &_op - ); - break; - default: - break; - } } void BMC::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + auto functionCallKind = *_funCall.annotation().kind; + + if (functionCallKind != FunctionCallKind::FunctionCall) { SMTEncoder::endVisit(_funCall); return; @@ -366,20 +396,9 @@ void BMC::endVisit(FunctionCall const& _funCall) SMTEncoder::endVisit(_funCall); internalOrExternalFunctionCall(_funCall); break; - case FunctionType::Kind::KECCAK256: - case FunctionType::Kind::ECRecover: - case FunctionType::Kind::SHA256: - case FunctionType::Kind::RIPEMD160: - case FunctionType::Kind::BlockHash: - case FunctionType::Kind::AddMod: - case FunctionType::Kind::MulMod: - SMTEncoder::endVisit(_funCall); - abstractFunctionCall(_funCall); - break; case FunctionType::Kind::Send: case FunctionType::Kind::Transfer: { - SMTEncoder::endVisit(_funCall); auto value = _funCall.arguments().front(); solAssert(value, ""); smtutil::Expression thisBalance = m_context.state().balance(); @@ -389,14 +408,30 @@ void BMC::endVisit(FunctionCall const& _funCall) thisBalance < expr(*value), &_funCall ); + + SMTEncoder::endVisit(_funCall); break; } + case FunctionType::Kind::KECCAK256: + case FunctionType::Kind::ECRecover: + case FunctionType::Kind::SHA256: + case FunctionType::Kind::RIPEMD160: + case FunctionType::Kind::BlockHash: + case FunctionType::Kind::AddMod: + case FunctionType::Kind::MulMod: + [[fallthrough]]; default: SMTEncoder::endVisit(_funCall); break; } } +void BMC::endVisit(Return const& _return) +{ + SMTEncoder::endVisit(_return); + setPathCondition(smtutil::Expression(false)); +} + /// Visitor helpers. void BMC::visitAssert(FunctionCall const& _funCall) @@ -424,6 +459,18 @@ void BMC::visitRequire(FunctionCall const& _funCall) ); } +void BMC::visitAddMulMod(FunctionCall const& _funCall) +{ + solAssert(_funCall.arguments().at(2), ""); + addVerificationTarget( + VerificationTarget::Type::DivByZero, + expr(*_funCall.arguments().at(2)), + &_funCall + ); + + SMTEncoder::visitAddMulMod(_funCall); +} + void BMC::inlineFunctionCall(FunctionCall const& _funCall) { solAssert(shouldInlineFunctionCall(_funCall), ""); @@ -446,32 +493,29 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall) // The reason why we need to pushCallStack here instead of visit(FunctionDefinition) // is that there we don't have `_funCall`. pushCallStack({funDef, &_funCall}); + pushPathCondition(currentPathConditions()); funDef->accept(*this); + popPathCondition(); } createReturnedExpressions(_funCall); } -void BMC::abstractFunctionCall(FunctionCall const& _funCall) -{ - vector smtArguments; - for (auto const& arg: _funCall.arguments()) - smtArguments.push_back(expr(*arg)); - defineExpr(_funCall, (*m_context.expression(_funCall.expression()))(smtArguments)); - m_uninterpretedTerms.insert(&_funCall); - setSymbolicUnknownValue(expr(_funCall), _funCall.annotation().type, m_context); -} - void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall) { auto const& funType = dynamic_cast(*_funCall.expression().annotation().type); if (shouldInlineFunctionCall(_funCall)) inlineFunctionCall(_funCall); + else if (isPublicGetter(_funCall.expression())) + { + // Do nothing here. + // The processing happens in SMT Encoder, but we need to prevent the resetting of the state variables. + } else if (funType.kind() == FunctionType::Kind::Internal) m_errorReporter.warning( 5729_error, _funCall.location(), - "Assertion checker does not yet implement this type of function call." + "BMC does not yet implement this type of function call." ); else { @@ -498,8 +542,37 @@ pair BMC::arithmeticOperation( auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression); + auto const* intType = dynamic_cast(_commonType); + if (!intType) + intType = TypeProvider::uint256(); + + // Mod does not need underflow/overflow checks. + if (_op == Token::Mod) + return values; + + VerificationTarget::Type type; + // The order matters here: + // If _op is Div and intType is signed, we only care about overflow. + if (_op == Token::Div) + { + if (intType->isSigned()) + // Signed division can only overflow. + type = VerificationTarget::Type::Overflow; + else + // Unsigned division cannot underflow/overflow. + return values; + } + else if (intType->isSigned()) + type = VerificationTarget::Type::UnderOverflow; + else if (_op == Token::Sub) + type = VerificationTarget::Type::Underflow; + else if (_op == Token::Add || _op == Token::Mul) + type = VerificationTarget::Type::Overflow; + else + solAssert(false, ""); + addVerificationTarget( - VerificationTarget::Type::UnderOverflow, + type, values.second, &_expression ); @@ -532,7 +605,7 @@ pair, vector> BMC::modelExpressions() auto const& type = var.second->type(); if ( type->isValueType() && - smt::smtKind(type->category()) != smtutil::Kind::Function + smt::smtKind(*type) != smtutil::Kind::Function ) { expressionsToEvaluate.emplace_back(var.second->currentValue()); @@ -605,12 +678,19 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smtutil::Expression con _target.type == VerificationTarget::Type::UnderOverflow, "" ); - IntegerType const* intType = nullptr; - if (auto const* type = dynamic_cast(_target.expression->annotation().type)) - intType = type; - else + + if ( + m_solvedTargets.count(_target.expression) && ( + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Underflow) || + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow) + ) + ) + return; + + auto const* intType = dynamic_cast(_target.expression->annotation().type); + if (!intType) intType = TypeProvider::uint256(); - solAssert(intType, ""); + checkCondition( _target.constraints && _constraints && _target.value < smt::minValue(*intType), _target.callStack, @@ -631,13 +711,19 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smtutil::Expression cons _target.type == VerificationTarget::Type::UnderOverflow, "" ); - IntegerType const* intType = nullptr; - if (auto const* type = dynamic_cast(_target.expression->annotation().type)) - intType = type; - else + + if ( + m_solvedTargets.count(_target.expression) && ( + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::Overflow) || + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::UnderOverflow) + ) + ) + return; + + auto const* intType = dynamic_cast(_target.expression->annotation().type); + if (!intType) intType = TypeProvider::uint256(); - solAssert(intType, ""); checkCondition( _target.constraints && _constraints && _target.value > smt::maxValue(*intType), _target.callStack, @@ -654,6 +740,13 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smtutil::Expression cons void BMC::checkDivByZero(BMCVerificationTarget& _target) { solAssert(_target.type == VerificationTarget::Type::DivByZero, ""); + + if ( + m_solvedTargets.count(_target.expression) && + m_solvedTargets.at(_target.expression).count(VerificationTarget::Type::DivByZero) + ) + return; + checkCondition( _target.constraints && (_target.value == 0), _target.callStack, @@ -761,7 +854,7 @@ void BMC::checkCondition( "\nNote that some information is erased after the execution of loops.\n" "You can re-introduce information using require()."; if (m_externalFunctionCallHappened) - extraComment+= + extraComment += "\nNote that external function calls are not inlined," " even if the source code of the function is available." " This is due to the possibility that the actual called contract" @@ -774,46 +867,40 @@ void BMC::checkCondition( { case smtutil::CheckResult::SATISFIABLE: { + solAssert(!_callStack.empty(), ""); std::ostringstream message; - message << _description << " happens here"; - if (_callStack.size()) - { - std::ostringstream modelMessage; - modelMessage << " for:\n"; - solAssert(values.size() == expressionNames.size(), ""); - map sortedModel; - for (size_t i = 0; i < values.size(); ++i) - if (expressionsToEvaluate.at(i).name != values.at(i)) - sortedModel[expressionNames.at(i)] = values.at(i); - - for (auto const& eval: sortedModel) - modelMessage << " " << eval.first << " = " << eval.second << "\n"; - m_errorReporter.warning( - _errorHappens, - _location, - message.str(), - SecondarySourceLocation().append(modelMessage.str(), SourceLocation{}) - .append(SMTEncoder::callStackMessage(_callStack)) - .append(move(secondaryLocation)) - ); - } - else - { - message << "."; - m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation); - } + message << "BMC: " << _description << " happens here."; + std::ostringstream modelMessage; + modelMessage << "Counterexample:\n"; + solAssert(values.size() == expressionNames.size(), ""); + map sortedModel; + for (size_t i = 0; i < values.size(); ++i) + if (expressionsToEvaluate.at(i).name != values.at(i)) + sortedModel[expressionNames.at(i)] = values.at(i); + + for (auto const& eval: sortedModel) + modelMessage << " " << eval.first << " = " << eval.second << "\n"; + + m_errorReporter.warning( + _errorHappens, + _location, + message.str(), + SecondarySourceLocation().append(modelMessage.str(), SourceLocation{}) + .append(SMTEncoder::callStackMessage(_callStack)) + .append(move(secondaryLocation)) + ); break; } case smtutil::CheckResult::UNSATISFIABLE: break; case smtutil::CheckResult::UNKNOWN: - m_errorReporter.warning(_errorMightHappen, _location, _description + " might happen here.", secondaryLocation); + m_errorReporter.warning(_errorMightHappen, _location, "BMC: " + _description + " might happen here.", secondaryLocation); break; case smtutil::CheckResult::CONFLICTING: - m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); + m_errorReporter.warning(1584_error, _location, "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound."); break; case smtutil::CheckResult::ERROR: - m_errorReporter.warning(1823_error, _location, "Error trying to invoke SMT solver."); + m_errorReporter.warning(1823_error, _location, "BMC: Error trying to invoke SMT solver."); break; } @@ -842,9 +929,9 @@ void BMC::checkBooleanNotConstant( m_interface->pop(); if (positiveResult == smtutil::CheckResult::ERROR || negatedResult == smtutil::CheckResult::ERROR) - m_errorReporter.warning(8592_error, _condition.location(), "Error trying to invoke SMT solver."); + m_errorReporter.warning(8592_error, _condition.location(), "BMC: Error trying to invoke SMT solver."); else if (positiveResult == smtutil::CheckResult::CONFLICTING || negatedResult == smtutil::CheckResult::CONFLICTING) - m_errorReporter.warning(3356_error, _condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound."); + m_errorReporter.warning(3356_error, _condition.location(), "BMC: At least two SMT solvers provided conflicting answers. Results might not be sound."); else if (positiveResult == smtutil::CheckResult::SATISFIABLE && negatedResult == smtutil::CheckResult::SATISFIABLE) { // everything fine. @@ -854,20 +941,20 @@ void BMC::checkBooleanNotConstant( // can't do anything. } else if (positiveResult == smtutil::CheckResult::UNSATISFIABLE && negatedResult == smtutil::CheckResult::UNSATISFIABLE) - m_errorReporter.warning(2512_error, _condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack)); + m_errorReporter.warning(2512_error, _condition.location(), "BMC: Condition unreachable.", SMTEncoder::callStackMessage(_callStack)); else { string description; if (positiveResult == smtutil::CheckResult::SATISFIABLE) { solAssert(negatedResult == smtutil::CheckResult::UNSATISFIABLE, ""); - description = "Condition is always true."; + description = "BMC: Condition is always true."; } else { solAssert(positiveResult == smtutil::CheckResult::UNSATISFIABLE, ""); solAssert(negatedResult == smtutil::CheckResult::SATISFIABLE, ""); - description = "Condition is always false."; + description = "BMC: Condition is always false."; } m_errorReporter.warning( 6838_error, @@ -889,7 +976,7 @@ BMC::checkSatisfiableAndGenerateModel(vector const& _expres } catch (smtutil::SolverError const& _e) { - string description("Error querying SMT solver"); + string description("BMC: Error querying SMT solver"); if (_e.comment()) description += ": " + *_e.comment(); m_errorReporter.warning(8140_error, description); @@ -914,3 +1001,14 @@ smtutil::CheckResult BMC::checkSatisfiable() return checkSatisfiableAndGenerateModel({}).first; } +void BMC::assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value) +{ + auto oldVar = _symVar.currentValue(); + auto newVar = _symVar.increaseIndex(); + m_context.addAssertion(smtutil::Expression::ite( + currentPathConditions(), + newVar == _value, + newVar == oldVar + )); +} + diff --git a/libsolidity/formal/BMC.h b/libsolidity/formal/BMC.h index 324595bbaf6c..3bdd0caa9168 100644 --- a/libsolidity/formal/BMC.h +++ b/libsolidity/formal/BMC.h @@ -61,7 +61,8 @@ class BMC: public SMTEncoder langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers + smtutil::SMTSolverChoice _enabledSolvers, + std::optional timeout ); void analyze(SourceUnit const& _sources, std::map> _solvedTargets); @@ -84,21 +85,23 @@ class BMC: public SMTEncoder bool visit(FunctionDefinition const& _node) override; void endVisit(FunctionDefinition const& _node) override; bool visit(IfStatement const& _node) override; + bool visit(Conditional const& _node) override; bool visit(WhileStatement const& _node) override; bool visit(ForStatement const& _node) override; void endVisit(UnaryOperation const& _node) override; void endVisit(FunctionCall const& _node) override; + void endVisit(Return const& _node) override; //@} /// Visitor helpers. //@{ void visitAssert(FunctionCall const& _funCall); void visitRequire(FunctionCall const& _funCall); + void visitAddMulMod(FunctionCall const& _funCall) override; + void assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value) override; /// Visits the FunctionDefinition of the called function /// if available and inlines the return value. void inlineFunctionCall(FunctionCall const& _funCall); - /// Creates an uninterpreted function call. - void abstractFunctionCall(FunctionCall const& _funCall); /// Inlines if the function call is internal or external to `this`. /// Erases knowledge about state variables if external. void internalOrExternalFunctionCall(FunctionCall const& _funCall); diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 2615cf9d78bd..bc20f33198f3 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -22,6 +22,9 @@ #include #endif +#include +#include +#include #include #include @@ -29,9 +32,12 @@ #include #include -#include #include +#ifdef HAVE_Z3_DLOPEN +#include +#endif + #include using namespace std; @@ -40,74 +46,78 @@ using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::smtutil; using namespace solidity::frontend; +using namespace solidity::frontend::smt; CHC::CHC( - smt::EncodingContext& _context, + EncodingContext& _context, ErrorReporter& _errorReporter, - map const& _smtlib2Responses, - ReadCallback::Callback const& _smtCallback, - [[maybe_unused]] smtutil::SMTSolverChoice _enabledSolvers + [[maybe_unused]] map const& _smtlib2Responses, + [[maybe_unused]] ReadCallback::Callback const& _smtCallback, + SMTSolverChoice _enabledSolvers, + optional _timeout ): SMTEncoder(_context), m_outerErrorReporter(_errorReporter), - m_enabledSolvers(_enabledSolvers) + m_enabledSolvers(_enabledSolvers), + m_queryTimeout(_timeout) { + bool usesZ3 = _enabledSolvers.z3; #ifdef HAVE_Z3 - if (_enabledSolvers.z3) - m_interface = make_unique(); + usesZ3 = usesZ3 && Z3Interface::available(); +#else + usesZ3 = false; #endif - if (!m_interface) - m_interface = make_unique(_smtlib2Responses, _smtCallback); + if (!usesZ3) + m_interface = make_unique(_smtlib2Responses, _smtCallback, m_queryTimeout); } void CHC::analyze(SourceUnit const& _source) { solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), ""); - bool usesZ3 = false; -#ifdef HAVE_Z3 - usesZ3 = m_enabledSolvers.z3; - if (usesZ3) + /// This is currently used to abort analysis of SourceUnits + /// containing file level functions or constants. + if (SMTEncoder::analyze(_source)) { - auto z3Interface = dynamic_cast(m_interface.get()); - solAssert(z3Interface, ""); - m_context.setSolver(z3Interface->z3Interface()); + resetSourceAnalysis(); + + set sources; + sources.insert(&_source); + for (auto const& source: _source.referencedSourceUnits(true)) + sources.insert(source); + for (auto const* source: sources) + defineInterfacesAndSummaries(*source); + for (auto const* source: sources) + source->accept(*this); + + checkVerificationTargets(); } -#endif - if (!usesZ3) + + bool ranSolver = true; + if (auto const* smtLibInterface = dynamic_cast(m_interface.get())) + ranSolver = smtLibInterface->unhandledQueries().empty(); + if (!ranSolver && !m_noSolverWarning) { - auto smtlib2Interface = dynamic_cast(m_interface.get()); - solAssert(smtlib2Interface, ""); - m_context.setSolver(smtlib2Interface->smtlib2Interface()); + m_noSolverWarning = true; + m_outerErrorReporter.warning( + 3996_error, + SourceLocation(), +#ifdef HAVE_Z3_DLOPEN + "CHC analysis was not possible since libz3.so." + to_string(Z3_MAJOR_VERSION) + "." + to_string(Z3_MINOR_VERSION) + " was not found." +#else + "CHC analysis was not possible since no integrated z3 SMT solver was found." +#endif + ); } - m_context.clear(); - m_context.setAssertionAccumulation(false); - m_variableUsage.setFunctionInlining(false); - - resetSourceAnalysis(); - - auto genesisSort = make_shared( - vector(), - smtutil::SortProvider::boolSort - ); - m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); - addRule(genesis(), "genesis"); - - set sources; - sources.insert(&_source); - for (auto const& source: _source.referencedSourceUnits(true)) - sources.insert(source); - for (auto const* source: sources) - defineInterfacesAndSummaries(*source); - for (auto const* source: sources) - source->accept(*this); + else + m_outerErrorReporter.append(m_errorReporter.errors()); - checkVerificationTargets(); + m_errorReporter.clear(); } vector CHC::unhandledQueries() const { - if (auto smtlib2 = dynamic_cast(m_interface.get())) + if (auto smtlib2 = dynamic_cast(m_interface.get())) return smtlib2->unhandledQueries(); return {}; @@ -116,21 +126,11 @@ vector CHC::unhandledQueries() const bool CHC::visit(ContractDefinition const& _contract) { resetContractAnalysis(); - initContract(_contract); - - m_stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); - m_stateSorts = stateSorts(_contract); - clearIndices(&_contract); - string suffix = _contract.name() + "_" + to_string(_contract.id()); - m_errorPredicate = createSymbolicBlock(arity0FunctionSort(), "error_" + suffix); - m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix); - m_symbolFunction[m_constructorSummaryPredicate->currentFunctionValue().name] = &_contract; - m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix); - auto stateExprs = currentStateVariables(); - setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); + m_stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); + solAssert(m_currentContract, ""); SMTEncoder::visit(_contract); return false; @@ -138,24 +138,66 @@ bool CHC::visit(ContractDefinition const& _contract) void CHC::endVisit(ContractDefinition const& _contract) { - auto implicitConstructor = (*m_implicitConstructorPredicate)({}); - connectBlocks(genesis(), implicitConstructor); - m_currentBlock = implicitConstructor; - m_context.addAssertion(m_error.currentValue() == 0); - if (auto constructor = _contract.constructor()) constructor->accept(*this); - else - inlineConstructorHierarchy(_contract); - connectBlocks(m_currentBlock, summary(_contract), m_error.currentValue() == 0); + defineContractInitializer(_contract); + + auto const& entry = *createConstructorBlock(_contract, "implicit_constructor_entry"); + + // In case constructors use uninitialized state variables, + // they need to be zeroed. + // This is not part of `initialConstraints` because it's only true here, + // at the beginning of the deployment routine. + smtutil::Expression zeroes(true); + for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract)) + zeroes = zeroes && currentValue(*var) == smt::zeroValue(var->type()); + addRule(smtutil::Expression::implies(initialConstraints(_contract) && zeroes, predicate(entry)), entry.functor().name); + setCurrentBlock(entry); + + solAssert(!m_errorDest, ""); + m_errorDest = m_constructorSummaries.at(&_contract); + // We need to evaluate the base constructor calls (arguments) from derived -> base + auto baseArgs = baseArguments(_contract); + for (auto base: _contract.annotation().linearizedBaseContracts) + { + if (base != &_contract) + { + m_callGraph[&_contract].insert(base); - clearIndices(m_currentContract, nullptr); - vector symbArgs = currentFunctionVariables(*m_currentContract); - setCurrentBlock(*m_constructorSummaryPredicate, &symbArgs); + auto baseConstructor = base->constructor(); + if (baseConstructor && baseArgs.count(base)) + { + vector> const& args = baseArgs.at(base); + auto const& params = baseConstructor->parameters(); + solAssert(params.size() == args.size(), ""); + for (unsigned i = 0; i < params.size(); ++i) + { + args.at(i)->accept(*this); + if (params.at(i)) + { + solAssert(m_context.knownVariable(*params.at(i)), ""); + m_context.addAssertion(currentValue(*params.at(i)) == expr(*args.at(i), params.at(i)->type())); + } + } + } + } + } + m_errorDest = nullptr; + // Then call initializer_Base from base -> derived + for (auto base: _contract.annotation().linearizedBaseContracts | boost::adaptors::reversed) + { + errorFlag().increaseIndex(); + m_context.addAssertion(smt::constructorCall(*m_contractInitializers.at(base), m_context)); + connectBlocks(m_currentBlock, summary(_contract), errorFlag().currentValue() > 0); + m_context.addAssertion(errorFlag().currentValue() == 0); + } + + connectBlocks(m_currentBlock, summary(_contract)); - addAssertVerificationTarget(m_currentContract, m_currentBlock, smtutil::Expression(true), m_error.currentValue()); - connectBlocks(m_currentBlock, interface(), m_error.currentValue() == 0); + setCurrentBlock(*m_constructorSummaries.at(&_contract)); + m_queryPlaceholders[&_contract].push_back({smtutil::Expression(true), errorFlag().currentValue(), m_currentBlock}); + connectBlocks(m_currentBlock, interface(), errorFlag().currentValue() == 0); SMTEncoder::endVisit(_contract); } @@ -164,47 +206,35 @@ bool CHC::visit(FunctionDefinition const& _function) { if (!_function.isImplemented()) { - connectBlocks(genesis(), summary(_function)); - return false; - } - - // This is the case for base constructor inlining. - if (m_currentFunction) - { - solAssert(m_currentFunction->isConstructor(), ""); - solAssert(_function.isConstructor(), ""); - solAssert(_function.scope() != m_currentContract, ""); - SMTEncoder::visit(_function); + addRule(summary(_function), "summary_function_" + to_string(_function.id())); return false; } + // No inlining. solAssert(!m_currentFunction, "Function inlining should not happen in CHC."); m_currentFunction = &_function; initFunction(_function); - auto functionEntryBlock = createBlock(m_currentFunction); - auto bodyBlock = createBlock(&m_currentFunction->body()); + auto functionEntryBlock = createBlock(m_currentFunction, PredicateType::FunctionBlock); + auto bodyBlock = createBlock(&m_currentFunction->body(), PredicateType::FunctionBlock); - auto functionPred = predicate(*functionEntryBlock, currentFunctionVariables()); + auto functionPred = predicate(*functionEntryBlock); auto bodyPred = predicate(*bodyBlock); - if (_function.isConstructor()) - connectBlocks(m_currentBlock, functionPred); - else - connectBlocks(genesis(), functionPred); + addRule(functionPred, functionPred.name); - m_context.addAssertion(m_error.currentValue() == 0); - for (auto const* var: m_stateVariables) - m_context.addAssertion(m_context.variable(*var)->valueAtIndex(0) == currentValue(*var)); - for (auto const& var: _function.parameters()) - m_context.addAssertion(m_context.variable(*var)->valueAtIndex(0) == currentValue(*var)); + solAssert(m_currentContract, ""); + m_context.addAssertion(initialConstraints(*m_currentContract, &_function)); connectBlocks(functionPred, bodyPred); setCurrentBlock(*bodyBlock); + solAssert(!m_errorDest, ""); + m_errorDest = m_summaries.at(m_currentContract).at(&_function); SMTEncoder::visit(*m_currentFunction); + m_errorDest = nullptr; return false; } @@ -215,52 +245,29 @@ void CHC::endVisit(FunctionDefinition const& _function) return; solAssert(m_currentFunction && m_currentContract, ""); + // No inlining. + solAssert(m_currentFunction == &_function, ""); - // This is the case for base constructor inlining. - if (m_currentFunction != &_function) - { - solAssert(m_currentFunction && m_currentFunction->isConstructor(), ""); - solAssert(_function.isConstructor(), ""); - solAssert(_function.scope() != m_currentContract, ""); - } - else + connectBlocks(m_currentBlock, summary(_function)); + setCurrentBlock(*m_summaries.at(m_currentContract).at(&_function)); + + // Query placeholders for constructors are not created here because + // of contracts without constructors. + // Instead, those are created in endVisit(ContractDefinition). + if (!_function.isConstructor()) { - // We create an extra exit block for constructors that simply - // connects to the interface in case an explicit constructor - // exists in the hierarchy. - // It is not connected directly here, as normal functions are, - // because of the case where there are only implicit constructors. - // This is done in endVisit(ContractDefinition). - if (_function.isConstructor()) + auto sum = summary(_function); + auto ifacePre = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context); + if (_function.isPublic()) { - string suffix = m_currentContract->name() + "_" + to_string(m_currentContract->id()); - auto constructorExit = createSymbolicBlock(constructorSort(), "constructor_exit_" + suffix); - connectBlocks(m_currentBlock, predicate(*constructorExit, currentFunctionVariables(*m_currentContract))); - - clearIndices(m_currentContract, m_currentFunction); - auto stateExprs = currentFunctionVariables(*m_currentContract); - setCurrentBlock(*constructorExit, &stateExprs); + auto txConstraints = m_context.state().txConstraints(_function); + m_queryPlaceholders[&_function].push_back({txConstraints && sum, errorFlag().currentValue(), ifacePre}); + connectBlocks(ifacePre, interface(), txConstraints && sum && errorFlag().currentValue() == 0); } - else - { - auto assertionError = m_error.currentValue(); - auto sum = summary(_function); - connectBlocks(m_currentBlock, sum); - - auto iface = interface(); - - auto stateExprs = initialStateVariables(); - setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); - - if (_function.isPublic()) - { - addAssertVerificationTarget(&_function, m_currentBlock, sum, assertionError); - connectBlocks(m_currentBlock, iface, sum && (assertionError == 0)); - } - } - m_currentFunction = nullptr; } + m_currentFunction = nullptr; + SMTEncoder::endVisit(_function); } @@ -274,10 +281,10 @@ bool CHC::visit(IfStatement const& _if) solAssert(m_currentFunction, ""); auto const& functionBody = m_currentFunction->body(); - auto ifHeaderBlock = createBlock(&_if, "if_header_"); - auto trueBlock = createBlock(&_if.trueStatement(), "if_true_"); - auto falseBlock = _if.falseStatement() ? createBlock(_if.falseStatement(), "if_false_") : nullptr; - auto afterIfBlock = createBlock(&functionBody); + auto ifHeaderBlock = createBlock(&_if, PredicateType::FunctionBlock, "if_header_"); + auto trueBlock = createBlock(&_if.trueStatement(), PredicateType::FunctionBlock, "if_true_"); + auto falseBlock = _if.falseStatement() ? createBlock(_if.falseStatement(), PredicateType::FunctionBlock, "if_false_") : nullptr; + auto afterIfBlock = createBlock(&functionBody, PredicateType::FunctionBlock); connectBlocks(m_currentBlock, predicate(*ifHeaderBlock)); @@ -321,14 +328,14 @@ bool CHC::visit(WhileStatement const& _while) auto const& functionBody = m_currentFunction->body(); auto namePrefix = string(_while.isDoWhile() ? "do_" : "") + "while"; - auto loopHeaderBlock = createBlock(&_while, namePrefix + "_header_"); - auto loopBodyBlock = createBlock(&_while.body(), namePrefix + "_body_"); - auto afterLoopBlock = createBlock(&functionBody); + auto loopHeaderBlock = createBlock(&_while, PredicateType::FunctionBlock, namePrefix + "_header_"); + auto loopBodyBlock = createBlock(&_while.body(), PredicateType::FunctionBlock, namePrefix + "_body_"); + auto afterLoopBlock = createBlock(&functionBody, PredicateType::FunctionBlock); auto outerBreakDest = m_breakDest; auto outerContinueDest = m_continueDest; - m_breakDest = afterLoopBlock.get(); - m_continueDest = loopHeaderBlock.get(); + m_breakDest = afterLoopBlock; + m_continueDest = loopHeaderBlock; if (_while.isDoWhile()) _while.body().accept(*this); @@ -370,16 +377,16 @@ bool CHC::visit(ForStatement const& _for) solAssert(m_currentFunction, ""); auto const& functionBody = m_currentFunction->body(); - auto loopHeaderBlock = createBlock(&_for, "for_header_"); - auto loopBodyBlock = createBlock(&_for.body(), "for_body_"); - auto afterLoopBlock = createBlock(&functionBody); + auto loopHeaderBlock = createBlock(&_for, PredicateType::FunctionBlock, "for_header_"); + auto loopBodyBlock = createBlock(&_for.body(), PredicateType::FunctionBlock, "for_body_"); + auto afterLoopBlock = createBlock(&functionBody, PredicateType::FunctionBlock); auto postLoop = _for.loopExpression(); - auto postLoopBlock = postLoop ? createBlock(postLoop, "for_post_") : nullptr; + auto postLoopBlock = postLoop ? createBlock(postLoop, PredicateType::FunctionBlock, "for_post_") : nullptr; auto outerBreakDest = m_breakDest; auto outerContinueDest = m_continueDest; - m_breakDest = afterLoopBlock.get(); - m_continueDest = postLoop ? postLoopBlock.get() : loopHeaderBlock.get(); + m_breakDest = afterLoopBlock; + m_continueDest = postLoop ? postLoopBlock : loopHeaderBlock; if (auto init = _for.initializationExpression()) init->accept(*this); @@ -425,9 +432,9 @@ bool CHC::visit(ForStatement const& _for) void CHC::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); + auto functionCallKind = *_funCall.annotation().kind; - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + if (functionCallKind != FunctionCallKind::FunctionCall) { SMTEncoder::endVisit(_funCall); return; @@ -453,6 +460,9 @@ void CHC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::Creation: + SMTEncoder::endVisit(_funCall); + unknownFunctionCall(_funCall); + break; case FunctionType::Kind::KECCAK256: case FunctionType::Kind::ECRecover: case FunctionType::Kind::SHA256: @@ -460,9 +470,7 @@ void CHC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BlockHash: case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: - SMTEncoder::endVisit(_funCall); - unknownFunctionCall(_funCall); - break; + [[fallthrough]]; default: SMTEncoder::endVisit(_funCall); break; @@ -475,7 +483,9 @@ void CHC::endVisit(Break const& _break) { solAssert(m_breakDest, ""); connectBlocks(m_currentBlock, predicate(*m_breakDest)); - auto breakGhost = createBlock(&_break, "break_ghost_"); + + // Add an unreachable ghost node to collect unreachable statements after a break. + auto breakGhost = createBlock(&_break, PredicateType::FunctionBlock, "break_ghost_"); m_currentBlock = predicate(*breakGhost); } @@ -483,10 +493,68 @@ void CHC::endVisit(Continue const& _continue) { solAssert(m_continueDest, ""); connectBlocks(m_currentBlock, predicate(*m_continueDest)); - auto continueGhost = createBlock(&_continue, "continue_ghost_"); + + // Add an unreachable ghost node to collect unreachable statements after a continue. + auto continueGhost = createBlock(&_continue, PredicateType::FunctionBlock, "continue_ghost_"); m_currentBlock = predicate(*continueGhost); } +void CHC::endVisit(IndexRangeAccess const& _range) +{ + createExpr(_range); + + auto baseArray = dynamic_pointer_cast(m_context.expression(_range.baseExpression())); + auto sliceArray = dynamic_pointer_cast(m_context.expression(_range)); + solAssert(baseArray && sliceArray, ""); + + auto const& sliceData = ArraySlicePredicate::create(sliceArray->sort(), m_context); + if (!sliceData.first) + { + for (auto pred: sliceData.second.predicates) + m_interface->registerRelation(pred->functor()); + for (auto const& rule: sliceData.second.rules) + addRule(rule, ""); + } + + auto start = _range.startExpression() ? expr(*_range.startExpression()) : 0; + auto end = _range.endExpression() ? expr(*_range.endExpression()) : baseArray->length(); + auto slicePred = (*sliceData.second.predicates.at(0))({ + baseArray->elements(), + sliceArray->elements(), + start, + end + }); + + m_context.addAssertion(slicePred); + m_context.addAssertion(sliceArray->length() == end - start); +} + +void CHC::endVisit(Return const& _return) +{ + SMTEncoder::endVisit(_return); + + connectBlocks(m_currentBlock, predicate(*m_returnDests.back())); + + // Add an unreachable ghost node to collect unreachable statements after a return. + auto returnGhost = createBlock(&_return, PredicateType::FunctionBlock, "return_ghost_"); + m_currentBlock = predicate(*returnGhost); +} + +void CHC::pushInlineFrame(CallableDeclaration const& _callable) +{ + m_returnDests.push_back(createBlock(&_callable, PredicateType::FunctionBlock, "return_")); +} + +void CHC::popInlineFrame(CallableDeclaration const& _callable) +{ + solAssert(!m_returnDests.empty(), ""); + auto const& ret = *m_returnDests.back(); + solAssert(ret.programNode() == &_callable, ""); + connectBlocks(m_currentBlock, predicate(ret)); + setCurrentBlock(ret); + m_returnDests.pop_back(); +} + void CHC::visitAssert(FunctionCall const& _funCall) { auto const& args = _funCall.arguments(); @@ -495,23 +563,17 @@ void CHC::visitAssert(FunctionCall const& _funCall) solAssert(m_currentContract, ""); solAssert(m_currentFunction, ""); - if (m_currentFunction->isConstructor()) - m_functionAssertions[m_currentContract].insert(&_funCall); - else - m_functionAssertions[m_currentFunction].insert(&_funCall); + auto errorCondition = !m_context.expression(*args.front())->currentValue(); + verificationTargetEncountered(&_funCall, VerificationTarget::Type::Assert, errorCondition); +} - auto previousError = m_error.currentValue(); - m_error.increaseIndex(); +void CHC::visitAddMulMod(FunctionCall const& _funCall) +{ + solAssert(_funCall.arguments().at(2), ""); - connectBlocks( - m_currentBlock, - m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction), - currentPathConditions() && !m_context.expression(*args.front())->currentValue() && ( - m_error.currentValue() == newErrorId(_funCall) - ) - ); + verificationTargetEncountered(&_funCall, VerificationTarget::Type::DivByZero, expr(*_funCall.arguments().at(2)) == 0); - m_context.addAssertion(m_error.currentValue() == previousError); + SMTEncoder::visitAddMulMod(_funCall); } void CHC::internalFunctionCall(FunctionCall const& _funCall) @@ -533,23 +595,29 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall) m_context.addAssertion(interface(*contract)); } - auto previousError = m_error.currentValue(); - m_context.addAssertion(predicate(_funCall)); + solAssert(m_errorDest, ""); connectBlocks( m_currentBlock, - (m_currentFunction && !m_currentFunction->isConstructor()) ? summary(*m_currentFunction) : summary(*m_currentContract), - (m_error.currentValue() > 0) + predicate(*m_errorDest), + errorFlag().currentValue() > 0 ); - m_context.addAssertion(m_error.currentValue() == 0); - m_error.increaseIndex(); - m_context.addAssertion(m_error.currentValue() == previousError); + m_context.addAssertion(errorFlag().currentValue() == 0); } void CHC::externalFunctionCall(FunctionCall const& _funCall) { + /// In external function calls we do not add a "predicate call" + /// because we do not trust their function body anyway, + /// so we just add the nondet_interface predicate. + solAssert(m_currentContract, ""); + if (isTrustedExternalCall(&_funCall.expression())) + { + externalFunctionCallToTrustedCode(_funCall); + return; + } FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); auto kind = funType.kind(); @@ -562,19 +630,61 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) for (auto var: function->returnParameters()) m_context.variable(*var)->increaseIndex(); - auto preCallState = currentStateVariables(); + auto preCallState = vector{state().state()} + currentStateVariables(); bool usesStaticCall = kind == FunctionType::Kind::BareStaticCall || function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View; if (!usesStaticCall) + { + state().newState(); for (auto const* var: m_stateVariables) m_context.variable(*var)->increaseIndex(); + } - auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + currentStateVariables()); - m_symbolFunction[nondet.name] = &_funCall; + auto postCallState = vector{state().state()} + currentStateVariables(); + auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + postCallState); + // TODO this could instead add the summary of the called function, where that summary + // basically has the nondet interface of this summary as a constraint. m_context.addAssertion(nondet); - m_context.addAssertion(m_error.currentValue() == 0); + m_context.addAssertion(errorFlag().currentValue() == 0); +} + +void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall) +{ + solAssert(m_currentContract, ""); + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + auto kind = funType.kind(); + solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, ""); + + auto const* function = functionCallToDefinition(_funCall); + if (!function) + return; + + // External call creates a new transaction. + auto originalTx = state().tx(); + auto txOrigin = state().txMember("tx.origin"); + state().newTx(); + // set the transaction sender as this contract + m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress()); + // set the origin to be the current transaction origin + m_context.addAssertion(state().txMember("tx.origin") == txOrigin); + + smtutil::Expression pred = predicate(_funCall); + + auto txConstraints = m_context.state().txConstraints(*function); + m_context.addAssertion(pred && txConstraints); + // restore the original transaction data + state().newTx(); + m_context.addAssertion(originalTx == state().tx()); + + solAssert(m_errorDest, ""); + connectBlocks( + m_currentBlock, + predicate(*m_errorDest), + (errorFlag().currentValue() > 0) + ); + m_context.addAssertion(errorFlag().currentValue() == 0); } void CHC::unknownFunctionCall(FunctionCall const&) @@ -596,42 +706,102 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) auto memberAccess = dynamic_cast(&_arrayPop.expression()); solAssert(memberAccess, ""); - auto symbArray = dynamic_pointer_cast(m_context.expression(memberAccess->expression())); + auto symbArray = dynamic_pointer_cast(m_context.expression(memberAccess->expression())); solAssert(symbArray, ""); - auto previousError = m_error.currentValue(); - m_error.increaseIndex(); + verificationTargetEncountered(&_arrayPop, VerificationTarget::Type::PopEmptyArray, symbArray->length() <= 0); +} - addArrayPopVerificationTarget(&_arrayPop, m_error.currentValue()); - connectBlocks( - m_currentBlock, - m_currentFunction->isConstructor() ? summary(*m_currentContract) : summary(*m_currentFunction), - currentPathConditions() && symbArray->length() <= 0 && m_error.currentValue() == newErrorId(_arrayPop) - ); +pair CHC::arithmeticOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType, + frontend::Expression const& _expression +) +{ + if (_op == Token::Mod || _op == Token::Div) + verificationTargetEncountered(&_expression, VerificationTarget::Type::DivByZero, _right == 0); + + auto values = SMTEncoder::arithmeticOperation(_op, _left, _right, _commonType, _expression); + + IntegerType const* intType = nullptr; + if (auto const* type = dynamic_cast(_commonType)) + intType = type; + else + intType = TypeProvider::uint256(); + + // Mod does not need underflow/overflow checks. + // Div only needs overflow check for signed types. + if (_op == Token::Mod || (_op == Token::Div && !intType->isSigned())) + return values; - m_context.addAssertion(m_error.currentValue() == previousError); + if (_op == Token::Div) + verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue()); + else if (intType->isSigned()) + { + verificationTargetEncountered(&_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue()); + verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue()); + } + else if (_op == Token::Sub) + verificationTargetEncountered(&_expression, VerificationTarget::Type::Underflow, values.second < intType->minValue()); + else if (_op == Token::Add || _op == Token::Mul) + verificationTargetEncountered(&_expression, VerificationTarget::Type::Overflow, values.second > intType->maxValue()); + else + solAssert(false, ""); + return values; } void CHC::resetSourceAnalysis() { - m_verificationTargets.clear(); m_safeTargets.clear(); m_unsafeTargets.clear(); - m_functionAssertions.clear(); - m_errorIds.clear(); + m_functionTargetIds.clear(); + m_verificationTargets.clear(); + m_queryPlaceholders.clear(); m_callGraph.clear(); m_summaries.clear(); - m_symbolFunction.clear(); + m_interfaces.clear(); + m_nondetInterfaces.clear(); + m_constructorSummaries.clear(); + m_contractInitializers.clear(); + Predicate::reset(); + ArraySlicePredicate::reset(); + m_blockCounter = 0; + + bool usesZ3 = false; +#ifdef HAVE_Z3 + usesZ3 = m_enabledSolvers.z3 && Z3Interface::available(); + if (usesZ3) + { + /// z3::fixedpoint does not have a reset mechanism, so we need to create another. + m_interface.reset(new Z3CHCInterface(m_queryTimeout)); + auto z3Interface = dynamic_cast(m_interface.get()); + solAssert(z3Interface, ""); + m_context.setSolver(z3Interface->z3Interface()); + } +#endif + if (!usesZ3) + { + auto smtlib2Interface = dynamic_cast(m_interface.get()); + smtlib2Interface->reset(); + solAssert(smtlib2Interface, ""); + m_context.setSolver(smtlib2Interface->smtlib2Interface()); + } + + m_context.clear(); + m_context.resetUniqueId(); + m_context.setAssertionAccumulation(false); } void CHC::resetContractAnalysis() { - m_stateSorts.clear(); m_stateVariables.clear(); m_unknownFunctionCallSeen = false; m_breakDest = nullptr; m_continueDest = nullptr; - m_error.resetIndex(); + m_returnDests.clear(); + errorFlag().resetIndex(); } void CHC::eraseKnowledge() @@ -651,173 +821,52 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c { for (auto const& var: _function->parameters() + _function->returnParameters()) m_context.variable(*var)->increaseIndex(); - for (auto const& var: _function->localVariables()) + for (auto const& var: localVariablesIncludingModifiers(*_function)) m_context.variable(*var)->increaseIndex(); } + + state().newState(); } -void CHC::setCurrentBlock( - smt::SymbolicFunctionVariable const& _block, - vector const* _arguments -) +void CHC::setCurrentBlock(Predicate const& _block) { if (m_context.solverStackHeigh() > 0) m_context.popSolver(); solAssert(m_currentContract, ""); clearIndices(m_currentContract, m_currentFunction); m_context.pushSolver(); - if (_arguments) - m_currentBlock = predicate(_block, *_arguments); - else - m_currentBlock = predicate(_block); + m_currentBlock = predicate(_block); } -set CHC::transactionAssertions(ASTNode const* _txRoot) +set CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot) { - set assertions; + set verificationTargetsIds; solidity::util::BreadthFirstSearch{{_txRoot}}.run([&](auto const* function, auto&& _addChild) { - assertions.insert(m_functionAssertions[function].begin(), m_functionAssertions[function].end()); + verificationTargetsIds.insert(m_functionTargetIds[function].begin(), m_functionTargetIds[function].end()); for (auto const* called: m_callGraph[function]) _addChild(called); }); - return assertions; -} - -vector CHC::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract) -{ - return fold( - _contract.annotation().linearizedBaseContracts, - vector{}, - [](auto&& _acc, auto _contract) { return _acc + _contract->stateVariables(); } - ); -} - -vector CHC::stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function) -{ - return stateVariablesIncludingInheritedAndPrivate(dynamic_cast(*_function.scope())); -} - -vector CHC::stateSorts(ContractDefinition const& _contract) -{ - return applyMap( - stateVariablesIncludingInheritedAndPrivate(_contract), - [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); } - ); -} - -smtutil::SortPointer CHC::constructorSort() -{ - solAssert(m_currentContract, ""); - if (auto const* constructor = m_currentContract->constructor()) - return sort(*constructor); - - return make_shared( - vector{smtutil::SortProvider::uintSort} + m_stateSorts, - smtutil::SortProvider::boolSort - ); -} - -smtutil::SortPointer CHC::interfaceSort() -{ - solAssert(m_currentContract, ""); - return interfaceSort(*m_currentContract); -} - -smtutil::SortPointer CHC::nondetInterfaceSort() -{ - solAssert(m_currentContract, ""); - return nondetInterfaceSort(*m_currentContract); + return verificationTargetsIds; } -smtutil::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) +SortPointer CHC::sort(FunctionDefinition const& _function) { - return make_shared( - stateSorts(_contract), - smtutil::SortProvider::boolSort - ); -} - -smtutil::SortPointer CHC::nondetInterfaceSort(ContractDefinition const& _contract) -{ - auto sorts = stateSorts(_contract); - return make_shared( - sorts + sorts, - smtutil::SortProvider::boolSort - ); -} - -smtutil::SortPointer CHC::arity0FunctionSort() -{ - return make_shared( - vector(), - smtutil::SortProvider::boolSort - ); -} - -/// A function in the symbolic CFG requires: -/// - Index of failed assertion. 0 means no assertion failed. -/// - 2 sets of state variables: -/// - State variables at the beginning of the current function, immutable -/// - Current state variables -/// At the beginning of the function these must equal set 1 -/// - 2 sets of input variables: -/// - Input variables at the beginning of the current function, immutable -/// - Current input variables -/// At the beginning of the function these must equal set 1 -/// - 1 set of output variables -smtutil::SortPointer CHC::sort(FunctionDefinition const& _function) -{ - auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; - auto inputSorts = applyMap(_function.parameters(), smtSort); - auto outputSorts = applyMap(_function.returnParameters(), smtSort); - return make_shared( - vector{smtutil::SortProvider::uintSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, - smtutil::SortProvider::boolSort - ); + return functionBodySort(_function, m_currentContract, state()); } -smtutil::SortPointer CHC::sort(ASTNode const* _node) +SortPointer CHC::sort(ASTNode const* _node) { if (auto funDef = dynamic_cast(_node)) return sort(*funDef); - auto fSort = dynamic_pointer_cast(sort(*m_currentFunction)); - solAssert(fSort, ""); - - auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; - return make_shared( - fSort->domain + applyMap(m_currentFunction->localVariables(), smtSort), - smtutil::SortProvider::boolSort - ); -} - -smtutil::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractDefinition const& _contract) -{ - auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); - auto sorts = stateSorts(_contract); - - auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; - auto inputSorts = applyMap(_function.parameters(), smtSort); - auto outputSorts = applyMap(_function.returnParameters(), smtSort); - return make_shared( - vector{smtutil::SortProvider::uintSort} + - sorts + - inputSorts + - sorts + - inputSorts + - outputSorts, - smtutil::SortProvider::boolSort - ); + solAssert(m_currentFunction, ""); + return functionBodySort(*m_currentFunction, m_currentContract, state()); } -unique_ptr CHC::createSymbolicBlock(smtutil::SortPointer _sort, string const& _name) +Predicate const* CHC::createSymbolicBlock(SortPointer _sort, string const& _name, PredicateType _predType, ASTNode const* _node) { - auto block = make_unique( - _sort, - _name, - m_context - ); - m_interface->registerRelation(block->currentFunctionValue()); + auto const* block = Predicate::create(_sort, _name, _predType, m_context, _node); + m_interface->registerRelation(block->functor()); return block; } @@ -825,30 +874,31 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) { for (auto const& node: _source.nodes()) if (auto const* contract = dynamic_cast(node.get())) - for (auto const* base: contract->annotation().linearizedBaseContracts) - { - string suffix = base->name() + "_" + to_string(base->id()); - m_interfaces[base] = createSymbolicBlock(interfaceSort(*base), "interface_" + suffix); - m_nondetInterfaces[base] = createSymbolicBlock(nondetInterfaceSort(*base), "nondet_interface_" + suffix); - - for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*base)) - if (!m_context.knownVariable(*var)) - createVariable(*var); - - /// Base nondeterministic interface that allows - /// 0 steps to be taken, used as base for the inductive - /// rule for each function. - auto const& iface = *m_nondetInterfaces.at(base); - auto state0 = stateVariablesAtIndex(0, *base); - addRule(iface(state0 + state0), "base_nondet"); + { + string suffix = contract->name() + "_" + to_string(contract->id()); + m_interfaces[contract] = createSymbolicBlock(interfaceSort(*contract, state()), "interface_" + suffix, PredicateType::Interface, contract); + m_nondetInterfaces[contract] = createSymbolicBlock(nondetInterfaceSort(*contract, state()), "nondet_interface_" + suffix, PredicateType::NondetInterface, contract); + m_constructorSummaries[contract] = createConstructorBlock(*contract, "summary_constructor"); + m_contractInitializers[contract] = createConstructorBlock(*contract, "contract_initializer"); + + for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*contract)) + if (!m_context.knownVariable(*var)) + createVariable(*var); + + /// Base nondeterministic interface that allows + /// 0 steps to be taken, used as base for the inductive + /// rule for each function. + auto const& iface = *m_nondetInterfaces.at(contract); + addRule(smt::nondetInterface(iface, *contract, m_context, 0, 0), "base_nondet"); + for (auto const* base: contract->annotation().linearizedBaseContracts) for (auto const* function: base->definedFunctions()) { for (auto var: function->parameters()) createVariable(*var); for (auto var: function->returnParameters()) createVariable(*var); - for (auto const* var: function->localVariables()) + for (auto const* var: localVariablesIncludingModifiers(*function)) createVariable(*var); m_summaries[contract].emplace(function, createSummaryBlock(*function, *contract)); @@ -860,37 +910,68 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) !base->isInterface() ) { - auto state1 = stateVariablesAtIndex(1, *base); - auto state2 = stateVariablesAtIndex(2, *base); + auto state1 = stateVariablesAtIndex(1, *contract); + auto state2 = stateVariablesAtIndex(2, *contract); - auto nondetPre = iface(state0 + state1); - auto nondetPost = iface(state0 + state2); + auto nondetPre = smt::nondetInterface(iface, *contract, m_context, 0, 1); + auto nondetPost = smt::nondetInterface(iface, *contract, m_context, 0, 2); - vector args{m_error.currentValue()}; + vector args{errorFlag().currentValue(), state().thisAddress(), state().crypto(), state().tx(), state().state(1)}; args += state1 + applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); }) + + vector{state().state(2)} + state2 + applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) + applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }); - connectBlocks(nondetPre, nondetPost, (*m_summaries.at(base).at(function))(args)); + connectBlocks(nondetPre, nondetPost, (*m_summaries.at(contract).at(function))(args)); } - } } + } +} + +void CHC::defineContractInitializer(ContractDefinition const& _contract) +{ + auto const& implicitConstructorPredicate = *createConstructorBlock(_contract, "contract_initializer_entry"); + + auto implicitFact = smt::constructor(implicitConstructorPredicate, m_context); + addRule(smtutil::Expression::implies(initialConstraints(_contract), implicitFact), implicitFact.name); + setCurrentBlock(implicitConstructorPredicate); + + solAssert(!m_errorDest, ""); + m_errorDest = m_contractInitializers.at(&_contract); + for (auto var: _contract.stateVariables()) + if (var->value()) + { + var->value()->accept(*this); + assignment(*var, *var->value()); + } + m_errorDest = nullptr; + + auto const& afterInit = *createConstructorBlock(_contract, "contract_initializer_after_init"); + connectBlocks(m_currentBlock, predicate(afterInit)); + setCurrentBlock(afterInit); + + if (auto constructor = _contract.constructor()) + { + errorFlag().increaseIndex(); + m_context.addAssertion(smt::functionCall(*m_summaries.at(&_contract).at(constructor), &_contract, m_context)); + connectBlocks(m_currentBlock, initializer(_contract), errorFlag().currentValue() > 0); + m_context.addAssertion(errorFlag().currentValue() == 0); + } + + connectBlocks(m_currentBlock, initializer(_contract)); } smtutil::Expression CHC::interface() { - auto paramExprs = applyMap( - m_stateVariables, - [this](auto _var) { return m_context.variable(*_var)->currentValue(); } - ); - return (*m_interfaces.at(m_currentContract))(paramExprs); + solAssert(m_currentContract, ""); + return interface(*m_currentContract); } smtutil::Expression CHC::interface(ContractDefinition const& _contract) { - return (*m_interfaces.at(&_contract))(stateVariablesAtIndex(0, _contract)); + return ::interface(*m_interfaces.at(&_contract), _contract, m_context); } smtutil::Expression CHC::error() @@ -900,32 +981,22 @@ smtutil::Expression CHC::error() smtutil::Expression CHC::error(unsigned _idx) { - return m_errorPredicate->functionValueAtIndex(_idx)({}); + return m_errorPredicate->functor(_idx)({}); } -smtutil::Expression CHC::summary(ContractDefinition const& _contract) +smtutil::Expression CHC::initializer(ContractDefinition const& _contract) { - if (auto const* constructor = _contract.constructor()) - return (*m_constructorSummaryPredicate)( - currentFunctionVariables(*constructor) - ); + return predicate(*m_contractInitializers.at(&_contract)); +} - return (*m_constructorSummaryPredicate)( - vector{m_error.currentValue()} + - currentStateVariables() - ); +smtutil::Expression CHC::summary(ContractDefinition const& _contract) +{ + return predicate(*m_constructorSummaries.at(&_contract)); } smtutil::Expression CHC::summary(FunctionDefinition const& _function, ContractDefinition const& _contract) { - vector args{m_error.currentValue()}; - auto contract = _function.annotation().contract; - args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : initialStateVariables(_contract); - args += applyMap(_function.parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); }); - args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables(_contract); - args += applyMap(_function.parameters(), [this](auto _var) { return currentValue(*_var); }); - args += applyMap(_function.returnParameters(), [this](auto _var) { return currentValue(*_var); }); - return (*m_summaries.at(&_contract).at(&_function))(args); + return smt::function(*m_summaries.at(&_contract).at(&_function), &_contract, m_context); } smtutil::Expression CHC::summary(FunctionDefinition const& _function) @@ -934,37 +1005,43 @@ smtutil::Expression CHC::summary(FunctionDefinition const& _function) return summary(_function, *m_currentContract); } -unique_ptr CHC::createBlock(ASTNode const* _node, string const& _prefix) +Predicate const* CHC::createBlock(ASTNode const* _node, PredicateType _predType, string const& _prefix) { - auto block = createSymbolicBlock(sort(_node), - "block_" + - uniquePrefix() + - "_" + - _prefix + - predicateName(_node)); + auto block = createSymbolicBlock( + sort(_node), + "block_" + uniquePrefix() + "_" + _prefix + predicateName(_node), + _predType, + _node + ); solAssert(m_currentFunction, ""); - m_symbolFunction[block->currentFunctionValue().name] = m_currentFunction; return block; } -unique_ptr CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) +Predicate const* CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) { - auto block = createSymbolicBlock(summarySort(_function, _contract), - "summary_" + - uniquePrefix() + - "_" + - predicateName(&_function, &_contract)); + return createSymbolicBlock( + functionSort(_function, &_contract, state()), + "summary_" + uniquePrefix() + "_" + predicateName(&_function, &_contract), + PredicateType::FunctionSummary, + &_function + ); +} - m_symbolFunction[block->currentFunctionValue().name] = &_function; - return block; +Predicate const* CHC::createConstructorBlock(ContractDefinition const& _contract, string const& _prefix) +{ + return createSymbolicBlock( + constructorSort(_contract, state()), + _prefix + "_" + contractSuffix(_contract) + "_" + uniquePrefix(), + PredicateType::ConstructorSummary, + &_contract + ); } void CHC::createErrorBlock() { - solAssert(m_errorPredicate, ""); - m_errorPredicate->increaseIndex(); - m_interface->registerRelation(m_errorPredicate->currentFunctionValue()); + m_errorPredicate = createSymbolicBlock(arity0FunctionSort(), "error_target_" + to_string(m_context.newUniqueId()), PredicateType::Error); + m_interface->registerRelation(m_errorPredicate->functor()); } void CHC::connectBlocks(smtutil::Expression const& _from, smtutil::Expression const& _to, smtutil::Expression const& _constraints) @@ -976,14 +1053,24 @@ void CHC::connectBlocks(smtutil::Expression const& _from, smtutil::Expression co addRule(edge, _from.name + "_to_" + _to.name); } -vector CHC::initialStateVariables() +smtutil::Expression CHC::initialConstraints(ContractDefinition const& _contract, FunctionDefinition const* _function) { - return stateVariablesAtIndex(0); + smtutil::Expression conj = state().state() == state().state(0); + conj = conj && errorFlag().currentValue() == 0; + for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract)) + conj = conj && m_context.variable(*var)->valueAtIndex(0) == currentValue(*var); + + FunctionDefinition const* function = _function ? _function : _contract.constructor(); + if (function) + for (auto var: function->parameters()) + conj = conj && m_context.variable(*var)->valueAtIndex(0) == currentValue(*var); + + return conj; } -vector CHC::initialStateVariables(ContractDefinition const& _contract) +vector CHC::initialStateVariables() { - return stateVariablesAtIndex(0, _contract); + return stateVariablesAtIndex(0); } vector CHC::stateVariablesAtIndex(unsigned _index) @@ -995,7 +1082,7 @@ vector CHC::stateVariablesAtIndex(unsigned _index) vector CHC::stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract) { return applyMap( - stateVariablesIncludingInheritedAndPrivate(_contract), + SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), [&](auto _var) { return valueAtIndex(*_var, _index); } ); } @@ -1008,47 +1095,7 @@ vector CHC::currentStateVariables() vector CHC::currentStateVariables(ContractDefinition const& _contract) { - return applyMap(stateVariablesIncludingInheritedAndPrivate(_contract), [this](auto _var) { return currentValue(*_var); }); -} - -vector CHC::currentFunctionVariables() -{ - solAssert(m_currentFunction, ""); - return currentFunctionVariables(*m_currentFunction); -} - -vector CHC::currentFunctionVariables(FunctionDefinition const& _function) -{ - vector initInputExprs; - vector mutableInputExprs; - for (auto const& var: _function.parameters()) - { - initInputExprs.push_back(m_context.variable(*var)->valueAtIndex(0)); - mutableInputExprs.push_back(m_context.variable(*var)->currentValue()); - } - auto returnExprs = applyMap(_function.returnParameters(), [this](auto _var) { return currentValue(*_var); }); - return vector{m_error.currentValue()} + - initialStateVariables() + - initInputExprs + - currentStateVariables() + - mutableInputExprs + - returnExprs; -} - -vector CHC::currentFunctionVariables(ContractDefinition const& _contract) -{ - if (auto const* constructor = _contract.constructor()) - return currentFunctionVariables(*constructor); - - return vector{m_error.currentValue()} + currentStateVariables(); -} - -vector CHC::currentBlockVariables() -{ - if (m_currentFunction) - return currentFunctionVariables() + applyMap(m_currentFunction->localVariables(), [this](auto _var) { return currentValue(*_var); }); - - return currentFunctionVariables(); + return applyMap(SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), [this](auto _var) { return currentValue(*_var); }); } string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contract) @@ -1068,39 +1115,76 @@ string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contr return prefix + "_" + to_string(_node->id()) + "_" + to_string(contract->id()); } -smtutil::Expression CHC::predicate(smt::SymbolicFunctionVariable const& _block) +smtutil::Expression CHC::predicate(Predicate const& _block) { - return _block(currentBlockVariables()); -} - -smtutil::Expression CHC::predicate( - smt::SymbolicFunctionVariable const& _block, - vector const& _arguments -) -{ - return _block(_arguments); + switch (_block.type()) + { + case PredicateType::Interface: + solAssert(m_currentContract, ""); + return ::interface(_block, *m_currentContract, m_context); + case PredicateType::ConstructorSummary: + return constructor(_block, m_context); + case PredicateType::FunctionSummary: + return smt::function(_block, m_currentContract, m_context); + case PredicateType::FunctionBlock: + solAssert(m_currentFunction, ""); + return functionBlock(_block, *m_currentFunction, m_currentContract, m_context); + case PredicateType::Error: + return _block({}); + case PredicateType::NondetInterface: + // Nondeterministic interface predicates are handled differently. + solAssert(false, ""); + case PredicateType::Custom: + // Custom rules are handled separately. + solAssert(false, ""); + } + solAssert(false, ""); } smtutil::Expression CHC::predicate(FunctionCall const& _funCall) { + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + auto kind = funType.kind(); + solAssert(kind == FunctionType::Kind::Internal || kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, ""); + auto const* function = functionCallToDefinition(_funCall); if (!function) return smtutil::Expression(true); - m_error.increaseIndex(); - vector args{m_error.currentValue()}; + auto contractAddressValue = [this](FunctionCall const& _f) { + FunctionType const& funType = dynamic_cast(*_f.expression().annotation().type); + if (funType.kind() == FunctionType::Kind::Internal) + return state().thisAddress(); + if (MemberAccess const* callBase = dynamic_cast(&_f.expression())) + return expr(callBase->expression()); + solAssert(false, "Unreachable!"); + }; + errorFlag().increaseIndex(); + vector args{errorFlag().currentValue(), contractAddressValue(_funCall), state().crypto(), state().tx(), state().state()}; + auto const* contract = function->annotation().contract; - FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); - bool otherContract = contract->isLibrary() || - funType.kind() == FunctionType::Kind::External || - funType.kind() == FunctionType::Kind::BareStaticCall; + auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; + solAssert(kind != FunctionType::Kind::Internal || contract->isLibrary() || contains(hierarchy, contract), ""); - args += otherContract ? stateVariablesAtIndex(0, *contract) : currentStateVariables(); + /// If the call is to a library, we use that library as the called contract. + /// If the call is to a contract not in the inheritance hierarchy, we also use that as the called contract. + /// Otherwise, the call is to some contract in the inheritance hierarchy of the current contract. + /// In this case we use current contract as the called one since the interfaces/predicates are different. + auto const* calledContract = contains(hierarchy, contract) ? m_currentContract : contract; + solAssert(calledContract, ""); + + bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View; + + args += currentStateVariables(*calledContract); args += symbolicArguments(_funCall); - if (!otherContract) + if (!calledContract->isLibrary() && !usesStaticCall) + { + state().newState(); for (auto const& var: m_stateVariables) m_context.variable(*var)->increaseIndex(); - args += otherContract ? stateVariablesAtIndex(1, *contract) : currentStateVariables(); + } + args += vector{state().state()}; + args += currentStateVariables(*calledContract); for (auto var: function->parameters() + function->returnParameters()) { @@ -1111,11 +1195,7 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall) args.push_back(currentValue(*var)); } - if (otherContract) - return (*m_summaries.at(contract).at(function))(args); - - solAssert(m_currentContract, ""); - return (*m_summaries.at(m_currentContract).at(function))(args); + return (*m_summaries.at(calledContract).at(function))(args); } void CHC::addRule(smtutil::Expression const& _rule, string const& _ruleName) @@ -1123,14 +1203,14 @@ void CHC::addRule(smtutil::Expression const& _rule, string const& _ruleName) m_interface->addRule(_rule, _ruleName); } -pair CHC::query(smtutil::Expression const& _query, langutil::SourceLocation const& _location) +pair CHC::query(smtutil::Expression const& _query, langutil::SourceLocation const& _location) { - smtutil::CheckResult result; + CheckResult result; CHCSolverInterface::CexGraph cex; tie(result, cex) = m_interface->query(_query); switch (result) { - case smtutil::CheckResult::SATISFIABLE: + case CheckResult::SATISFIABLE: { #ifdef HAVE_Z3 // Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete. @@ -1139,146 +1219,197 @@ pair CHC::query(smtutil::Exp solAssert(spacer, ""); spacer->setSpacerOptions(false); - smtutil::CheckResult resultNoOpt; + CheckResult resultNoOpt; CHCSolverInterface::CexGraph cexNoOpt; tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); - if (resultNoOpt == smtutil::CheckResult::SATISFIABLE) + if (resultNoOpt == CheckResult::SATISFIABLE) cex = move(cexNoOpt); spacer->setSpacerOptions(true); #endif break; } - case smtutil::CheckResult::UNSATISFIABLE: + case CheckResult::UNSATISFIABLE: break; - case smtutil::CheckResult::UNKNOWN: + case CheckResult::UNKNOWN: break; - case smtutil::CheckResult::CONFLICTING: - m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); + case CheckResult::CONFLICTING: + m_errorReporter.warning(1988_error, _location, "CHC: At least two SMT solvers provided conflicting answers. Results might not be sound."); break; - case smtutil::CheckResult::ERROR: - m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver."); + case CheckResult::ERROR: + m_errorReporter.warning(1218_error, _location, "CHC: Error trying to invoke SMT solver."); break; } return {result, cex}; } -void CHC::addVerificationTarget( - ASTNode const* _scope, +void CHC::verificationTargetEncountered( + ASTNode const* const _errorNode, VerificationTarget::Type _type, - smtutil::Expression _from, - smtutil::Expression _constraints, - smtutil::Expression _errorId + smtutil::Expression const& _errorCondition ) { - m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId}); -} + solAssert(m_currentContract || m_currentFunction, ""); + SourceUnit const* source = m_currentContract ? sourceUnitContaining(*m_currentContract) : sourceUnitContaining(*m_currentFunction); + solAssert(source, ""); + if (!source->annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker)) + return; -void CHC::addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId) -{ - addVerificationTarget(_scope, VerificationTarget::Type::Assert, _from, _constraints, _errorId); -} + bool scopeIsFunction = m_currentFunction && !m_currentFunction->isConstructor(); + auto errorId = newErrorId(); + solAssert(m_verificationTargets.count(errorId) == 0, "Error ID is not unique!"); + m_verificationTargets.emplace(errorId, CHCVerificationTarget{{_type, _errorCondition, smtutil::Expression(true)}, errorId, _errorNode}); + if (scopeIsFunction) + m_functionTargetIds[m_currentFunction].push_back(errorId); + else + m_functionTargetIds[m_currentContract].push_back(errorId); + auto previousError = errorFlag().currentValue(); + errorFlag().increaseIndex(); -void CHC::addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId) -{ - solAssert(m_currentContract, ""); - solAssert(m_currentFunction, ""); + // create an error edge to the summary + solAssert(m_errorDest, ""); + connectBlocks( + m_currentBlock, + predicate(*m_errorDest), + _errorCondition && errorFlag().currentValue() == errorId + ); - if (m_currentFunction->isConstructor()) - addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, summary(*m_currentContract), smtutil::Expression(true), _errorId); - else - { - auto iface = (*m_interfaces.at(m_currentContract))(initialStateVariables()); - auto sum = summary(*m_currentFunction); - addVerificationTarget(_scope, VerificationTarget::Type::PopEmptyArray, iface, sum, _errorId); - } + m_context.addAssertion(errorFlag().currentValue() == previousError); } void CHC::checkVerificationTargets() { - for (auto const& [scope, target]: m_verificationTargets) + // The verification conditions have been collected per function where they have been encountered (m_verificationTargets). + // Also, all possible contexts in which an external function can be called has been recorded (m_queryPlaceholders). + // Here we combine every context in which an external function can be called with all possible verification conditions + // in its call graph. Each such combination forms a unique verification target. + vector verificationTargets; + for (auto const& [function, placeholders]: m_queryPlaceholders) { - if (target.type == VerificationTarget::Type::Assert) - checkAssertTarget(scope, target); - else - { - string satMsg; - string unknownMsg; - ErrorId errorReporterId; - - if (target.type == VerificationTarget::Type::PopEmptyArray) + auto functionTargets = transactionVerificationTargetsIds(function); + for (auto const& placeholder: placeholders) + for (unsigned id: functionTargets) { - solAssert(dynamic_cast(scope), ""); - satMsg = "Empty array \"pop\" detected here"; - unknownMsg = "Empty array \"pop\" might happen here."; - errorReporterId = 2529_error; + auto const& target = m_verificationTargets.at(id); + verificationTargets.push_back(CHCVerificationTarget{ + {target.type, placeholder.fromPredicate, placeholder.constraints && placeholder.errorExpression == target.errorId}, + target.errorId, + target.errorNode + }); } - else - solAssert(false, ""); - - auto it = m_errorIds.find(scope->id()); - solAssert(it != m_errorIds.end(), ""); - checkAndReportTarget(scope, target, it->second, errorReporterId, satMsg, unknownMsg); - } } -} -void CHC::checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target) -{ - solAssert(_target.type == VerificationTarget::Type::Assert, ""); - auto assertions = transactionAssertions(_scope); - for (auto const* assertion: assertions) + set checkedErrorIds; + for (auto const& target: verificationTargets) { - auto it = m_errorIds.find(assertion->id()); - solAssert(it != m_errorIds.end(), ""); - unsigned errorId = it->second; + string errorType; + ErrorId errorReporterId; + + if (target.type == VerificationTarget::Type::PopEmptyArray) + { + solAssert(dynamic_cast(target.errorNode), ""); + errorType = "Empty array \"pop\""; + errorReporterId = 2529_error; + } + else if ( + target.type == VerificationTarget::Type::Underflow || + target.type == VerificationTarget::Type::Overflow + ) + { + auto const* expr = dynamic_cast(target.errorNode); + solAssert(expr, ""); + auto const* intType = dynamic_cast(expr->annotation().type); + if (!intType) + intType = TypeProvider::uint256(); - checkAndReportTarget(assertion, _target, errorId, 6328_error, "Assertion violation happens here"); + if (target.type == VerificationTarget::Type::Underflow) + { + errorType = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")"; + errorReporterId = 3944_error; + } + else if (target.type == VerificationTarget::Type::Overflow) + { + errorType = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")"; + errorReporterId = 4984_error; + } + } + else if (target.type == VerificationTarget::Type::DivByZero) + { + errorType = "Division by zero"; + errorReporterId = 4281_error; + } + else if (target.type == VerificationTarget::Type::Assert) + { + errorType = "Assertion violation"; + errorReporterId = 6328_error; + } + else + solAssert(false, ""); + + checkAndReportTarget(target, errorReporterId, errorType + " happens here.", errorType + " might happen here."); + checkedErrorIds.insert(target.errorId); } + + // There can be targets in internal functions that are not reachable from the external interface. + // These are safe by definition and are not even checked by the CHC engine, but this information + // must still be reported safe by the BMC engine. + set allErrorIds; + for (auto const& entry: m_functionTargetIds) + for (unsigned id: entry.second) + allErrorIds.insert(id); + + set unreachableErrorIds; + set_difference( + allErrorIds.begin(), + allErrorIds.end(), + checkedErrorIds.begin(), + checkedErrorIds.end(), + inserter(unreachableErrorIds, unreachableErrorIds.begin()) + ); + for (auto id: unreachableErrorIds) + m_safeTargets[m_verificationTargets.at(id).errorNode].insert(m_verificationTargets.at(id).type); } void CHC::checkAndReportTarget( - ASTNode const* _scope, CHCVerificationTarget const& _target, - unsigned _errorId, ErrorId _errorReporterId, string _satMsg, string _unknownMsg ) { - if (m_unsafeTargets.count(_scope) && m_unsafeTargets.at(_scope).count(_target.type)) + if (m_unsafeTargets.count(_target.errorNode) && m_unsafeTargets.at(_target.errorNode).count(_target.type)) return; createErrorBlock(); - connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId)); - auto const& [result, model] = query(error(), _scope->location()); - if (result == smtutil::CheckResult::UNSATISFIABLE) - m_safeTargets[_scope].insert(_target.type); - else if (result == smtutil::CheckResult::SATISFIABLE) + connectBlocks(_target.value, error(), _target.constraints); + auto const& location = _target.errorNode->location(); + auto const& [result, model] = query(error(), location); + if (result == CheckResult::UNSATISFIABLE) + m_safeTargets[_target.errorNode].insert(_target.type); + else if (result == CheckResult::SATISFIABLE) { solAssert(!_satMsg.empty(), ""); - m_unsafeTargets[_scope].insert(_target.type); + m_unsafeTargets[_target.errorNode].insert(_target.type); auto cex = generateCounterexample(model, error().name); if (cex) - m_outerErrorReporter.warning( + m_errorReporter.warning( _errorReporterId, - _scope->location(), - _satMsg, - SecondarySourceLocation().append(" for:\n" + *cex, SourceLocation{}) + location, + "CHC: " + _satMsg + "\nCounterexample:\n" + *cex ); else - m_outerErrorReporter.warning( + m_errorReporter.warning( _errorReporterId, - _scope->location(), - _satMsg + "." + location, + "CHC: " + _satMsg ); } else if (!_unknownMsg.empty()) - m_outerErrorReporter.warning( + m_errorReporter.warning( _errorReporterId, - _scope->location(), - _unknownMsg + location, + "CHC: " + _unknownMsg ); } @@ -1303,7 +1434,7 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& { optional rootId; for (auto const& [id, node]: _graph.nodes) - if (node.first == _root) + if (node.name == _root) { rootId = id; break; @@ -1323,42 +1454,31 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& solAssert(edges.size() <= 2, ""); unsigned summaryId = edges.at(0); - optional interfaceId; if (edges.size() == 2) { interfaceId = edges.at(1); - if (_graph.nodes.at(summaryId).first.rfind("summary", 0) != 0) + if (!Predicate::predicate(_graph.nodes.at(summaryId).name)->isSummary()) swap(summaryId, *interfaceId); - solAssert(_graph.nodes.at(*interfaceId).first.rfind("interface", 0) == 0, ""); + auto interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).name); + solAssert(interfacePredicate && interfacePredicate->isInterface(), ""); } /// The children are unordered, so we need to check which is the summary and /// which is the interface. - solAssert(_graph.nodes.at(summaryId).first.rfind("summary", 0) == 0, ""); + Predicate const* summaryPredicate = Predicate::predicate(_graph.nodes.at(summaryId).name); + solAssert(summaryPredicate && summaryPredicate->isSummary(), ""); /// At this point property 2 from the function description is verified for this node. + vector summaryArgs = _graph.nodes.at(summaryId).arguments; - auto const& summaryNode = _graph.nodes.at(summaryId); - solAssert(m_symbolFunction.count(summaryNode.first), ""); - - FunctionDefinition const* calledFun = nullptr; - ContractDefinition const* calledContract = nullptr; - if (auto const* contract = dynamic_cast(m_symbolFunction.at(summaryNode.first))) - { - if (auto const* constructor = contract->constructor()) - calledFun = constructor; - else - calledContract = contract; - } - else if (auto const* fun = dynamic_cast(m_symbolFunction.at(summaryNode.first))) - calledFun = fun; - else - solAssert(false, ""); + FunctionDefinition const* calledFun = summaryPredicate->programFunction(); + ContractDefinition const* calledContract = summaryPredicate->programContract(); solAssert((calledFun && !calledContract) || (!calledFun && calledContract), ""); - auto const& stateVars = calledFun ? stateVariablesIncludingInheritedAndPrivate(*calledFun) : stateVariablesIncludingInheritedAndPrivate(*calledContract); - /// calledContract != nullptr implies that the constructor of the analyzed contract is implicit and - /// therefore takes no parameters. + auto stateVars = summaryPredicate->stateVariables(); + solAssert(stateVars.has_value(), ""); + auto stateValues = summaryPredicate->summaryStateValues(summaryArgs); + solAssert(stateValues.size() == stateVars->size(), ""); /// This summary node is the end of a tx. /// If it is the first summary node seen in this loop, it is the summary @@ -1368,115 +1488,53 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& { lastTxSeen = true; /// Generate counterexample message local to the failed target. - localState = formatStateCounterexample(stateVars, calledFun, summaryNode.second) + "\n"; + localState = formatVariableModel(*stateVars, stateValues, ", ") + "\n"; if (calledFun) { - /// The signature of a summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). + auto inValues = summaryPredicate->summaryPostInputValues(summaryArgs); auto const& inParams = calledFun->parameters(); - unsigned initLocals = stateVars.size() * 2 + 1 + inParams.size(); - /// In this loop we are interested in postInputVars. - for (unsigned i = initLocals; i < initLocals + inParams.size(); ++i) - { - auto param = inParams.at(i - initLocals); - if (param->type()->isValueType()) - localState += param->name() + " = " + summaryNode.second.at(i) + "\n"; - } + localState += formatVariableModel(inParams, inValues, "\n") + "\n"; + auto outValues = summaryPredicate->summaryPostOutputValues(summaryArgs); auto const& outParams = calledFun->returnParameters(); - initLocals += inParams.size(); - /// In this loop we are interested in outputVars. - for (unsigned i = initLocals; i < initLocals + outParams.size(); ++i) - { - auto param = outParams.at(i - initLocals); - if (param->type()->isValueType()) - localState += param->name() + " = " + summaryNode.second.at(i) + "\n"; - } + localState += formatVariableModel(outParams, outValues, "\n") + "\n"; } } else + { + auto modelMsg = formatVariableModel(*stateVars, stateValues, ", "); /// We report the state after every tx in the trace except for the last, which is reported /// first in the code above. - path.emplace_back("State: " + formatStateCounterexample(stateVars, calledFun, summaryNode.second)); + if (!modelMsg.empty()) + path.emplace_back("State: " + modelMsg); + } - string txCex = calledContract ? "constructor()" : formatFunctionCallCounterexample(stateVars, *calledFun, summaryNode.second); + string txCex = summaryPredicate->formatSummaryCall(summaryArgs); path.emplace_back(txCex); - /// Recurse on the next interface node which represents the previous transaction - /// or stop. - if (interfaceId) - node = *interfaceId; - else + /// Stop when we reach the summary of the analyzed constructor. + if (summaryPredicate->type() == PredicateType::ConstructorSummary) break; - } - - return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n"); -} - -string CHC::formatStateCounterexample(vector const& _stateVars, FunctionDefinition const* _function, vector const& _summaryValues) -{ - /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). - /// The signature of an implicit constructor summary predicate is: summary(error, postStateVars). - /// Here we are interested in postStateVars. - vector::const_iterator stateFirst; - vector::const_iterator stateLast; - if (_function) - { - stateFirst = _summaryValues.begin() + 1 + static_cast(_stateVars.size()) + static_cast(_function->parameters().size()); - stateLast = stateFirst + static_cast(_stateVars.size()); - } - else - { - stateFirst = _summaryValues.begin() + 1; - stateLast = stateFirst + static_cast(_stateVars.size()); - } - solAssert(stateFirst >= _summaryValues.begin() && stateFirst <= _summaryValues.end(), ""); - solAssert(stateLast >= _summaryValues.begin() && stateLast <= _summaryValues.end(), ""); - vector stateArgs(stateFirst, stateLast); - solAssert(stateArgs.size() == _stateVars.size(), ""); - - vector stateCex; - for (unsigned i = 0; i < stateArgs.size(); ++i) - { - auto var = _stateVars.at(i); - if (var->type()->isValueType()) - stateCex.emplace_back(var->name() + " = " + stateArgs.at(i)); + /// Recurse on the next interface node which represents the previous transaction. + node = *interfaceId; } - return boost::algorithm::join(stateCex, ", "); -} - -string CHC::formatFunctionCallCounterexample(vector const& _stateVars, FunctionDefinition const& _function, vector const& _summaryValues) -{ - /// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postInputVars, outputVars). - /// Here we are interested in preInputVars. - vector::const_iterator first = _summaryValues.begin() + static_cast(_stateVars.size()) + 1; - vector::const_iterator last = first + static_cast(_function.parameters().size()); - solAssert(first >= _summaryValues.begin() && first <= _summaryValues.end(), ""); - solAssert(last >= _summaryValues.begin() && last <= _summaryValues.end(), ""); - vector functionArgsCex(first, last); - vector functionArgs; - - auto const& params = _function.parameters(); - solAssert(params.size() == functionArgsCex.size(), ""); - for (unsigned i = 0; i < params.size(); ++i) - if (params[i]->type()->isValueType()) - functionArgs.emplace_back(functionArgsCex[i]); - else - functionArgs.emplace_back(params[i]->name()); - - string fName = _function.isConstructor() ? "constructor" : - _function.isFallback() ? "fallback" : - _function.isReceive() ? "receive" : - _function.name(); - return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")"; + return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n"); } -string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex) +string CHC::cex2dot(CHCSolverInterface::CexGraph const& _cex) { string dot = "digraph {\n"; auto pred = [&](CHCSolverInterface::CexNode const& _node) { - return "\"" + _node.first + "(" + boost::algorithm::join(_node.second, ", ") + ")\""; + vector args = applyMap( + _node.arguments, + [&](auto const& arg) { + solAssert(arg.arguments.empty(), ""); + return arg.name; + } + ); + return "\"" + _node.name + "(" + boost::algorithm::join(args, ", ") + ")\""; }; for (auto const& [u, vs]: _cex.edges) @@ -1492,13 +1550,27 @@ string CHC::uniquePrefix() return to_string(m_blockCounter++); } -unsigned CHC::newErrorId(frontend::Expression const& _expr) +string CHC::contractSuffix(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + to_string(_contract.id()); +} + +unsigned CHC::newErrorId() { unsigned errorId = m_context.newUniqueId(); // We need to make sure the error id is not zero, // because error id zero actually means no error in the CHC encoding. if (errorId == 0) errorId = m_context.newUniqueId(); - m_errorIds.emplace(_expr.id(), errorId); return errorId; } + +SymbolicState& CHC::state() +{ + return m_context.state(); +} + +SymbolicIntVariable& CHC::errorFlag() +{ + return state().errorFlag(); +} diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 8342524bbfa2..6a4fad26b7de 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -31,12 +31,15 @@ #pragma once +#include #include #include #include +#include + #include #include #include @@ -52,7 +55,8 @@ class CHC: public SMTEncoder langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, ReadCallback::Callback const& _smtCallback, - smtutil::SMTSolverChoice _enabledSolvers + smtutil::SMTSolverChoice _enabledSolvers, + std::optional timeout ); void analyze(SourceUnit const& _sources); @@ -78,62 +82,59 @@ class CHC: public SMTEncoder void endVisit(FunctionCall const& _node) override; void endVisit(Break const& _node) override; void endVisit(Continue const& _node) override; + void endVisit(IndexRangeAccess const& _node) override; + void endVisit(Return const& _node) override; + + void pushInlineFrame(CallableDeclaration const& _callable) override; + void popInlineFrame(CallableDeclaration const& _callable) override; void visitAssert(FunctionCall const& _funCall); + void visitAddMulMod(FunctionCall const& _funCall) override; void internalFunctionCall(FunctionCall const& _funCall); void externalFunctionCall(FunctionCall const& _funCall); + void externalFunctionCallToTrustedCode(FunctionCall const& _funCall); void unknownFunctionCall(FunctionCall const& _funCall); void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override; + /// Creates underflow/overflow verification targets. + std::pair arithmeticOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType, + Expression const& _expression + ) override; //@} - struct IdCompare - { - bool operator()(ASTNode const* lhs, ASTNode const* rhs) const - { - return lhs->id() < rhs->id(); - } - }; - /// Helpers. //@{ void resetSourceAnalysis(); void resetContractAnalysis(); void eraseKnowledge(); void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override; - void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector const* _arguments = nullptr); - std::set transactionAssertions(ASTNode const* _txRoot); - static std::vector stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); - static std::vector stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); + void setCurrentBlock(Predicate const& _block); + std::set transactionVerificationTargetsIds(ASTNode const* _txRoot); //@} /// Sort helpers. //@{ - static std::vector stateSorts(ContractDefinition const& _contract); - smtutil::SortPointer constructorSort(); - smtutil::SortPointer interfaceSort(); - smtutil::SortPointer nondetInterfaceSort(); - static smtutil::SortPointer interfaceSort(ContractDefinition const& _const); - static smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _const); - smtutil::SortPointer arity0FunctionSort(); smtutil::SortPointer sort(FunctionDefinition const& _function); smtutil::SortPointer sort(ASTNode const* _block); - /// @returns the sort of a predicate that represents the summary of _function in the scope of _contract. - /// The _contract is also needed because the same function might be in many contracts due to inheritance, - /// where the sort changes because the set of state variables might change. - static smtutil::SortPointer summarySort(FunctionDefinition const& _function, ContractDefinition const& _contract); //@} /// Predicate helpers. //@{ /// @returns a new block of given _sort and _name. - std::unique_ptr createSymbolicBlock(smtutil::SortPointer _sort, std::string const& _name); + Predicate const* createSymbolicBlock(smtutil::SortPointer _sort, std::string const& _name, PredicateType _predType, ASTNode const* _node = nullptr); /// Creates summary predicates for all functions of all contracts /// in a given _source. void defineInterfacesAndSummaries(SourceUnit const& _source); - /// Genesis predicate. - smtutil::Expression genesis() { return (*m_genesisPredicate)({}); } + /// Creates a CHC system that, for a given contract, + /// - initializes its state variables (as 0 or given value, if any). + /// - "calls" the explicit constructor function of the contract, if any. + void defineContractInitializer(ContractDefinition const& _contract); + /// Interface predicate over current variables. smtutil::Expression interface(); smtutil::Expression interface(ContractDefinition const& _contract); @@ -142,10 +143,13 @@ class CHC: public SMTEncoder smtutil::Expression error(unsigned _idx); /// Creates a block for the given _node. - std::unique_ptr createBlock(ASTNode const* _node, std::string const& _prefix = ""); + Predicate const* createBlock(ASTNode const* _node, PredicateType _predType, std::string const& _prefix = ""); /// Creates a call block for the given function _function from contract _contract. /// The contract is needed here because of inheritance. - std::unique_ptr createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract); + Predicate const* createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract); + + /// @returns a block related to @a _contract's constructor. + Predicate const* createConstructorBlock(ContractDefinition const& _contract, std::string const& _prefix); /// Creates a new error block to be used by an assertion. /// Also registers the predicate. @@ -153,34 +157,26 @@ class CHC: public SMTEncoder void connectBlocks(smtutil::Expression const& _from, smtutil::Expression const& _to, smtutil::Expression const& _constraints = smtutil::Expression(true)); + /// @returns The initial constraints that set up the beginning of a function. + smtutil::Expression initialConstraints(ContractDefinition const& _contract, FunctionDefinition const* _function = nullptr); + /// @returns the symbolic values of the state variables at the beginning /// of the current transaction. std::vector initialStateVariables(); - std::vector initialStateVariables(ContractDefinition const& _contract); std::vector stateVariablesAtIndex(unsigned _index); std::vector stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract); /// @returns the current symbolic values of the current state variables. std::vector currentStateVariables(); std::vector currentStateVariables(ContractDefinition const& _contract); - /// @returns the current symbolic values of the current function's - /// input and output parameters. - std::vector currentFunctionVariables(); - std::vector currentFunctionVariables(FunctionDefinition const& _function); - std::vector currentFunctionVariables(ContractDefinition const& _contract); - - /// @returns the same as currentFunctionVariables plus - /// local variables. - std::vector currentBlockVariables(); - /// @returns the predicate name for a given node. std::string predicateName(ASTNode const* _node, ContractDefinition const* _contract = nullptr); - /// @returns a predicate application over the current scoped variables. - smtutil::Expression predicate(smt::SymbolicFunctionVariable const& _block); - /// @returns a predicate application over @param _arguments. - smtutil::Expression predicate(smt::SymbolicFunctionVariable const& _block, std::vector const& _arguments); + /// @returns a predicate application after checking the predicate's type. + smtutil::Expression predicate(Predicate const& _block); /// @returns the summary predicate for the called function. smtutil::Expression predicate(FunctionCall const& _funCall); + /// @returns a predicate that defines a contract initializer. + smtutil::Expression initializer(ContractDefinition const& _contract); /// @returns a predicate that defines a constructor summary. smtutil::Expression summary(ContractDefinition const& _contract); /// @returns a predicate that defines a function summary. @@ -196,33 +192,37 @@ class CHC: public SMTEncoder /// @returns otherwise. std::pair query(smtutil::Expression const& _query, langutil::SourceLocation const& _location); - void addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type _type, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId); - void addAssertVerificationTarget(ASTNode const* _scope, smtutil::Expression _from, smtutil::Expression _constraints, smtutil::Expression _errorId); - void addArrayPopVerificationTarget(ASTNode const* _scope, smtutil::Expression _errorId); + void verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTarget::Type _type, smtutil::Expression const& _errorCondition); void checkVerificationTargets(); // Forward declaration. Definition is below. struct CHCVerificationTarget; void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target); void checkAndReportTarget( - ASTNode const* _scope, CHCVerificationTarget const& _target, - unsigned _errorId, langutil::ErrorId _errorReporterId, std::string _satMsg, std::string _unknownMsg = "" ); std::optional generateCounterexample(smtutil::CHCSolverInterface::CexGraph const& _graph, std::string const& _root); - /// @returns values for the _stateVariables after a transaction calling - /// _function was executed. - /// _function = nullptr means the transaction was the deployment of a - /// contract without an explicit constructor. - std::string formatStateCounterexample(std::vector const& _stateVariables, FunctionDefinition const* _function, std::vector const& _summaryValues); - /// @returns a formatted text representing a call to _function - /// with the concrete values for value type parameters and - /// the parameter name for reference types. - std::string formatFunctionCallCounterexample(std::vector const& _stateVariables, FunctionDefinition const& _function, std::vector const& _summaryValues); + + /// @returns a set of pairs _var = _value separated by _separator. + template + std::string formatVariableModel(std::vector const& _variables, std::vector> const& _values, std::string const& _separator) const + { + solAssert(_variables.size() == _values.size(), ""); + + std::vector assignments; + for (unsigned i = 0; i < _values.size(); ++i) + { + auto var = _variables.at(i); + if (var && _values.at(i)) + assignments.emplace_back(var->name() + " = " + *_values.at(i)); + } + + return boost::algorithm::join(assignments, _separator); + } /// @returns a DAG in the dot format. /// Used for debugging purposes. @@ -231,61 +231,46 @@ class CHC: public SMTEncoder /// Misc. //@{ - /// Returns a prefix to be used in a new unique block name + /// @returns a prefix to be used in a new unique block name /// and increases the block counter. std::string uniquePrefix(); + /// @returns a suffix to be used by contract related predicates. + std::string contractSuffix(ContractDefinition const& _contract); + /// @returns a new unique error id associated with _expr and stores /// it into m_errorIds. - unsigned newErrorId(Expression const& _expr); + unsigned newErrorId(); + + smt::SymbolicState& state(); + smt::SymbolicIntVariable& errorFlag(); //@} /// Predicates. //@{ - /// Genesis predicate. - std::unique_ptr m_genesisPredicate; - - /// Implicit constructor predicate. - /// Explicit constructors are handled as functions. - std::unique_ptr m_implicitConstructorPredicate; - - /// Constructor summary predicate, exists after the constructor - /// (implicit or explicit) and before the interface. - std::unique_ptr m_constructorSummaryPredicate; - /// Artificial Interface predicate. /// Single entry block for all functions. - std::map> m_interfaces; + std::map m_interfaces; /// Nondeterministic interfaces. /// These are used when the analyzed contract makes external calls to unknown code, /// which means that the analyzed contract can potentially be called /// nondeterministically. - std::map> m_nondetInterfaces; + std::map m_nondetInterfaces; + + std::map m_constructorSummaries; + std::map m_contractInitializers; /// Artificial Error predicate. /// Single error block for all assertions. - std::unique_ptr m_errorPredicate; + Predicate const* m_errorPredicate = nullptr; /// Function predicates. - std::map>> m_summaries; - - smt::SymbolicIntVariable m_error{ - TypeProvider::uint256(), - TypeProvider::uint256(), - "error", - m_context - }; - - /// Maps predicate names to the ASTNodes they came from. - std::map m_symbolFunction; + std::map> m_summaries; //@} /// Variables. //@{ - /// State variables sorts. - /// Used by all predicates. - std::vector m_stateSorts; /// State variables. /// Used to create all predicates. std::vector m_stateVariables; @@ -295,10 +280,30 @@ class CHC: public SMTEncoder //@{ struct CHCVerificationTarget: VerificationTarget { - smtutil::Expression errorId; + unsigned const errorId; + ASTNode const* const errorNode; + }; + + /// Query placeholder stores information necessary to create the final query edge in the CHC system. + /// It is combined with the unique error id (and error type) to create a complete Verification Target. + struct CHCQueryPlaceholder + { + smtutil::Expression const constraints; + smtutil::Expression const errorExpression; + smtutil::Expression const fromPredicate; }; - std::map m_verificationTargets; + /// Query placeholders for constructors, if the key has type ContractDefinition*, + /// or external functions, if the key has type FunctionDefinition*. + /// A placeholder is created for each possible context of a function (e.g. multiple contracts in contract inheritance hierarchy). + std::map, smt::EncodingContext::IdCompare> m_queryPlaceholders; + + /// Records verification conditions IDs per function encountered during an analysis of that function. + /// The key is the ASTNode of the function where the verification condition has been encountered, + /// or the ASTNode of the contract if the verification condition happens inside an implicit constructor. + std::map, smt::EncodingContext::IdCompare> m_functionTargetIds; + /// Helper mapping unique IDs to actual verification targets. + std::map m_verificationTargets; /// Targets proven safe. std::map> m_safeTargets; @@ -310,14 +315,7 @@ class CHC: public SMTEncoder //@{ FunctionDefinition const* m_currentFunction = nullptr; - std::map, IdCompare> m_callGraph; - - std::map, IdCompare> m_functionAssertions; - - /// Maps ASTNode ids to error ids. - /// A multimap is used instead of map anticipating the UnderOverflow - /// target which has 2 error ids. - std::multimap m_errorIds; + std::map, smt::EncodingContext::IdCompare> m_callGraph; /// The current block. smtutil::Expression m_currentBlock = smtutil::Expression(true); @@ -329,9 +327,22 @@ class CHC: public SMTEncoder bool m_unknownFunctionCallSeen = false; /// Block where a loop break should go to. - smt::SymbolicFunctionVariable const* m_breakDest = nullptr; + Predicate const* m_breakDest = nullptr; /// Block where a loop continue should go to. - smt::SymbolicFunctionVariable const* m_continueDest = nullptr; + Predicate const* m_continueDest = nullptr; + + /// Block where an error condition should go to. + /// This can be: + /// 1) Constructor initializer summary, if error happens while evaluating initial values of state variables. + /// 2) Constructor summary, if error happens while evaluating base constructor arguments. + /// 3) Function summary, if error happens inside a function. + Predicate const* m_errorDest = nullptr; + + /// Represents the stack of destinations where a `return` should go. + /// This is different from `m_errorDest` above: + /// - Constructor initializers and constructor summaries will never be `return` targets because they are artificial. + /// - Modifiers also have their own `return` target blocks, whereas they do not have their own error destination. + std::vector m_returnDests; //@} /// CHC solver. @@ -342,6 +353,9 @@ class CHC: public SMTEncoder /// SMT solvers that are chosen at runtime. smtutil::SMTSolverChoice m_enabledSolvers; + + /// SMT query timeout in seconds. + std::optional m_queryTimeout; }; } diff --git a/libsolidity/formal/EncodingContext.cpp b/libsolidity/formal/EncodingContext.cpp index 422eb44eeab5..da690a05f0e3 100644 --- a/libsolidity/formal/EncodingContext.cpp +++ b/libsolidity/formal/EncodingContext.cpp @@ -33,7 +33,6 @@ EncodingContext::EncodingContext(): void EncodingContext::reset() { resetAllVariables(); - resetUniqueId(); m_expressions.clear(); m_globalContext.clear(); m_state.reset(); diff --git a/libsolidity/formal/EncodingContext.h b/libsolidity/formal/EncodingContext.h index e188792d8130..7c001f452b61 100644 --- a/libsolidity/formal/EncodingContext.h +++ b/libsolidity/formal/EncodingContext.h @@ -23,8 +23,7 @@ #include -#include -#include +#include namespace solidity::frontend::smt { @@ -67,12 +66,20 @@ class EncodingContext return m_solver->newVariable(move(_name), move(_sort)); } + struct IdCompare + { + bool operator()(ASTNode const* lhs, ASTNode const* rhs) const + { + return lhs->id() < rhs->id(); + } + }; + /// Variables. //@{ /// @returns the symbolic representation of a program variable. std::shared_ptr variable(frontend::VariableDeclaration const& _varDecl); /// @returns all symbolic variables. - std::unordered_map> const& variables() const { return m_variables; } + std::map, IdCompare> const& variables() const { return m_variables; } /// Creates a symbolic variable and /// @returns true if a variable's type is not supported and is therefore abstract. @@ -105,7 +112,7 @@ class EncodingContext /// @returns the symbolic representation of an AST node expression. std::shared_ptr expression(frontend::Expression const& _e); /// @returns all symbolic expressions. - std::unordered_map> const& expressions() const { return m_expressions; } + std::map, IdCompare> const& expressions() const { return m_expressions; } /// Creates the expression (value can be arbitrary). /// @returns true if type is not supported. @@ -119,7 +126,7 @@ class EncodingContext /// Global variables and functions. std::shared_ptr globalSymbol(std::string const& _name); /// @returns all symbolic globals. - std::unordered_map> const& globalSymbols() const { return m_globalContext; } + std::map> const& globalSymbols() const { return m_globalContext; } /// Defines a new global variable or function /// and @returns true if type was abstracted. @@ -135,7 +142,7 @@ class EncodingContext void pushSolver(); void popSolver(); void addAssertion(smtutil::Expression const& _e); - unsigned solverStackHeigh() { return m_assertions.size(); } const + size_t solverStackHeigh() { return m_assertions.size(); } const smtutil::SolverInterface* solver() { solAssert(m_solver, ""); @@ -149,14 +156,14 @@ class EncodingContext /// Symbolic expressions. //{@ /// Symbolic variables. - std::unordered_map> m_variables; + std::map, IdCompare> m_variables; /// Symbolic expressions. - std::unordered_map> m_expressions; + std::map, IdCompare> m_expressions; /// Symbolic representation of global symbols including /// variables and functions. - std::unordered_map> m_globalContext; + std::map> m_globalContext; /// Symbolic representation of the blockchain state. SymbolicState m_state; diff --git a/libsolidity/formal/ModelChecker.cpp b/libsolidity/formal/ModelChecker.cpp index 9465dd34200d..809b805c623c 100644 --- a/libsolidity/formal/ModelChecker.cpp +++ b/libsolidity/formal/ModelChecker.cpp @@ -17,6 +17,9 @@ // SPDX-License-Identifier: GPL-3.0 #include +#ifdef HAVE_Z3 +#include +#endif using namespace std; using namespace solidity; @@ -27,12 +30,14 @@ using namespace solidity::frontend; ModelChecker::ModelChecker( ErrorReporter& _errorReporter, map const& _smtlib2Responses, + ModelCheckerSettings _settings, ReadCallback::Callback const& _smtCallback, smtutil::SMTSolverChoice _enabledSolvers ): + m_settings(_settings), m_context(), - m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers), - m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers) + m_bmc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout), + m_chc(m_context, _errorReporter, _smtlib2Responses, _smtCallback, _enabledSolvers, _settings.timeout) { } @@ -41,13 +46,15 @@ void ModelChecker::analyze(SourceUnit const& _source) if (!_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker)) return; - m_chc.analyze(_source); + if (m_settings.engine.chc) + m_chc.analyze(_source); auto solvedTargets = m_chc.safeTargets(); for (auto const& target: m_chc.unsafeTargets()) solvedTargets[target.first] += target.second; - m_bmc.analyze(_source, solvedTargets); + if (m_settings.engine.bmc) + m_bmc.analyze(_source, solvedTargets); } vector ModelChecker::unhandledQueries() @@ -59,7 +66,7 @@ solidity::smtutil::SMTSolverChoice ModelChecker::availableSolvers() { smtutil::SMTSolverChoice available = smtutil::SMTSolverChoice::None(); #ifdef HAVE_Z3 - available.z3 = true; + available.z3 = solidity::smtutil::Z3Interface::available(); #endif #ifdef HAVE_CVC4 available.cvc4 = true; diff --git a/libsolidity/formal/ModelChecker.h b/libsolidity/formal/ModelChecker.h index 42d116b28556..78770aa4e8b2 100644 --- a/libsolidity/formal/ModelChecker.h +++ b/libsolidity/formal/ModelChecker.h @@ -32,6 +32,8 @@ #include #include +#include + namespace solidity::langutil { class ErrorReporter; @@ -41,6 +43,40 @@ struct SourceLocation; namespace solidity::frontend { +struct ModelCheckerEngine +{ + bool bmc = false; + bool chc = false; + + static constexpr ModelCheckerEngine All() { return {true, true}; } + static constexpr ModelCheckerEngine BMC() { return {true, false}; } + static constexpr ModelCheckerEngine CHC() { return {false, true}; } + static constexpr ModelCheckerEngine None() { return {false, false}; } + + bool none() const { return !any(); } + bool any() const { return bmc || chc; } + bool all() const { return bmc && chc; } + + static std::optional fromString(std::string const& _engine) + { + static std::map engineMap{ + {"all", All()}, + {"bmc", BMC()}, + {"chc", CHC()}, + {"none", None()} + }; + if (engineMap.count(_engine)) + return engineMap.at(_engine); + return {}; + } +}; + +struct ModelCheckerSettings +{ + ModelCheckerEngine engine = ModelCheckerEngine::All(); + std::optional timeout; +}; + class ModelChecker { public: @@ -49,6 +85,7 @@ class ModelChecker ModelChecker( langutil::ErrorReporter& _errorReporter, std::map const& _smtlib2Responses, + ModelCheckerSettings _settings = ModelCheckerSettings{}, ReadCallback::Callback const& _smtCallback = ReadCallback::Callback(), smtutil::SMTSolverChoice _enabledSolvers = smtutil::SMTSolverChoice::All() ); @@ -64,6 +101,8 @@ class ModelChecker static smtutil::SMTSolverChoice availableSolvers(); private: + ModelCheckerSettings m_settings; + /// Stores the context of the encoding. smt::EncodingContext m_context; diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp new file mode 100644 index 000000000000..b95738eaf6b4 --- /dev/null +++ b/libsolidity/formal/Predicate.cpp @@ -0,0 +1,401 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::smtutil; +using namespace solidity::frontend; +using namespace solidity::frontend::smt; + +map Predicate::m_predicates; + +Predicate const* Predicate::create( + SortPointer _sort, + string _name, + PredicateType _type, + EncodingContext& _context, + ASTNode const* _node +) +{ + smt::SymbolicFunctionVariable predicate{_sort, move(_name), _context}; + string functorName = predicate.currentName(); + solAssert(!m_predicates.count(functorName), ""); + return &m_predicates.emplace( + std::piecewise_construct, + std::forward_as_tuple(functorName), + std::forward_as_tuple(move(predicate), _type, _node) + ).first->second; +} + +Predicate::Predicate( + smt::SymbolicFunctionVariable&& _predicate, + PredicateType _type, + ASTNode const* _node +): + m_predicate(move(_predicate)), + m_type(_type), + m_node(_node) +{ +} + +Predicate const* Predicate::predicate(string const& _name) +{ + return &m_predicates.at(_name); +} + +void Predicate::reset() +{ + m_predicates.clear(); +} + +smtutil::Expression Predicate::operator()(vector const& _args) const +{ + return m_predicate(_args); +} + +smtutil::Expression Predicate::functor() const +{ + return m_predicate.currentFunctionValue(); +} + +smtutil::Expression Predicate::functor(unsigned _idx) const +{ + return m_predicate.functionValueAtIndex(_idx); +} + +void Predicate::newFunctor() +{ + m_predicate.increaseIndex(); +} + +ASTNode const* Predicate::programNode() const +{ + return m_node; +} + +ContractDefinition const* Predicate::programContract() const +{ + if (auto const* contract = dynamic_cast(m_node)) + if (!contract->constructor()) + return contract; + + return nullptr; +} + +FunctionDefinition const* Predicate::programFunction() const +{ + if (auto const* contract = dynamic_cast(m_node)) + { + if (contract->constructor()) + return contract->constructor(); + return nullptr; + } + + if (auto const* fun = dynamic_cast(m_node)) + return fun; + + return nullptr; +} + +optional> Predicate::stateVariables() const +{ + if (auto const* fun = programFunction()) + return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun); + if (auto const* contract = programContract()) + return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*contract); + + auto const* node = m_node; + while (auto const* scopable = dynamic_cast(node)) + { + node = scopable->scope(); + if (auto const* fun = dynamic_cast(node)) + return SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*fun); + } + + return nullopt; +} + +bool Predicate::isSummary() const +{ + return m_type == PredicateType::ConstructorSummary || m_type == PredicateType::FunctionSummary; +} + +bool Predicate::isInterface() const +{ + return m_type == PredicateType::Interface; +} + +string Predicate::formatSummaryCall(vector const& _args) const +{ + if (programContract()) + return "constructor()"; + + solAssert(isSummary(), ""); + + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + auto const* fun = programFunction(); + solAssert(fun, ""); + + /// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// Here we are interested in preInputVars. + auto first = _args.begin() + 5 + static_cast(stateVars->size()); + auto last = first + static_cast(fun->parameters().size()); + solAssert(first >= _args.begin() && first <= _args.end(), ""); + solAssert(last >= _args.begin() && last <= _args.end(), ""); + auto inTypes = FunctionType(*fun).parameterTypes(); + vector> functionArgsCex = formatExpressions(vector(first, last), inTypes); + vector functionArgs; + + auto const& params = fun->parameters(); + solAssert(params.size() == functionArgsCex.size(), ""); + for (unsigned i = 0; i < params.size(); ++i) + if (params.at(i) && functionArgsCex.at(i)) + functionArgs.emplace_back(*functionArgsCex.at(i)); + else + functionArgs.emplace_back(params[i]->name()); + + string fName = fun->isConstructor() ? "constructor" : + fun->isFallback() ? "fallback" : + fun->isReceive() ? "receive" : + fun->name(); + return fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")"; + +} + +vector> Predicate::summaryStateValues(vector const& _args) const +{ + /// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// The signature of the summary predicate of a contract without constructor is: summary(error, this, cryptoFunctions, txData, preBlockchainState, postBlockchainState, preStateVars, postStateVars). + /// Here we are interested in postStateVars. + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + + vector::const_iterator stateFirst; + vector::const_iterator stateLast; + if (auto const* function = programFunction()) + { + stateFirst = _args.begin() + 5 + static_cast(stateVars->size()) + static_cast(function->parameters().size()) + 1; + stateLast = stateFirst + static_cast(stateVars->size()); + } + else if (programContract()) + { + stateFirst = _args.begin() + 6 + static_cast(stateVars->size()); + stateLast = stateFirst + static_cast(stateVars->size()); + } + else + solAssert(false, ""); + + solAssert(stateFirst >= _args.begin() && stateFirst <= _args.end(), ""); + solAssert(stateLast >= _args.begin() && stateLast <= _args.end(), ""); + + vector stateArgs(stateFirst, stateLast); + solAssert(stateArgs.size() == stateVars->size(), ""); + auto stateTypes = applyMap(*stateVars, [&](auto const& _var) { return _var->type(); }); + return formatExpressions(stateArgs, stateTypes); +} + +vector> Predicate::summaryPostInputValues(vector const& _args) const +{ + /// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// Here we are interested in postInputVars. + auto const* function = programFunction(); + solAssert(function, ""); + + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + + auto const& inParams = function->parameters(); + + auto first = _args.begin() + 5 + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()) + 1; + auto last = first + static_cast(inParams.size()); + + solAssert(first >= _args.begin() && first <= _args.end(), ""); + solAssert(last >= _args.begin() && last <= _args.end(), ""); + + vector inValues(first, last); + solAssert(inValues.size() == inParams.size(), ""); + auto inTypes = FunctionType(*function).parameterTypes(); + return formatExpressions(inValues, inTypes); +} + +vector> Predicate::summaryPostOutputValues(vector const& _args) const +{ + /// The signature of a function summary predicate is: summary(error, this, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// Here we are interested in outputVars. + auto const* function = programFunction(); + solAssert(function, ""); + + auto stateVars = stateVariables(); + solAssert(stateVars.has_value(), ""); + + auto const& inParams = function->parameters(); + + auto first = _args.begin() + 5 + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()) * 2 + 1; + + solAssert(first >= _args.begin() && first <= _args.end(), ""); + + vector outValues(first, _args.end()); + solAssert(outValues.size() == function->returnParameters().size(), ""); + auto outTypes = FunctionType(*function).returnParameterTypes(); + return formatExpressions(outValues, outTypes); +} + +vector> Predicate::formatExpressions(vector const& _exprs, vector const& _types) const +{ + solAssert(_exprs.size() == _types.size(), ""); + vector> strExprs; + for (unsigned i = 0; i < _exprs.size(); ++i) + strExprs.push_back(expressionToString(_exprs.at(i), _types.at(i))); + return strExprs; +} + +optional Predicate::expressionToString(smtutil::Expression const& _expr, TypePointer _type) const +{ + if (smt::isNumber(*_type)) + { + solAssert(_expr.sort->kind == Kind::Int, ""); + solAssert(_expr.arguments.empty(), ""); + // TODO assert that _expr.name is a number. + return _expr.name; + } + if (smt::isBool(*_type)) + { + solAssert(_expr.sort->kind == Kind::Bool, ""); + solAssert(_expr.arguments.empty(), ""); + solAssert(_expr.name == "true" || _expr.name == "false", ""); + return _expr.name; + } + if (smt::isFunction(*_type)) + { + solAssert(_expr.arguments.empty(), ""); + return _expr.name; + } + if (smt::isArray(*_type)) + { + auto const& arrayType = dynamic_cast(*_type); + solAssert(_expr.name == "tuple_constructor", ""); + auto const& tupleSort = dynamic_cast(*_expr.sort); + solAssert(tupleSort.components.size() == 2, ""); + + unsigned long length; + try + { + length = stoul(_expr.arguments.at(1).name); + } + catch(out_of_range const&) + { + return {}; + } + // Limit this counterexample size to 1k. + // Some OSs give you "unlimited" memory through swap and other virtual memory, + // so purely relying on bad_alloc being thrown is not a good idea. + // In that case, the array allocation might cause OOM and the program is killed. + if (length >= 1024) + return {}; + try + { + vector array(length); + if (!fillArray(_expr.arguments.at(0), array, arrayType)) + return {}; + return "[" + boost::algorithm::join(array, ", ") + "]"; + } + catch (bad_alloc const&) + { + // Solver gave a concrete array but length is too large. + } + } + if (smt::isNonRecursiveStruct(*_type)) + { + auto const& structType = dynamic_cast(*_type); + solAssert(_expr.name == "tuple_constructor", ""); + auto const& tupleSort = dynamic_cast(*_expr.sort); + auto members = structType.structDefinition().members(); + solAssert(tupleSort.components.size() == members.size(), ""); + solAssert(_expr.arguments.size() == members.size(), ""); + vector elements; + for (unsigned i = 0; i < members.size(); ++i) + { + optional elementStr = expressionToString(_expr.arguments.at(i), members[i]->type()); + elements.push_back(members[i]->name() + (elementStr.has_value() ? ": " + elementStr.value() : "")); + } + return "{" + boost::algorithm::join(elements, ", ") + "}"; + } + + return {}; +} + +bool Predicate::fillArray(smtutil::Expression const& _expr, vector& _array, ArrayType const& _type) const +{ + // Base case + if (_expr.name == "const_array") + { + auto length = _array.size(); + optional elemStr = expressionToString(_expr.arguments.at(1), _type.baseType()); + if (!elemStr) + return false; + _array.clear(); + _array.resize(length, *elemStr); + return true; + } + + // Recursive case. + if (_expr.name == "store") + { + if (!fillArray(_expr.arguments.at(0), _array, _type)) + return false; + optional indexStr = expressionToString(_expr.arguments.at(1), TypeProvider::uint256()); + if (!indexStr) + return false; + // Sometimes the solver assigns huge lengths that are not related, + // we should catch and ignore those. + unsigned long index; + try + { + index = stoul(*indexStr); + } + catch (out_of_range const&) + { + return true; + } + optional elemStr = expressionToString(_expr.arguments.at(2), _type.baseType()); + if (!elemStr) + return false; + if (index < _array.size()) + _array.at(index) = *elemStr; + return true; + } + + // Special base case, not supported yet. + if (_expr.name.rfind("(_ as-array") == 0) + { + // Z3 expression representing reinterpretation of a different term as an array + return false; + } + + solAssert(false, ""); +} diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h new file mode 100644 index 000000000000..3b295e286d37 --- /dev/null +++ b/libsolidity/formal/Predicate.h @@ -0,0 +1,150 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include + +#include +#include +#include + +namespace solidity::frontend +{ + +enum class PredicateType +{ + Interface, + NondetInterface, + ConstructorSummary, + FunctionSummary, + FunctionBlock, + Error, + Custom +}; + +/** + * Represents a predicate used by the CHC engine. + */ +class Predicate +{ +public: + static Predicate const* create( + smtutil::SortPointer _sort, + std::string _name, + PredicateType _type, + smt::EncodingContext& _context, + ASTNode const* _node = nullptr + ); + + Predicate( + smt::SymbolicFunctionVariable&& _predicate, + PredicateType _type, + ASTNode const* _node = nullptr + ); + + /// Predicate should not be copiable. + Predicate(Predicate const&) = delete; + Predicate& operator=(Predicate const&) = delete; + + /// @returns the Predicate associated with _name. + static Predicate const* predicate(std::string const& _name); + + /// Resets all the allocated predicates. + static void reset(); + + /// @returns a function application of the predicate over _args. + smtutil::Expression operator()(std::vector const& _args) const; + + /// @returns the function declaration of the predicate. + smtutil::Expression functor() const; + /// @returns the function declaration of the predicate with index _idx. + smtutil::Expression functor(unsigned _idx) const; + /// Increases the index of the function declaration of the predicate. + void newFunctor(); + + /// @returns the program node this predicate represents. + ASTNode const* programNode() const; + + /// @returns the ContractDefinition that this predicate represents + /// or nullptr otherwise. + ContractDefinition const* programContract() const; + + /// @returns the FunctionDefinition that this predicate represents + /// or nullptr otherwise. + FunctionDefinition const* programFunction() const; + + /// @returns the program state variables in the scope of this predicate. + std::optional> stateVariables() const; + + /// @returns true if this predicate represents a summary. + bool isSummary() const; + + /// @returns true if this predicate represents an interface. + bool isInterface() const; + + PredicateType type() const { return m_type; } + + /// @returns a formatted string representing a call to this predicate + /// with _args. + std::string formatSummaryCall(std::vector const& _args) const; + + /// @returns the values of the state variables from _args at the point + /// where this summary was reached. + std::vector> summaryStateValues(std::vector const& _args) const; + + /// @returns the values of the function input variables from _args at the point + /// where this summary was reached. + std::vector> summaryPostInputValues(std::vector const& _args) const; + + /// @returns the values of the function output variables from _args at the point + /// where this summary was reached. + std::vector> summaryPostOutputValues(std::vector const& _args) const; + +private: + /// @returns the formatted version of the given SMT expressions. Those expressions must be SMT constants. + std::vector> formatExpressions(std::vector const& _exprs, std::vector const& _types) const; + + /// @returns a string representation of the SMT expression based on a Solidity type. + std::optional expressionToString(smtutil::Expression const& _expr, TypePointer _type) const; + + /// Recursively fills _array from _expr. + /// _expr should have the form `store(store(...(const_array(x_0), i_0, e_0), i_m, e_m), i_k, e_k)`. + /// @returns true if the construction worked, + /// and false if at least one element could not be built. + bool fillArray(smtutil::Expression const& _expr, std::vector& _array, ArrayType const& _type) const; + + /// The actual SMT expression. + smt::SymbolicFunctionVariable m_predicate; + + /// The type of this predicate. + PredicateType m_type; + + /// The ASTNode that this predicate represents. + /// nullptr if this predicate is not associated with a specific program AST node. + ASTNode const* m_node = nullptr; + + /// Maps the name of the predicate to the actual Predicate. + /// Used in counterexample generation. + static std::map m_predicates; +}; + +} diff --git a/libsolidity/formal/PredicateInstance.cpp b/libsolidity/formal/PredicateInstance.cpp new file mode 100644 index 000000000000..53cc03c7965c --- /dev/null +++ b/libsolidity/formal/PredicateInstance.cpp @@ -0,0 +1,187 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::smtutil; + +namespace solidity::frontend::smt +{ +smtutil::Expression interfacePre(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context) +{ + auto& state = _context.state(); + vector stateExprs{state.thisAddress(0), state.crypto(0), state.state(0)}; + return _pred(stateExprs + initialStateVariables(_contract, _context)); +} + +smtutil::Expression interface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context) +{ + auto& state = _context.state(); + vector stateExprs{state.thisAddress(0), state.crypto(0), state.state()}; + return _pred(stateExprs + currentStateVariables(_contract, _context)); +} + +smtutil::Expression nondetInterface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context, unsigned _preIdx, unsigned _postIdx) +{ + return _pred( + vector{_context.state().state(_preIdx)} + + stateVariablesAtIndex(_preIdx, _contract, _context) + + vector{_context.state().state(_postIdx)} + + stateVariablesAtIndex(_postIdx, _contract, _context) + ); +} + +smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _context) +{ + auto const& contract = dynamic_cast(*_pred.programNode()); + if (auto const* constructor = contract.constructor()) + return _pred(currentFunctionVariablesForDefinition(*constructor, &contract, _context)); + + auto& state = _context.state(); + vector stateExprs{state.errorFlag().currentValue(), state.thisAddress(0), state.crypto(0), state.tx(0), state.state(0), state.state()}; + return _pred(stateExprs + initialStateVariables(contract, _context) + currentStateVariables(contract, _context)); +} + +smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context) +{ + auto const& contract = dynamic_cast(*_pred.programNode()); + if (auto const* constructor = contract.constructor()) + return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context)); + + auto& state = _context.state(); + vector stateExprs{state.errorFlag().currentValue(), state.thisAddress(0), state.crypto(0), state.tx(0), state.state()}; + state.newState(); + stateExprs += vector{state.state()}; + stateExprs += currentStateVariables(contract, _context); + stateExprs += newStateVariables(contract, _context); + return _pred(stateExprs); +} + +smtutil::Expression function( + Predicate const& _pred, + ContractDefinition const* _contract, + EncodingContext& _context +) +{ + auto const& function = dynamic_cast(*_pred.programNode()); + return _pred(currentFunctionVariablesForDefinition(function, _contract, _context)); +} + +smtutil::Expression functionCall( + Predicate const& _pred, + ContractDefinition const* _contract, + EncodingContext& _context +) +{ + auto const& function = dynamic_cast(*_pred.programNode()); + return _pred(currentFunctionVariablesForCall(function, _contract, _context)); +} + +smtutil::Expression functionBlock( + Predicate const& _pred, + FunctionDefinition const& _function, + ContractDefinition const* _contract, + EncodingContext& _context +) +{ + return _pred(currentBlockVariables(_function, _contract, _context)); +} + +/// Helpers + +vector initialStateVariables(ContractDefinition const& _contract, EncodingContext& _context) +{ + return stateVariablesAtIndex(0, _contract, _context); +} + +vector stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract, EncodingContext& _context) +{ + return applyMap( + SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), + [&](auto _var) { return _context.variable(*_var)->valueAtIndex(_index); } + ); +} + +vector currentStateVariables(ContractDefinition const& _contract, EncodingContext& _context) +{ + return applyMap( + SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), + [&](auto _var) { return _context.variable(*_var)->currentValue(); } + ); +} + +vector newStateVariables(ContractDefinition const& _contract, EncodingContext& _context) +{ + return applyMap( + SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), + [&](auto _var) { return _context.variable(*_var)->increaseIndex(); } + ); +} + +vector currentFunctionVariablesForDefinition( + FunctionDefinition const& _function, + ContractDefinition const* _contract, + EncodingContext& _context +) +{ + auto& state = _context.state(); + vector exprs{state.errorFlag().currentValue(), state.thisAddress(0), state.crypto(0), state.tx(0), state.state(0)}; + exprs += _contract ? initialStateVariables(*_contract, _context) : vector{}; + exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->valueAtIndex(0); }); + exprs += vector{state.state()}; + exprs += _contract ? currentStateVariables(*_contract, _context) : vector{}; + exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); }); + exprs += applyMap(_function.returnParameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); }); + return exprs; +} + +vector currentFunctionVariablesForCall( + FunctionDefinition const& _function, + ContractDefinition const* _contract, + EncodingContext& _context +) +{ + auto& state = _context.state(); + vector exprs{state.errorFlag().currentValue(), state.thisAddress(0), state.crypto(0), state.tx(0), state.state()}; + exprs += _contract ? currentStateVariables(*_contract, _context) : vector{}; + exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); }); + + state.newState(); + + exprs += vector{state.state()}; + exprs += _contract ? newStateVariables(*_contract, _context) : vector{}; + exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->increaseIndex(); }); + exprs += applyMap(_function.returnParameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); }); + return exprs; +} + +vector currentBlockVariables(FunctionDefinition const& _function, ContractDefinition const* _contract, EncodingContext& _context) +{ + return currentFunctionVariablesForDefinition(_function, _contract, _context) + + applyMap( + SMTEncoder::localVariablesIncludingModifiers(_function), + [&](auto _var) { return _context.variable(*_var)->currentValue(); } + ); +} + +} diff --git a/libsolidity/formal/PredicateInstance.h b/libsolidity/formal/PredicateInstance.h new file mode 100644 index 000000000000..2f6c77381319 --- /dev/null +++ b/libsolidity/formal/PredicateInstance.h @@ -0,0 +1,89 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +namespace solidity::frontend::smt +{ + +class EncodingContext; + +/** + * This file represents the specification for building CHC predicate instances. + * The predicates follow the specification in PredicateSort.h. + * */ + +smtutil::Expression interfacePre(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context); + +smtutil::Expression interface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context); + +smtutil::Expression nondetInterface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context, unsigned _preIdx, unsigned _postIdx); + +smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _context); +smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _context); + +smtutil::Expression function( + Predicate const& _pred, + ContractDefinition const* _contract, + EncodingContext& _context +); + +smtutil::Expression functionCall( + Predicate const& _pred, + ContractDefinition const* _contract, + EncodingContext& _context +); + +smtutil::Expression functionBlock( + Predicate const& _pred, + FunctionDefinition const& _function, + ContractDefinition const* _contract, + EncodingContext& _context +); + +/// Helpers + +std::vector initialStateVariables(ContractDefinition const& _contract, EncodingContext& _context); + +std::vector stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract, EncodingContext& _context); + +std::vector currentStateVariables(ContractDefinition const& _contract, EncodingContext& _context); + +std::vector newStateVariables(ContractDefinition const& _contract, EncodingContext& _context); + +std::vector currentFunctionVariablesForDefinition( + FunctionDefinition const& _function, + ContractDefinition const* _contract, + EncodingContext& _context +); + +std::vector currentFunctionVariablesForCall( + FunctionDefinition const& _function, + ContractDefinition const* _contract, + EncodingContext& _context +); + +std::vector currentBlockVariables( + FunctionDefinition const& _function, + ContractDefinition const* _contract, + EncodingContext& _context +); + +} diff --git a/libsolidity/formal/PredicateSort.cpp b/libsolidity/formal/PredicateSort.cpp new file mode 100644 index 000000000000..2c3443bc6ac2 --- /dev/null +++ b/libsolidity/formal/PredicateSort.cpp @@ -0,0 +1,110 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::smtutil; + +namespace solidity::frontend::smt +{ + +SortPointer interfaceSort(ContractDefinition const& _contract, SymbolicState& _state) +{ + return make_shared( + vector{_state.thisAddressSort(), _state.cryptoSort(), _state.stateSort()} + stateSorts(_contract), + SortProvider::boolSort + ); +} + +SortPointer nondetInterfaceSort(ContractDefinition const& _contract, SymbolicState& _state) +{ + auto varSorts = stateSorts(_contract); + vector stateSort{_state.stateSort()}; + return make_shared( + stateSort + varSorts + stateSort + varSorts, + SortProvider::boolSort + ); +} + +SortPointer constructorSort(ContractDefinition const& _contract, SymbolicState& _state) +{ + if (auto const* constructor = _contract.constructor()) + return functionSort(*constructor, &_contract, _state); + + auto varSorts = stateSorts(_contract); + vector stateSort{_state.stateSort()}; + return make_shared( + vector{_state.errorFlagSort(), _state.thisAddressSort(), _state.cryptoSort(), _state.txSort(), _state.stateSort(), _state.stateSort()} + varSorts + varSorts, + SortProvider::boolSort + ); +} + +SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state) +{ + auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; + auto varSorts = _contract ? stateSorts(*_contract) : vector{}; + auto inputSorts = applyMap(_function.parameters(), smtSort); + auto outputSorts = applyMap(_function.returnParameters(), smtSort); + return make_shared( + vector{_state.errorFlagSort(), _state.thisAddressSort(), _state.cryptoSort(), _state.txSort(), _state.stateSort()} + + varSorts + + inputSorts + + vector{_state.stateSort()} + + varSorts + + inputSorts + + outputSorts, + SortProvider::boolSort + ); +} + +SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state) +{ + auto fSort = dynamic_pointer_cast(functionSort(_function, _contract, _state)); + solAssert(fSort, ""); + + auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; + return make_shared( + fSort->domain + applyMap(SMTEncoder::localVariablesIncludingModifiers(_function), smtSort), + SortProvider::boolSort + ); +} + +SortPointer arity0FunctionSort() +{ + return make_shared( + vector(), + SortProvider::boolSort + ); +} + +/// Helpers + +vector stateSorts(ContractDefinition const& _contract) +{ + return applyMap( + SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), + [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); } + ); +} + +} diff --git a/libsolidity/formal/PredicateSort.h b/libsolidity/formal/PredicateSort.h new file mode 100644 index 000000000000..b30d09a71ae2 --- /dev/null +++ b/libsolidity/formal/PredicateSort.h @@ -0,0 +1,79 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include + +#include + +namespace solidity::frontend::smt +{ + +/** + * This file represents the specification for CHC predicate sorts. + * Types of predicates: + * + * 1. Interface + * The idle state of a contract. Signature: + * interface(this, cryptoFunctions, blockchainState, stateVariables). + * + * 2. Nondet interface + * The nondeterminism behavior of a contract. Signature: + * nondet_interface(blockchainState, stateVariables, blockchainState', stateVariables'). + * + * 3. Constructor entry/summary + * The summary of a contract's deployment procedure. + * Signature: + * If the contract has a constructor function, this is the same as the summary of that function. Otherwise: + * constructor_summary(error, this, cryptoFunctions, txData, blockchainState, blockchainState', stateVariables, stateVariables'). + * + * 4. Function entry/summary + * The entry point of a function definition. Signature: + * function_entry(error, this, cryptoFunctions, txData, blockchainState, stateVariables, inputVariables, blockchainState', stateVariables', inputVariables', outputVariables'). + * + * 5. Function body + * Use for any predicate within a function. Signature: + * function_body(error, this, txData, blockchainState, stateVariables, inputVariables, blockchainState', stateVariables', inputVariables', outputVariables', localVariables). + */ + +/// @returns the interface predicate sort for _contract. +smtutil::SortPointer interfaceSort(ContractDefinition const& _contract, SymbolicState& _state); + +/// @returns the nondeterminisc interface predicate sort for _contract. +smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _contract, SymbolicState& _state); + +/// @returns the constructor entry/summary predicate sort for _contract. +smtutil::SortPointer constructorSort(ContractDefinition const& _contract, SymbolicState& _state); + +/// @returns the function entry/summary predicate sort for _function contained in _contract. +smtutil::SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state); + +/// @returns the function body predicate sort for _function contained in _contract. +smtutil::SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state); + +/// @returns the sort of a predicate without parameters. +smtutil::SortPointer arity0FunctionSort(); + +/// Helpers + +std::vector stateSorts(ContractDefinition const& _contract) ; + +} diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index a84ad4333f56..5c5c8d322be9 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -22,7 +22,10 @@ #include #include +#include + #include +#include #include #include @@ -39,6 +42,38 @@ SMTEncoder::SMTEncoder(smt::EncodingContext& _context): { } +bool SMTEncoder::analyze(SourceUnit const& _source) +{ + set sources; + sources.insert(&_source); + for (auto const& source: _source.referencedSourceUnits(true)) + sources.insert(source); + + bool analysis = true; + for (auto source: sources) + for (auto node: source->nodes()) + if (auto function = dynamic_pointer_cast(node)) + { + m_errorReporter.warning( + 6660_error, + function->location(), + "Model checker analysis was not possible because file level functions are not supported." + ); + analysis = false; + } + else if (auto var = dynamic_pointer_cast(node)) + { + m_errorReporter.warning( + 8195_error, + var->location(), + "Model checker analysis was not possible because file level constants are not supported." + ); + analysis = false; + } + + return analysis; +} + bool SMTEncoder::visit(ContractDefinition const& _contract) { solAssert(m_currentContract, ""); @@ -57,7 +92,7 @@ bool SMTEncoder::visit(ContractDefinition const& _contract) if (auto const& constructor = base->constructor()) for (auto const& invocation: constructor->modifiers()) { - auto refDecl = invocation->name()->annotation().referencedDeclaration; + auto refDecl = invocation->name().annotation().referencedDeclaration; if (auto const& baseContract = dynamic_cast(refDecl)) { solAssert(!m_baseConstructorCalls.count(baseContract), ""); @@ -130,13 +165,7 @@ bool SMTEncoder::visit(FunctionDefinition const& _function) { m_modifierDepthStack.push_back(-1); - if (_function.isConstructor()) - inlineConstructorHierarchy(dynamic_cast(*_function.scope())); - - // Base constructors' parameters should be set by explicit calls, - // but the most derived one needs to be initialized. - if (_function.scope() == m_currentContract) - initializeLocalVariables(_function); + initializeLocalVariables(_function); _function.parameterList().accept(*this); if (_function.returnParameterList()) @@ -158,7 +187,11 @@ void SMTEncoder::visitFunctionOrModifier() if (m_modifierDepthStack.back() == static_cast(function.modifiers().size())) { if (function.isImplemented()) + { + pushInlineFrame(function); function.body().accept(*this); + popInlineFrame(function); + } } else { @@ -166,7 +199,7 @@ void SMTEncoder::visitFunctionOrModifier() ASTPointer const& modifierInvocation = function.modifiers()[static_cast(m_modifierDepthStack.back())]; solAssert(modifierInvocation, ""); - auto refDecl = modifierInvocation->name()->annotation().referencedDeclaration; + auto refDecl = modifierInvocation->name().annotation().referencedDeclaration; if (dynamic_cast(refDecl)) visitFunctionOrModifier(); else if (auto modifierDef = dynamic_cast(refDecl)) @@ -195,6 +228,7 @@ void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation, initializeFunctionCallParameters(*_definition, args); pushCallStack({_definition, _invocation}); + pushInlineFrame(*_definition); if (auto modifier = dynamic_cast(_definition)) { if (modifier->isImplemented()) @@ -207,6 +241,7 @@ void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation, function->accept(*this); // Functions are popped from the callstack in endVisit(FunctionDefinition) } + popInlineFrame(*_definition); } void SMTEncoder::inlineConstructorHierarchy(ContractDefinition const& _contract) @@ -290,24 +325,14 @@ bool SMTEncoder::visit(TryCatchClause const& _clause) return false; } -bool SMTEncoder::visit(IfStatement const& _node) +void SMTEncoder::pushInlineFrame(CallableDeclaration const&) { - _node.condition().accept(*this); - - auto indicesEndTrue = visitBranch(&_node.trueStatement(), expr(_node.condition())); - auto touchedVars = touchedVariables(_node.trueStatement()); - decltype(indicesEndTrue) indicesEndFalse; - if (_node.falseStatement()) - { - indicesEndFalse = visitBranch(_node.falseStatement(), !expr(_node.condition())); - touchedVars += touchedVariables(*_node.falseStatement()); - } - else - indicesEndFalse = copyVariableIndices(); - - mergeVariables(touchedVars, expr(_node.condition()), indicesEndTrue, indicesEndFalse); + pushPathCondition(currentPathConditions()); +} - return false; +void SMTEncoder::popInlineFrame(CallableDeclaration const&) +{ + popPathCondition(); } void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl) @@ -336,49 +361,55 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl) assignment(*declarations.at(i), symbTuple->component(i, components.at(i), declarations.at(i)->type())); } } - else if (m_context.knownVariable(*_varDecl.declarations().front())) + else { + solAssert(m_context.knownVariable(*_varDecl.declarations().front()), ""); if (_varDecl.initialValue()) assignment(*_varDecl.declarations().front(), *_varDecl.initialValue()); } - else - m_errorReporter.warning( - 7186_error, - _varDecl.location(), - "Assertion checker does not yet implement such variable declarations." - ); +} + +bool SMTEncoder::visit(Assignment const& _assignment) +{ + auto const& left = _assignment.leftHandSide(); + auto const& right = _assignment.rightHandSide(); + + if (auto const* memberAccess = isEmptyPush(left)) + { + right.accept(*this); + left.accept(*this); + + auto const& memberExpr = memberAccess->expression(); + auto& symbArray = dynamic_cast(*m_context.expression(memberExpr)); + smtutil::Expression oldElements = symbArray.elements(); + smtutil::Expression length = symbArray.length(); + symbArray.increaseIndex(); + m_context.addAssertion(symbArray.elements() == smtutil::Expression::store( + oldElements, + length - 1, + expr(right) + )); + m_context.addAssertion(symbArray.length() == length); + + arrayPushPopAssign(memberExpr, symbArray.currentValue()); + defineExpr(_assignment, expr(left)); + return false; + } + return true; } void SMTEncoder::endVisit(Assignment const& _assignment) { createExpr(_assignment); - static set const compoundOps{ - Token::AssignAdd, - Token::AssignSub, - Token::AssignMul, - Token::AssignDiv, - Token::AssignMod - }; Token op = _assignment.assignmentOperator(); - if (op != Token::Assign && !compoundOps.count(op)) - { - Expression const* identifier = &_assignment.leftHandSide(); - if (auto const* indexAccess = dynamic_cast(identifier)) - identifier = leftmostBase(*indexAccess); - // Give it a new index anyway to keep the SSA scheme sound. - solAssert(identifier, ""); - if (auto varDecl = identifierToVariable(*identifier)) - m_context.newValue(*varDecl); + solAssert(TokenTraits::isAssignmentOp(op), ""); - m_errorReporter.warning( - 9149_error, - _assignment.location(), - "Assertion checker does not yet implement this assignment operator." - ); - } - else if (!smt::isSupportedType(_assignment.annotation().type->category())) + if (isEmptyPush(_assignment.leftHandSide())) + return; + + if (!smt::isSupportedType(*_assignment.annotation().type)) { // Give it a new index anyway to keep the SSA scheme sound. @@ -396,15 +427,14 @@ void SMTEncoder::endVisit(Assignment const& _assignment) else { auto const& type = _assignment.annotation().type; - auto rightHandSide = compoundOps.count(op) ? - compoundAssignment(_assignment) : - expr(_assignment.rightHandSide(), type); + auto rightHandSide = op == Token::Assign ? + expr(_assignment.rightHandSide(), type) : + compoundAssignment(_assignment); defineExpr(_assignment, rightHandSide); assignment( _assignment.leftHandSide(), expr(_assignment, type), - type, - _assignment.location() + type ); } } @@ -415,13 +445,18 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) createExpr(_tuple); if (_tuple.isInlineArray()) - m_errorReporter.warning( - 2177_error, - _tuple.location(), - "Assertion checker does not yet implement inline arrays." - ); - else if (_tuple.annotation().type->category() == Type::Category::Tuple) { + // Add constraints for the length and values as it is known. + auto symbArray = dynamic_pointer_cast(m_context.expression(_tuple)); + solAssert(symbArray, ""); + + addArrayLiteralAssertions(*symbArray, applyMap(_tuple.components(), [&](auto const& c) { return expr(*c); })); + } + else if (_tuple.components().size() == 1) + defineExpr(_tuple, expr(*_tuple.components().front())); + else + { + solAssert(_tuple.annotation().type->category() == Type::Category::Tuple, ""); auto const& symbTuple = dynamic_pointer_cast(m_context.expression(_tuple)); solAssert(symbTuple, ""); auto const& symbComponents = symbTuple->components(); @@ -445,97 +480,106 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) } } } - else - { - /// Parenthesized expressions are also TupleExpression regardless their type. - auto const& components = _tuple.components(); - solAssert(components.size() == 1, ""); - defineExpr(_tuple, expr(*components.front())); - } } void SMTEncoder::endVisit(UnaryOperation const& _op) { + /// We need to shortcut here due to potentially unknown + /// rational number sizes. if (_op.annotation().type->category() == Type::Category::RationalNumber) return; + if (TokenTraits::isBitOp(_op.getOperator())) + return bitwiseNotOperation(_op); + createExpr(_op); + auto const* subExpr = innermostTuple(_op.subExpression()); + auto type = _op.annotation().type; switch (_op.getOperator()) { case Token::Not: // ! { - solAssert(smt::isBool(_op.annotation().type->category()), ""); - defineExpr(_op, !expr(_op.subExpression())); + solAssert(smt::isBool(*type), ""); + defineExpr(_op, !expr(*subExpr)); break; } case Token::Inc: // ++ (pre- or postfix) case Token::Dec: // -- (pre- or postfix) { - auto cat = _op.annotation().type->category(); - solAssert(smt::isInteger(cat) || smt::isFixedPoint(cat), ""); - solAssert(_op.subExpression().annotation().willBeWrittenTo, ""); - if (auto identifier = dynamic_cast(&_op.subExpression())) + solAssert(smt::isInteger(*type) || smt::isFixedPoint(*type), ""); + solAssert(subExpr->annotation().willBeWrittenTo, ""); + auto computeNewValue = [&](auto currentValue) { + return arithmeticOperation( + _op.getOperator() == Token::Inc ? Token::Add : Token::Sub, + currentValue, + smtutil::Expression(size_t(1)), + _op.annotation().type, + _op + ).first; + }; + if (auto identifier = dynamic_cast(subExpr)) { auto decl = identifierToVariable(*identifier); solAssert(decl, ""); auto innerValue = currentValue(*decl); - auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1; + auto newValue = computeNewValue(innerValue); defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); assignment(*decl, newValue); } - else if (dynamic_cast(&_op.subExpression())) + else if ( + dynamic_cast(subExpr) || + dynamic_cast(subExpr) + ) + { + auto innerValue = expr(*subExpr); + auto newValue = computeNewValue(innerValue); + defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); + indexOrMemberAssignment(*subExpr, newValue); + } + else if (isEmptyPush(*subExpr)) { - auto innerValue = expr(_op.subExpression()); - auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1; + auto innerValue = expr(*subExpr); + auto newValue = computeNewValue(innerValue); defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); - arrayIndexAssignment(_op.subExpression(), newValue); + arrayPushPopAssign(*subExpr, newValue); } else - m_errorReporter.warning( - 1950_error, - _op.location(), - "Assertion checker does not yet implement such increments / decrements." - ); + solAssert(false, ""); break; } case Token::Sub: // - { - defineExpr(_op, 0 - expr(_op.subExpression())); + defineExpr(_op, 0 - expr(*subExpr)); break; } case Token::Delete: { - auto const& subExpr = _op.subExpression(); - if (auto decl = identifierToVariable(subExpr)) + if (auto decl = identifierToVariable(*subExpr)) { m_context.newValue(*decl); m_context.setZeroValue(*decl); } else { - solAssert(m_context.knownExpression(subExpr), ""); - auto const& symbVar = m_context.expression(subExpr); + solAssert(m_context.knownExpression(*subExpr), ""); + auto const& symbVar = m_context.expression(*subExpr); symbVar->increaseIndex(); m_context.setZeroValue(*symbVar); - if (dynamic_cast(&_op.subExpression())) - arrayIndexAssignment(_op.subExpression(), symbVar->currentValue()); - else - m_errorReporter.warning( - 2683_error, - _op.location(), - "Assertion checker does not yet implement \"delete\" for this expression." - ); + if ( + dynamic_cast(subExpr) || + dynamic_cast(subExpr) + ) + indexOrMemberAssignment(*subExpr, symbVar->currentValue()); + // Empty push added a zero value anyway, so no need to delete extra. + else if (!isEmptyPush(*subExpr)) + solAssert(false, ""); } break; } default: - m_errorReporter.warning( - 3682_error, - _op.location(), - "Assertion checker does not yet implement this operator." - ); + solAssert(false, ""); } } @@ -558,7 +602,8 @@ bool SMTEncoder::visit(BinaryOperation const& _op) void SMTEncoder::endVisit(BinaryOperation const& _op) { - if (_op.annotation().type->category() == Type::Category::RationalNumber) + /// If _op is const evaluated the RationalNumber shortcut was taken. + if (isConstant(_op)) return; if (TokenTraits::isBooleanOp(_op.getOperator())) return; @@ -569,31 +614,45 @@ void SMTEncoder::endVisit(BinaryOperation const& _op) arithmeticOperation(_op); else if (TokenTraits::isCompareOp(_op.getOperator())) compareOperation(_op); - else if (TokenTraits::isBitOp(_op.getOperator())) + else if (TokenTraits::isBitOp(_op.getOperator()) || TokenTraits::isShiftOp(_op.getOperator())) bitwiseOperation(_op); else - m_errorReporter.warning( - 3876_error, - _op.location(), - "Assertion checker does not yet implement this operator." - ); + solAssert(false, ""); +} + +bool SMTEncoder::visit(Conditional const& _op) +{ + _op.condition().accept(*this); + + auto indicesEndTrue = visitBranch(&_op.trueExpression(), expr(_op.condition())).first; + auto touchedVars = touchedVariables(_op.trueExpression()); + + auto indicesEndFalse = visitBranch(&_op.falseExpression(), !expr(_op.condition())).first; + touchedVars += touchedVariables(_op.falseExpression()); + + mergeVariables(touchedVars, expr(_op.condition()), indicesEndTrue, indicesEndFalse); + + defineExpr(_op, smtutil::Expression::ite( + expr(_op.condition()), + expr(_op.trueExpression(), _op.annotation().type), + expr(_op.falseExpression(), _op.annotation().type) + )); + + return false; } void SMTEncoder::endVisit(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); + auto functionCallKind = *_funCall.annotation().kind; + createExpr(_funCall); - if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall) + if (functionCallKind == FunctionCallKind::StructConstructorCall) { - m_errorReporter.warning( - 4639_error, - _funCall.location(), - "Assertion checker does not yet implement this expression." - ); + visitStructConstructorCall(_funCall); return; } - if (_funCall.annotation().kind == FunctionCallKind::TypeConversion) + if (functionCallKind == FunctionCallKind::TypeConversion) { visitTypeConversion(_funCall); return; @@ -610,11 +669,18 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::Require: visitRequire(_funCall); break; + case FunctionType::Kind::Revert: + // Revert is a special case of require and equals to `require(false)` + addPathImpliedExpression(smtutil::Expression(false)); + break; case FunctionType::Kind::GasLeft: visitGasLeft(_funCall); break; - case FunctionType::Kind::Internal: case FunctionType::Kind::External: + if (isPublicGetter(_funCall.expression())) + visitPublicGetter(_funCall); + break; + case FunctionType::Kind::Internal: case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCall: case FunctionType::Kind::BareCallCode: @@ -632,9 +698,14 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::pedersenHash: case FunctionType::Kind::SHA256: case FunctionType::Kind::RIPEMD160: + visitCryptoFunction(_funCall); + break; case FunctionType::Kind::BlockHash: + defineExpr(_funCall, m_context.state().blockhash(expr(*_funCall.arguments().at(0)))); + break; case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: + visitAddMulMod(_funCall); break; case FunctionType::Kind::Send: case FunctionType::Kind::Transfer: @@ -651,11 +722,18 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) break; } case FunctionType::Kind::ArrayPush: + case FunctionType::Kind::ByteArrayPush: arrayPush(_funCall); break; case FunctionType::Kind::ArrayPop: arrayPop(_funCall); break; + case FunctionType::Kind::Event: + // This can be safely ignored. + break; + case FunctionType::Kind::ObjectCreation: + visitObjectCreation(_funCall); + return; default: m_errorReporter.warning( 4588_error, @@ -714,6 +792,38 @@ void SMTEncoder::visitRequire(FunctionCall const& _funCall) addPathImpliedExpression(expr(*args.front())); } +void SMTEncoder::visitCryptoFunction(FunctionCall const& _funCall) +{ + auto const& funType = dynamic_cast(*_funCall.expression().annotation().type); + auto kind = funType.kind(); + auto arg0 = expr(*_funCall.arguments().at(0)); + optional result; + if (kind == FunctionType::Kind::KECCAK256) + result = smtutil::Expression::select(m_context.state().cryptoFunction("keccak256"), arg0); + else if (kind == FunctionType::Kind::SHA256) + result = smtutil::Expression::select(m_context.state().cryptoFunction("sha256"), arg0); + else if (kind == FunctionType::Kind::RIPEMD160) + result = smtutil::Expression::select(m_context.state().cryptoFunction("ripemd160"), arg0); + else if (kind == FunctionType::Kind::ECRecover) + { + auto e = m_context.state().cryptoFunction("ecrecover"); + auto arg0 = expr(*_funCall.arguments().at(0)); + auto arg1 = expr(*_funCall.arguments().at(1)); + auto arg2 = expr(*_funCall.arguments().at(2)); + auto arg3 = expr(*_funCall.arguments().at(3)); + auto inputSort = dynamic_cast(*e.sort).domain; + auto ecrecoverInput = smtutil::Expression::tuple_constructor( + smtutil::Expression(make_shared(inputSort), ""), + {arg0, arg1, arg2, arg3} + ); + result = smtutil::Expression::select(e, ecrecoverInput); + } + else + solAssert(false, ""); + + defineExpr(_funCall, *result); +} + void SMTEncoder::visitGasLeft(FunctionCall const& _funCall) { string gasLeft = "gasleft()"; @@ -728,6 +838,43 @@ void SMTEncoder::visitGasLeft(FunctionCall const& _funCall) m_context.addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1)); } +void SMTEncoder::visitAddMulMod(FunctionCall const& _funCall) +{ + auto const& funType = dynamic_cast(*_funCall.expression().annotation().type); + auto kind = funType.kind(); + solAssert(kind == FunctionType::Kind::AddMod || kind == FunctionType::Kind::MulMod, ""); + auto const& args = _funCall.arguments(); + solAssert(args.at(0) && args.at(1) && args.at(2), ""); + auto x = expr(*args.at(0)); + auto y = expr(*args.at(1)); + auto k = expr(*args.at(2)); + auto const& intType = dynamic_cast(*_funCall.annotation().type); + + if (kind == FunctionType::Kind::AddMod) + defineExpr(_funCall, divModWithSlacks(x + y, k, intType).second); + else + defineExpr(_funCall, divModWithSlacks(x * y, k, intType).second); +} + +void SMTEncoder::visitObjectCreation(FunctionCall const& _funCall) +{ + auto const& args = _funCall.arguments(); + solAssert(args.size() >= 1, ""); + auto argType = args.front()->annotation().type->category(); + solAssert(argType == Type::Category::Integer || argType == Type::Category::RationalNumber, ""); + + smtutil::Expression arraySize = expr(*args.front()); + setSymbolicUnknownValue(arraySize, TypeProvider::uint256(), m_context); + + auto symbArray = dynamic_pointer_cast(m_context.expression(_funCall)); + solAssert(symbArray, ""); + smt::setSymbolicZeroValue(*symbArray, m_context); + auto zeroElements = symbArray->elements(); + symbArray->increaseIndex(); + m_context.addAssertion(symbArray->length() == arraySize); + m_context.addAssertion(symbArray->elements() == zeroElements); +} + void SMTEncoder::endVisit(Identifier const& _identifier) { if (auto decl = identifierToVariable(_identifier)) @@ -741,6 +888,16 @@ void SMTEncoder::endVisit(Identifier const& _identifier) defineExpr(_identifier, m_context.state().thisAddress()); m_uninterpretedTerms.insert(&_identifier); } + // Ignore type identifiers + else if (dynamic_cast(_identifier.annotation().type)) + return; + // Ignore the builtin abi, it is handled in FunctionCall. + // TODO: ignore MagicType in general (abi, block, msg, tx, type) + else if (auto magicType = dynamic_cast(_identifier.annotation().type); magicType && magicType->kind() == MagicType::Kind::ABI) + { + solAssert(_identifier.name() == "abi", ""); + return; + } else createExpr(_identifier); } @@ -757,40 +914,224 @@ void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName) m_context.createExpression(_typeName, result.second); } +namespace // helpers for SMTEncoder::visitPublicGetter +{ + +bool isReturnedFromStructGetter(TypePointer _type) +{ + // So far it seems that only Mappings and ordinary Arrays are not returned. + auto category = _type->category(); + if (category == Type::Category::Mapping) + return false; + if (category == Type::Category::Array) + return dynamic_cast(*_type).isByteArray(); + // default + return true; +} + +vector structGetterReturnedMembers(StructType const& _structType) +{ + vector returnedMembers; + for (auto const& member: _structType.nativeMembers(nullptr)) + if (isReturnedFromStructGetter(member.type)) + returnedMembers.push_back(member.name); + return returnedMembers; +} + +} + +void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall) +{ + MemberAccess const& access = dynamic_cast(_funCall.expression()); + auto var = dynamic_cast(access.annotation().referencedDeclaration); + solAssert(var, ""); + solAssert(m_context.knownExpression(_funCall), ""); + auto paramExpectedTypes = FunctionType(*var).parameterTypes(); + auto actualArguments = _funCall.arguments(); + solAssert(actualArguments.size() == paramExpectedTypes.size(), ""); + vector symbArguments; + for (unsigned i = 0; i < paramExpectedTypes.size(); ++i) + symbArguments.push_back(expr(*actualArguments[i], paramExpectedTypes[i])); + + TypePointer type = var->type(); + if ( + type->isValueType() || + (type->category() == Type::Category::Array && dynamic_cast(*type).isByteArray()) + ) + { + solAssert(symbArguments.empty(), ""); + defineExpr(_funCall, currentValue(*var)); + return; + } + switch (type->category()) + { + case Type::Category::Array: + case Type::Category::Mapping: + { + // For nested arrays/mappings, each argument in the call is an index to the next layer. + // We mirror this with `select` after unpacking the SMT-LIB array expression. + smtutil::Expression exprVal = currentValue(*var); + for (auto const& arg: symbArguments) + { + exprVal = smtutil::Expression::select( + smtutil::Expression::tuple_get(exprVal, 0), + arg + ); + } + defineExpr(_funCall, exprVal); + break; + } + case Type::Category::Struct: + { + auto returnedMembers = structGetterReturnedMembers(dynamic_cast(*type)); + solAssert(!returnedMembers.empty(), ""); + auto structVar = dynamic_pointer_cast(m_context.variable(*var)); + solAssert(structVar, ""); + auto returnedValues = applyMap(returnedMembers, [&](string const& memberName) { return structVar->member(memberName); }); + if (returnedValues.size() == 1) + defineExpr(_funCall, returnedValues.front()); + else + { + auto symbTuple = dynamic_pointer_cast(m_context.expression(_funCall)); + solAssert(symbTuple, ""); + symbTuple->increaseIndex(); // Increasing the index explicitly since we cannot use defineExpr in this case. + auto const& symbComponents = symbTuple->components(); + solAssert(symbComponents.size() == returnedValues.size(), ""); + for (unsigned i = 0; i < symbComponents.size(); ++i) + m_context.addAssertion(symbTuple->component(i) == returnedValues.at(i)); + } + break; + } + default: {} // Unsupported cases, do nothing and the getter will be abstracted. + } +} + void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) { - solAssert(_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + solAssert(*_funCall.annotation().kind == FunctionCallKind::TypeConversion, ""); solAssert(_funCall.arguments().size() == 1, ""); + auto argument = _funCall.arguments().front(); - unsigned argSize = argument->annotation().type->storageBytes(); - unsigned castSize = _funCall.annotation().type->storageBytes(); + auto const argType = argument->annotation().type; + auto const funCallType = _funCall.annotation().type; + + auto symbArg = expr(*argument, funCallType); + + if (smt::isStringLiteral(*argType) && smt::isFixedBytes(*funCallType)) + { + defineExpr(_funCall, symbArg); + return; + } + + // TODO Simplify this whole thing for 0.8.0 where weird casts are disallowed. + + unsigned argSize = argType->storageBytes(); + unsigned castSize = funCallType->storageBytes(); + bool castIsSigned = smt::isNumber(*funCallType) && smt::isSigned(funCallType); + bool argIsSigned = smt::isNumber(*argType) && smt::isSigned(argType); + optional symbMin; + optional symbMax; + if (smt::isNumber(*funCallType)) + { + symbMin = smt::minValue(funCallType); + symbMax = smt::maxValue(funCallType); + } if (argSize == castSize) - defineExpr(_funCall, expr(*argument)); - else { - m_context.setUnknownValue(*m_context.expression(_funCall)); - auto const& funCallCategory = _funCall.annotation().type->category(); - // TODO: truncating and bytesX needs a different approach because of right padding. - if (funCallCategory == Type::Category::Integer || funCallCategory == Type::Category::Address) + // If sizes are the same, it's possible that the signs are different. + if (smt::isNumber(*funCallType) && smt::isNumber(*argType)) { - if (argSize < castSize) - defineExpr(_funCall, expr(*argument)); + // castIsSigned && !argIsSigned => might overflow if arg > castType.max + // !castIsSigned && argIsSigned => might underflow if arg < castType.min + // !castIsSigned && !argIsSigned => ok + // castIsSigned && argIsSigned => ok + + if (castIsSigned && !argIsSigned) + { + auto wrap = smtutil::Expression::ite( + symbArg > *symbMax, + symbArg - (*symbMax - *symbMin + 1), + symbArg + ); + defineExpr(_funCall, wrap); + } + else if (!castIsSigned && argIsSigned) + { + auto wrap = smtutil::Expression::ite( + symbArg < *symbMin, + symbArg + (*symbMax + 1), + symbArg + ); + defineExpr(_funCall, wrap); + } else + defineExpr(_funCall, symbArg); + } + else + defineExpr(_funCall, symbArg); + } + else if (castSize > argSize) + { + solAssert(smt::isNumber(*funCallType), ""); + // RationalNumbers have size 32. + solAssert(argType->category() != Type::Category::RationalNumber, ""); + + // castIsSigned && !argIsSigned => ok + // castIsSigned && argIsSigned => ok + // !castIsSigned && !argIsSigned => ok except for FixedBytesType, need to adjust padding + // !castIsSigned && argIsSigned => might underflow if arg < castType.min + + if (!castIsSigned && argIsSigned) + { + auto wrap = smtutil::Expression::ite( + symbArg < *symbMin, + symbArg + (*symbMax + 1), + symbArg + ); + defineExpr(_funCall, wrap); + } + else if (!castIsSigned && !argIsSigned) + { + if (auto const* fixedCast = dynamic_cast(funCallType)) { - auto const& intType = dynamic_cast(*m_context.expression(_funCall)->type()); - defineExpr(_funCall, smtutil::Expression::ite( - expr(*argument) >= smt::minValue(intType) && expr(*argument) <= smt::maxValue(intType), - expr(*argument), - expr(_funCall) - )); + auto const* fixedArg = dynamic_cast(argType); + solAssert(fixedArg, ""); + auto diff = fixedCast->numBytes() - fixedArg->numBytes(); + solAssert(diff > 0, ""); + auto bvSize = fixedCast->numBytes() * 8; + defineExpr( + _funCall, + smtutil::Expression::bv2int(smtutil::Expression::int2bv(symbArg, bvSize) << smtutil::Expression::int2bv(diff * 8, bvSize)) + ); } + else + defineExpr(_funCall, symbArg); } + else + defineExpr(_funCall, symbArg); + } + else // castSize < argSize + { + solAssert(smt::isNumber(*funCallType), ""); - m_errorReporter.warning( - 5084_error, - _funCall.location(), - "Type conversion is not yet fully supported and might yield false positives." - ); + auto const* fixedCast = dynamic_cast(funCallType); + auto const* fixedArg = dynamic_cast(argType); + if (fixedCast && fixedArg) + { + createExpr(_funCall); + auto diff = argSize - castSize; + solAssert(fixedArg->numBytes() - fixedCast->numBytes() == diff, ""); + + auto argValueBV = smtutil::Expression::int2bv(symbArg, argSize * 8); + auto shr = smtutil::Expression::int2bv(diff * 8, argSize * 8); + solAssert(!castIsSigned, ""); + defineExpr(_funCall, smtutil::Expression::bv2int(argValueBV >> shr)); + } + else + { + auto argValueBV = smtutil::Expression::int2bv(symbArg, castSize * 8); + defineExpr(_funCall, smtutil::Expression::bv2int(argValueBV, castIsSigned)); + } } } @@ -804,26 +1145,50 @@ void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier) } } +void SMTEncoder::visitStructConstructorCall(FunctionCall const& _funCall) +{ + solAssert(*_funCall.annotation().kind == FunctionCallKind::StructConstructorCall, ""); + if (smt::isNonRecursiveStruct(*_funCall.annotation().type)) + { + auto& structSymbolicVar = dynamic_cast(*m_context.expression(_funCall)); + structSymbolicVar.assignAllMembers(applyMap(_funCall.sortedArguments(), [this](auto const& arg) { return expr(*arg); })); + } + +} + void SMTEncoder::endVisit(Literal const& _literal) { solAssert(_literal.annotation().type, "Expected type for AST node"); Type const& type = *_literal.annotation().type; - if (smt::isNumber(type.category())) + if (smt::isNumber(type)) defineExpr(_literal, smtutil::Expression(type.literalValue(&_literal))); - else if (smt::isBool(type.category())) + else if (smt::isBool(type)) defineExpr(_literal, smtutil::Expression(_literal.token() == Token::TrueLiteral ? true : false)); - else if (smt::isStringLiteral(type.category())) - createExpr(_literal); - else + else if (smt::isStringLiteral(type)) { - m_errorReporter.warning( - 7885_error, - _literal.location(), - "Assertion checker does not yet support the type of this literal (" + - _literal.annotation().type->toString() + - ")." + createExpr(_literal); + + // Add constraints for the length and values as it is known. + auto symbArray = dynamic_pointer_cast(m_context.expression(_literal)); + solAssert(symbArray, ""); + + addArrayLiteralAssertions( + *symbArray, + applyMap(_literal.value(), [](unsigned char c) { return smtutil::Expression{size_t(c)}; }) ); } + else + solAssert(false, ""); +} + +void SMTEncoder::addArrayLiteralAssertions( + smt::SymbolicArrayVariable& _symArray, + vector const& _elementValues +) +{ + m_context.addAssertion(_symArray.length() == _elementValues.size()); + for (size_t i = 0; i < _elementValues.size(); i++) + m_context.addAssertion(smtutil::Expression::select(_symArray.elements(), i) == _elementValues[i]); } void SMTEncoder::endVisit(Return const& _return) @@ -843,10 +1208,10 @@ void SMTEncoder::endVisit(Return const& _return) solAssert(types.size() == returnParams.size(), ""); for (unsigned i = 0; i < returnParams.size(); ++i) - m_context.addAssertion(symbTuple->component(i, types.at(i), returnParams.at(i)->type()) == m_context.newValue(*returnParams.at(i))); + assignment(*returnParams.at(i), symbTuple->component(i, types.at(i), returnParams.at(i)->type())); } else if (returnParams.size() == 1) - m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front())); + assignment(*returnParams.front(), expr(*_return.expression(), returnParams.front()->type())); } } @@ -860,30 +1225,67 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) auto const& exprType = _memberAccess.expression().annotation().type; solAssert(exprType, ""); - auto identifier = dynamic_cast(&_memberAccess.expression()); if (exprType->category() == Type::Category::Magic) { - string accessedName; - if (identifier) - accessedName = identifier->name(); + if (auto const* identifier = dynamic_cast(&_memberAccess.expression())) + { + auto const& name = identifier->name(); + solAssert(name == "block" || name == "msg" || name == "tx", ""); + defineExpr(_memberAccess, m_context.state().txMember(name + "." + _memberAccess.memberName())); + } + else if (auto magicType = dynamic_cast(exprType); magicType->kind() == MagicType::Kind::MetaType) + { + auto const& memberName = _memberAccess.memberName(); + if (memberName == "min" || memberName == "max") + { + IntegerType const& integerType = dynamic_cast(*magicType->typeArgument()); + defineExpr(_memberAccess, memberName == "min" ? integerType.minValue() : integerType.maxValue()); + } + else if (memberName == "interfaceId") + { + ContractDefinition const& contract = dynamic_cast(*magicType->typeArgument()).contractDefinition(); + defineExpr(_memberAccess, contract.interfaceId()); + } + else + // NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not + // at all usable in the SMT checker currently + m_errorReporter.warning( + 7507_error, + _memberAccess.location(), + "Assertion checker does not yet support this expression." + ); + } else - m_errorReporter.warning( - 9551_error, - _memberAccess.location(), - "Assertion checker does not yet support this expression." - ); - defineGlobalVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess); + solUnimplementedAssert(false, ""); + + return false; + } + else if (smt::isNonRecursiveStruct(*exprType)) + { + _memberAccess.expression().accept(*this); + auto const& symbStruct = dynamic_pointer_cast(m_context.expression(_memberAccess.expression())); + defineExpr(_memberAccess, symbStruct->member(_memberAccess.memberName())); return false; } else if (exprType->category() == Type::Category::TypeType) { - if (identifier && dynamic_cast(identifier->annotation().referencedDeclaration)) + auto const* decl = expressionToDeclaration(_memberAccess.expression()); + if (dynamic_cast(decl)) { auto enumType = dynamic_cast(accessType); solAssert(enumType, ""); defineExpr(_memberAccess, enumType->memberValue(_memberAccess.memberName())); + + return false; + } + else if (dynamic_cast(decl)) + { + if (auto const* var = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + defineExpr(_memberAccess, currentValue(*var)); + return false; + } } - return false; } else if (exprType->category() == Type::Category::Address) { @@ -913,6 +1315,16 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) } return false; } + else if ( + auto const* functionType = dynamic_cast(exprType); + functionType && + _memberAccess.memberName() == "selector" && + functionType->hasDeclaration() + ) + { + defineExpr(_memberAccess, functionType->externalIdentifier()); + return false; + } else m_errorReporter.warning( 7650_error, @@ -929,44 +1341,58 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) if (_indexAccess.annotation().type->category() == Type::Category::TypeType) return; - - shared_ptr array; - if (auto const* id = dynamic_cast(&_indexAccess.baseExpression())) + if (auto const* type = dynamic_cast(_indexAccess.baseExpression().annotation().type)) { - auto varDecl = identifierToVariable(*id); - solAssert(varDecl, ""); - array = m_context.variable(*varDecl); + smtutil::Expression base = expr(_indexAccess.baseExpression()); - if (varDecl->type()->category() == Type::Category::FixedBytes) + if (type->numBytes() == 1) + defineExpr(_indexAccess, base); + else { - m_errorReporter.warning( - 7989_error, - _indexAccess.location(), - "Assertion checker does not yet support index accessing fixed bytes." + auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_indexAccess.baseExpression().annotation().type); + solAssert(!isSigned, ""); + solAssert(bvSize >= 16, ""); + solAssert(bvSize % 8 == 0, ""); + + smtutil::Expression idx = expr(*_indexAccess.indexExpression()); + + auto bvBase = smtutil::Expression::int2bv(base, bvSize); + auto bvShl = smtutil::Expression::int2bv(idx * 8, bvSize); + auto bvShr = smtutil::Expression::int2bv(bvSize - 8, bvSize); + auto result = (bvBase << bvShl) >> bvShr; + + auto anyValue = expr(_indexAccess); + m_context.expression(_indexAccess)->increaseIndex(); + unsigned numBytes = bvSize / 8; + auto withBound = smtutil::Expression::ite( + idx < numBytes, + smtutil::Expression::bv2int(result, false), + anyValue ); - return; + defineExpr(_indexAccess, withBound); } + return; } - else if (auto const* innerAccess = dynamic_cast(&_indexAccess.baseExpression())) + + shared_ptr array; + if (auto const* id = dynamic_cast(&_indexAccess.baseExpression())) { - solAssert(m_context.knownExpression(*innerAccess), ""); - array = m_context.expression(*innerAccess); + auto varDecl = identifierToVariable(*id); + solAssert(varDecl, ""); + array = m_context.variable(*varDecl); } else { - m_errorReporter.warning( - 9118_error, - _indexAccess.location(), - "Assertion checker does not yet implement this expression." - ); - return; + solAssert(m_context.knownExpression(_indexAccess.baseExpression()), ""); + array = m_context.expression(_indexAccess.baseExpression()); } auto arrayVar = dynamic_pointer_cast(array); solAssert(arrayVar, ""); + TypePointer baseType = _indexAccess.baseExpression().annotation().type; defineExpr(_indexAccess, smtutil::Expression::select( arrayVar->elements(), - expr(*_indexAccess.indexExpression()) + expr(*_indexAccess.indexExpression(), keyType(baseType)) )); setSymbolicUnknownValue( expr(_indexAccess), @@ -979,11 +1405,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess) void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess) { createExpr(_indexRangeAccess); - m_errorReporter.warning( - 2923_error, - _indexRangeAccess.location(), - "Assertion checker does not yet implement this expression." - ); + /// The actual slice is created by CHC which also assigns the length. } void SMTEncoder::arrayAssignment() @@ -991,58 +1413,96 @@ void SMTEncoder::arrayAssignment() m_arrayAssignmentHappened = true; } -void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smtutil::Expression const& _rightHandSide) +void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expression const& _rightHandSide) { + if (auto const* memberAccess = dynamic_cast(&_expr)) + { + if (dynamic_cast(expressionToDeclaration(memberAccess->expression()))) + { + if (auto const* var = dynamic_cast(memberAccess->annotation().referencedDeclaration)) + { + if (var->hasReferenceOrMappingType()) + resetReferences(*var); + + assignment(*var, _rightHandSide); + defineExpr(_expr, currentValue(*var)); + return; + } + } + } + auto toStore = _rightHandSide; - auto indexAccess = dynamic_cast(&_expr); - solAssert(indexAccess, ""); + auto const* lastExpr = &_expr; while (true) { - if (auto const& id = dynamic_cast(&indexAccess->baseExpression())) + if (auto const* indexAccess = dynamic_cast(lastExpr)) { - auto varDecl = identifierToVariable(*id); - solAssert(varDecl, ""); - - if (varDecl->hasReferenceOrMappingType()) - resetReferences(*varDecl); + auto const& base = indexAccess->baseExpression(); + if (dynamic_cast(&base)) + base.accept(*this); - auto symbArray = dynamic_pointer_cast(m_context.variable(*varDecl)); - smtutil::Expression store = smtutil::Expression::store( - symbArray->elements(), - expr(*indexAccess->indexExpression()), - toStore + TypePointer baseType = base.annotation().type; + auto indexExpr = expr(*indexAccess->indexExpression(), keyType(baseType)); + auto symbArray = dynamic_pointer_cast(m_context.expression(base)); + solAssert(symbArray, ""); + toStore = smtutil::Expression::tuple_constructor( + smtutil::Expression(make_shared(smt::smtSort(*baseType)), baseType->toString(true)), + {smtutil::Expression::store(symbArray->elements(), indexExpr, toStore), symbArray->length()} ); - auto oldLength = symbArray->length(); - symbArray->increaseIndex(); - m_context.addAssertion(symbArray->elements() == store); - m_context.addAssertion(symbArray->length() == oldLength); - // Update the SMT select value after the assignment, - // necessary for sound models. defineExpr(*indexAccess, smtutil::Expression::select( symbArray->elements(), - expr(*indexAccess->indexExpression()) + indexExpr )); + lastExpr = &indexAccess->baseExpression(); + } + else if (auto const* memberAccess = dynamic_cast(lastExpr)) + { + auto const& base = memberAccess->expression(); + if (dynamic_cast(&base)) + base.accept(*this); + + if ( + auto const* structType = dynamic_cast(base.annotation().type); + structType && structType->recursive() + ) + { + m_errorReporter.warning( + 4375_error, + memberAccess->location(), + "Assertion checker does not support recursive structs." + ); + return; + } - break; + auto symbStruct = dynamic_pointer_cast(m_context.expression(base)); + solAssert(symbStruct, ""); + symbStruct->assignMember(memberAccess->memberName(), toStore); + toStore = symbStruct->currentValue(); + defineExpr(*memberAccess, symbStruct->member(memberAccess->memberName())); + lastExpr = &memberAccess->expression(); } - else if (auto base = dynamic_cast(&indexAccess->baseExpression())) + else if (auto const& id = dynamic_cast(lastExpr)) { - auto symbArray = dynamic_pointer_cast(m_context.expression(*base)); - solAssert(symbArray, ""); - auto baseType = base->annotation().type; - toStore = smtutil::Expression::tuple_constructor( - smtutil::Expression(make_shared(smt::smtSort(*baseType)), baseType->toString(true)), - {smtutil::Expression::store(symbArray->elements(), expr(*indexAccess->indexExpression()), toStore), symbArray->length()} - ); - indexAccess = base; + auto varDecl = identifierToVariable(*id); + solAssert(varDecl, ""); + + if (varDecl->hasReferenceOrMappingType()) + resetReferences(*varDecl); + + assignment(*varDecl, toStore); + defineExpr(*id, currentValue(*varDecl)); + break; } else { - m_errorReporter.warning( - 9056_error, - _expr.location(), - "Assertion checker does not yet implement this expression." - ); + auto type = lastExpr->annotation().type; + if ( + dynamic_cast(type) || + dynamic_cast(type) + ) + resetReferences(type); + + assignment(*m_context.expression(*lastExpr), toStore); break; } } @@ -1090,25 +1550,22 @@ void SMTEncoder::arrayPop(FunctionCall const& _funCall) auto oldElements = symbArray->elements(); auto oldLength = symbArray->length(); - m_context.addAssertion(oldLength > 0); symbArray->increaseIndex(); m_context.addAssertion(symbArray->elements() == oldElements); auto newLength = smtutil::Expression::ite( - oldLength == 0, - smt::maxValue(*TypeProvider::uint256()), - oldLength - 1 + oldLength > 0, + oldLength - 1, + 0 ); - m_context.addAssertion(symbArray->length() == oldLength - 1); + m_context.addAssertion(symbArray->length() == newLength); arrayPushPopAssign(memberAccess->expression(), symbArray->currentValue()); } void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression const& _array) { - Expression const* expr = &_expr; - if (auto const* tupleExpr = dynamic_cast(expr)) - expr = innermostTuple(*tupleExpr); + Expression const* expr = innermostTuple(_expr); if (auto const* id = dynamic_cast(expr)) { @@ -1117,12 +1574,19 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression if (varDecl->hasReferenceOrMappingType()) resetReferences(*varDecl); m_context.addAssertion(m_context.newValue(*varDecl) == _array); - } - else if (auto const* indexAccess = dynamic_cast(expr)) - arrayIndexAssignment(*indexAccess, _array); + m_context.expression(*id)->increaseIndex(); + defineExpr(*id,currentValue(*varDecl)); + } + else if ( + dynamic_cast(expr) || + dynamic_cast(expr) + ) + indexOrMemberAssignment(_expr, _array); else if (auto const* funCall = dynamic_cast(expr)) { FunctionType const& funType = dynamic_cast(*funCall->expression().annotation().type); + // Push cannot occur on an expression that is itself a ByteArrayPush, i.e., bytes.push().push() is not possible. + solAssert(funType.kind() != FunctionType::Kind::ByteArrayPush, ""); if (funType.kind() == FunctionType::Kind::ArrayPush) { auto memberAccess = dynamic_cast(&funCall->expression()); @@ -1142,12 +1606,6 @@ void SMTEncoder::arrayPushPopAssign(Expression const& _expr, smtutil::Expression arrayPushPopAssign(memberAccess->expression(), symbArray->currentValue()); } } - else if (dynamic_cast(expr)) - m_errorReporter.warning( - 9599_error, - _expr.location(), - "Assertion checker does not yet implement this expression." - ); else solAssert(false, ""); } @@ -1168,63 +1626,53 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex m_context.globalSymbol(_name)->increaseIndex(); // The default behavior is not to increase the index since // most of the global values stay the same throughout a tx. - if (smt::isSupportedType(_expr.annotation().type->category())) + if (smt::isSupportedType(*_expr.annotation().type)) defineExpr(_expr, m_context.globalSymbol(_name)->currentValue()); } bool SMTEncoder::shortcutRationalNumber(Expression const& _expr) { - if (_expr.annotation().type->category() == Type::Category::RationalNumber) - { - auto rationalType = dynamic_cast(_expr.annotation().type); - solAssert(rationalType, ""); - if (rationalType->isNegative()) - defineExpr(_expr, smtutil::Expression(u2s(rationalType->literalValue(nullptr)))); - else - defineExpr(_expr, smtutil::Expression(rationalType->literalValue(nullptr))); - return true; - } - return false; + RationalNumberType const* rationalType = isConstant(_expr); + if (!rationalType) + return false; + + if (rationalType->isNegative()) + defineExpr(_expr, smtutil::Expression(u2s(rationalType->literalValue(nullptr)))); + else + defineExpr(_expr, smtutil::Expression(rationalType->literalValue(nullptr))); + return true; } void SMTEncoder::arithmeticOperation(BinaryOperation const& _op) { auto type = _op.annotation().commonType; solAssert(type, ""); - if (type->category() == Type::Category::Integer || type->category() == Type::Category::FixedPoint) + solAssert(type->category() == Type::Category::Integer || type->category() == Type::Category::FixedPoint, ""); + switch (_op.getOperator()) { - switch (_op.getOperator()) - { - case Token::Add: - case Token::Sub: - case Token::Mul: - case Token::Div: - case Token::Mod: - { - auto values = arithmeticOperation( - _op.getOperator(), - expr(_op.leftExpression()), - expr(_op.rightExpression()), - _op.annotation().commonType, - _op - ); - defineExpr(_op, values.first); - break; - } - default: - m_errorReporter.warning( - 5188_error, - _op.location(), - "Assertion checker does not yet implement this operator." - ); - } + case Token::Add: + case Token::Sub: + case Token::Mul: + case Token::Div: + case Token::Mod: + { + auto values = arithmeticOperation( + _op.getOperator(), + expr(_op.leftExpression()), + expr(_op.rightExpression()), + _op.annotation().commonType, + _op + ); + defineExpr(_op, values.first); + break; } - else + default: m_errorReporter.warning( - 9011_error, + 5188_error, _op.location(), - "Assertion checker does not yet implement this operator for type " + type->richIdentifier() + "." + "Assertion checker does not yet implement this operator." ); + } } pair SMTEncoder::arithmeticOperation( @@ -1261,16 +1709,14 @@ pair SMTEncoder::arithmeticOperation( case Token::Add: return _left + _right; case Token::Sub: return _left - _right; case Token::Mul: return _left * _right; - case Token::Div: return division(_left, _right, *intType); - case Token::Mod: return _left % _right; + case Token::Div: return divModWithSlacks(_left, _right, *intType).first; + case Token::Mod: return divModWithSlacks(_left, _right, *intType).second; default: solAssert(false, ""); } }(); if (_op == Token::Div || _op == Token::Mod) { - m_context.addAssertion(_right != 0); - // mod and unsigned division never underflow/overflow if (_op == Token::Mod || !intType->isSigned()) return {valueUnbounded, valueUnbounded}; @@ -1317,17 +1763,71 @@ pair SMTEncoder::arithmeticOperation( return {value, valueUnbounded}; } +smtutil::Expression SMTEncoder::bitwiseOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType +) +{ + static set validOperators{ + Token::BitAnd, + Token::BitOr, + Token::BitXor, + Token::SHL, + Token::SHR, + Token::SAR + }; + solAssert(validOperators.count(_op), ""); + solAssert(_commonType, ""); + + auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_commonType); + + auto bvLeft = smtutil::Expression::int2bv(_left, bvSize); + auto bvRight = smtutil::Expression::int2bv(_right, bvSize); + + optional result; + switch (_op) + { + case Token::BitAnd: + result = bvLeft & bvRight; + break; + case Token::BitOr: + result = bvLeft | bvRight; + break; + case Token::BitXor: + result = bvLeft ^ bvRight; + break; + case Token::SHL: + result = bvLeft << bvRight; + break; + case Token::SHR: + result = bvLeft >> bvRight; + break; + case Token::SAR: + result = isSigned ? + smtutil::Expression::ashr(bvLeft, bvRight) : + bvLeft >> bvRight; + break; + default: + solAssert(false, ""); + } + + solAssert(result.has_value(), ""); + return smtutil::Expression::bv2int(*result, isSigned); +} + void SMTEncoder::compareOperation(BinaryOperation const& _op) { auto const& commonType = _op.annotation().commonType; solAssert(commonType, ""); - if (smt::isSupportedType(commonType->category())) + if (smt::isSupportedType(*commonType)) { smtutil::Expression left(expr(_op.leftExpression(), commonType)); smtutil::Expression right(expr(_op.rightExpression(), commonType)); Token op = _op.getOperator(); shared_ptr value; - if (smt::isNumber(commonType->category())) + if (smt::isNumber(*commonType)) { value = make_shared( op == Token::Equal ? (left == right) : @@ -1340,7 +1840,7 @@ void SMTEncoder::compareOperation(BinaryOperation const& _op) } else // Bool { - solUnimplementedAssert(smt::isBool(commonType->category()), "Operation not yet supported"); + solUnimplementedAssert(smt::isBool(*commonType), "Operation not yet supported"); value = make_shared( op == Token::Equal ? (left == right) : /*op == Token::NotEqual*/ (left != right) @@ -1361,88 +1861,82 @@ void SMTEncoder::booleanOperation(BinaryOperation const& _op) { solAssert(_op.getOperator() == Token::And || _op.getOperator() == Token::Or, ""); solAssert(_op.annotation().commonType, ""); - if (_op.annotation().commonType->category() == Type::Category::Bool) + solAssert(_op.annotation().commonType->category() == Type::Category::Bool, ""); + // @TODO check that both of them are not constant + _op.leftExpression().accept(*this); + if (_op.getOperator() == Token::And) { - // @TODO check that both of them are not constant - _op.leftExpression().accept(*this); - if (_op.getOperator() == Token::And) - { - auto indicesAfterSecond = visitBranch(&_op.rightExpression(), expr(_op.leftExpression())); - mergeVariables(touchedVariables(_op.rightExpression()), !expr(_op.leftExpression()), copyVariableIndices(), indicesAfterSecond); - defineExpr(_op, expr(_op.leftExpression()) && expr(_op.rightExpression())); - } - else - { - auto indicesAfterSecond = visitBranch(&_op.rightExpression(), !expr(_op.leftExpression())); - mergeVariables(touchedVariables(_op.rightExpression()), expr(_op.leftExpression()), copyVariableIndices(), indicesAfterSecond); - defineExpr(_op, expr(_op.leftExpression()) || expr(_op.rightExpression())); - } + auto indicesAfterSecond = visitBranch(&_op.rightExpression(), expr(_op.leftExpression())).first; + mergeVariables(touchedVariables(_op.rightExpression()), !expr(_op.leftExpression()), copyVariableIndices(), indicesAfterSecond); + defineExpr(_op, expr(_op.leftExpression()) && expr(_op.rightExpression())); } else - m_errorReporter.warning( - 3263_error, - _op.location(), - "Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations" - ); + { + auto indicesAfterSecond = visitBranch(&_op.rightExpression(), !expr(_op.leftExpression())).first; + mergeVariables(touchedVariables(_op.rightExpression()), expr(_op.leftExpression()), copyVariableIndices(), indicesAfterSecond); + defineExpr(_op, expr(_op.leftExpression()) || expr(_op.rightExpression())); + } } void SMTEncoder::bitwiseOperation(BinaryOperation const& _op) { - solAssert(TokenTraits::isBitOp(_op.getOperator()), ""); + auto op = _op.getOperator(); + solAssert(TokenTraits::isBitOp(op) || TokenTraits::isShiftOp(op), ""); auto commonType = _op.annotation().commonType; solAssert(commonType, ""); - unsigned bvSize = 256; - bool isSigned = false; - if (auto const* intType = dynamic_cast(commonType)) - { - bvSize = intType->numBits(); - isSigned = intType->isSigned(); - } - else if (auto const* fixedType = dynamic_cast(commonType)) - { - bvSize = fixedType->numBits(); - isSigned = fixedType->isSigned(); - } - else if (auto const* fixedBytesType = dynamic_cast(commonType)) - bvSize = fixedBytesType->numBytes() * 8; + defineExpr(_op, bitwiseOperation( + _op.getOperator(), + expr(_op.leftExpression(), commonType), + expr(_op.rightExpression(), commonType), + commonType + )); +} - auto bvLeft = smtutil::Expression::int2bv(expr(_op.leftExpression(), commonType), bvSize); - auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize); +void SMTEncoder::bitwiseNotOperation(UnaryOperation const& _op) +{ + solAssert(_op.getOperator() == Token::BitNot, ""); - optional result; - if (_op.getOperator() == Token::BitAnd) - result = bvLeft & bvRight; - // TODO implement the other operators - else - m_errorReporter.warning( - 1093_error, - _op.location(), - "Assertion checker does not yet implement this bitwise operator." - ); + auto [bvSize, isSigned] = smt::typeBvSizeAndSignedness(_op.annotation().type); - if (result) - defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned)); + auto bvOperand = smtutil::Expression::int2bv(expr(_op.subExpression(), _op.annotation().type), bvSize); + defineExpr(_op, smtutil::Expression::bv2int(~bvOperand, isSigned)); } -smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Expression _right, IntegerType const& _type) +pair SMTEncoder::divModWithSlacks( + smtutil::Expression _left, + smtutil::Expression _right, + IntegerType const& _type +) { - // Signed division in SMTLIB2 rounds differently for negative division. + IntegerType const* intType = &_type; + string suffix = "div_mod_" + to_string(m_context.newUniqueId()); + smt::SymbolicIntVariable dSymb(intType, intType, "d_" + suffix, m_context); + smt::SymbolicIntVariable rSymb(intType, intType, "r_" + suffix, m_context); + auto d = dSymb.currentValue(); + auto r = rSymb.currentValue(); + + // x / y = d and x % y = r iff d * y + r = x and + // either x >= 0 and 0 <= r < abs(y) (or just 0 <= r < y for unsigned) + // or x < 0 and -abs(y) < r <= 0 + m_context.addAssertion(((d * _right) + r) == _left); if (_type.isSigned()) - return (smtutil::Expression::ite( - _left >= 0, - smtutil::Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))), - smtutil::Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right)) - )); - else - return _left / _right; + m_context.addAssertion( + (_left >= 0 && 0 <= r && (_right == 0 || r < smtutil::abs(_right))) || + (_left < 0 && ((_right == 0 || 0 - smtutil::abs(_right) < r) && r <= 0)) + ); + else // unsigned version + m_context.addAssertion(0 <= r && (_right == 0 || r < _right)); + + auto divResult = smtutil::Expression::ite(_right == 0, 0, d); + auto modResult = smtutil::Expression::ite(_right == 0, 0, r); + return {divResult, modResult}; } void SMTEncoder::assignment( Expression const& _left, smtutil::Expression const& _right, - TypePointer const& _type, - langutil::SourceLocation const& _location + TypePointer const& _type ) { solAssert( @@ -1450,45 +1944,37 @@ void SMTEncoder::assignment( "Tuple assignments should be handled by tupleAssignment." ); - Expression const* left = &_left; - if (auto const* tuple = dynamic_cast(left)) - left = innermostTuple(*tuple); + Expression const* left = innermostTuple(_left); - if (!smt::isSupportedType(_type->category())) + if (!smt::isSupportedType(*_type)) { // Give it a new index anyway to keep the SSA scheme sound. if (auto varDecl = identifierToVariable(*left)) m_context.newValue(*varDecl); - - m_errorReporter.warning( - 6191_error, - _location, - "Assertion checker does not yet implement type " + _type->toString() - ); } else if (auto varDecl = identifierToVariable(*left)) assignment(*varDecl, _right); - else if (dynamic_cast(left)) - arrayIndexAssignment(*left, _right); + else if ( + dynamic_cast(left) || + dynamic_cast(left) + ) + indexOrMemberAssignment(*left, _right); else - m_errorReporter.warning( - 8182_error, - _location, - "Assertion checker does not yet implement such assignments." - ); + solAssert(false, ""); } void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _right) { - auto lTuple = dynamic_cast(innermostTuple(dynamic_cast(_left))); + auto lTuple = dynamic_cast(innermostTuple(_left)); solAssert(lTuple, ""); + Expression const* right = innermostTuple(_right); auto const& lComponents = lTuple->components(); // If both sides are tuple expressions, we individually and potentially // recursively assign each pair of components. // This is because of potential type conversion. - if (auto rTuple = dynamic_cast(&_right)) + if (auto rTuple = dynamic_cast(right)) { auto const& rComponents = rTuple->components(); solAssert(lComponents.size() == rComponents.size(), ""); @@ -1503,24 +1989,24 @@ void SMTEncoder::tupleAssignment(Expression const& _left, Expression const& _rig else { auto type = lExpr.annotation().type; - assignment(lExpr, expr(rExpr, type), type, lExpr.location()); + assignment(lExpr, expr(rExpr, type), type); } } } else { - auto rType = dynamic_cast(_right.annotation().type); + auto rType = dynamic_cast(right->annotation().type); solAssert(rType, ""); auto const& rComponents = rType->components(); solAssert(lComponents.size() == rComponents.size(), ""); - auto symbRight = expr(_right); + auto symbRight = expr(*right); solAssert(symbRight.sort->kind == smtutil::Kind::Tuple, ""); for (unsigned i = 0; i < lComponents.size(); ++i) if (auto component = lComponents.at(i); component && rComponents.at(i)) - assignment(*component, smtutil::Expression::tuple_get(symbRight, i), component->annotation().type, component->location()); + assignment(*component, smtutil::Expression::tuple_get(symbRight, i), component->annotation().type); } } @@ -1533,13 +2019,31 @@ smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment {Token::AssignDiv, Token::Div}, {Token::AssignMod, Token::Mod} }; + static map const compoundToBitwise{ + {Token::AssignBitAnd, Token::BitAnd}, + {Token::AssignBitOr, Token::BitOr}, + {Token::AssignBitXor, Token::BitXor}, + {Token::AssignShl, Token::SHL}, + {Token::AssignShr, Token::SHR}, + {Token::AssignSar, Token::SAR} + }; Token op = _assignment.assignmentOperator(); - solAssert(compoundToArithmetic.count(op), ""); + solAssert(compoundToArithmetic.count(op) || compoundToBitwise.count(op), ""); + auto decl = identifierToVariable(_assignment.leftHandSide()); + + if (compoundToBitwise.count(op)) + return bitwiseOperation( + compoundToBitwise.at(op), + decl ? currentValue(*decl) : expr(_assignment.leftHandSide(), _assignment.annotation().type), + expr(_assignment.rightHandSide(), _assignment.annotation().type), + _assignment.annotation().type + ); + auto values = arithmeticOperation( compoundToArithmetic.at(op), - decl ? currentValue(*decl) : expr(_assignment.leftHandSide()), - expr(_assignment.rightHandSide()), + decl ? currentValue(*decl) : expr(_assignment.leftHandSide(), _assignment.annotation().type), + expr(_assignment.rightHandSide(), _assignment.annotation().type), _assignment.annotation().type, _assignment ); @@ -1553,9 +2057,7 @@ void SMTEncoder::assignment(VariableDeclaration const& _variable, Expression con // This is a special case where the SMT sorts are different. // For now we are unaware of other cases where this happens, but if they do appear // we should extract this into an `implicitConversion` function. - if (_variable.type()->category() != Type::Category::Array || _value.annotation().type->category() != Type::Category::StringLiteral) - assignment(_variable, expr(_value, _variable.type())); - // TODO else { store each string literal byte into the array } + assignment(_variable, expr(_value, _variable.type())); } void SMTEncoder::assignment(VariableDeclaration const& _variable, smtutil::Expression const& _value) @@ -1563,25 +2065,37 @@ void SMTEncoder::assignment(VariableDeclaration const& _variable, smtutil::Expre TypePointer type = _variable.type(); if (type->category() == Type::Category::Mapping) arrayAssignment(); - m_context.addAssertion(m_context.newValue(_variable) == _value); + assignment(*m_context.variable(_variable), _value); +} + +void SMTEncoder::assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value) +{ + m_context.addAssertion(_symVar.increaseIndex() == _value); } -SMTEncoder::VariableIndices SMTEncoder::visitBranch(ASTNode const* _statement, smtutil::Expression _condition) +pair SMTEncoder::visitBranch( + ASTNode const* _statement, + smtutil::Expression _condition +) { return visitBranch(_statement, &_condition); } -SMTEncoder::VariableIndices SMTEncoder::visitBranch(ASTNode const* _statement, smtutil::Expression const* _condition) +pair SMTEncoder::visitBranch( + ASTNode const* _statement, + smtutil::Expression const* _condition +) { auto indicesBeforeBranch = copyVariableIndices(); if (_condition) pushPathCondition(*_condition); _statement->accept(*this); + auto pathConditionOnExit = currentPathConditions(); if (_condition) popPathCondition(); auto indicesAfterBranch = copyVariableIndices(); resetVariableIndices(indicesBeforeBranch); - return indicesAfterBranch; + return {indicesAfterBranch, pathConditionOnExit}; } void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _function, vector const& _callArgs) @@ -1596,7 +2110,12 @@ void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _fu m_arrayAssignmentHappened = true; } - for (auto const& variable: _function.localVariables()) + vector localVars; + if (auto const* fun = dynamic_cast(&_function)) + localVars = localVariablesIncludingModifiers(*fun); + else + localVars = _function.localVariables(); + for (auto const& variable: localVars) if (createVariable(*variable)) { m_context.newValue(*variable); @@ -1614,7 +2133,7 @@ void SMTEncoder::initializeFunctionCallParameters(CallableDeclaration const& _fu void SMTEncoder::createStateVariables(ContractDefinition const& _contract) { - for (auto var: _contract.stateVariablesIncludingInherited()) + for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract)) createVariable(*var); } @@ -1636,7 +2155,7 @@ void SMTEncoder::initializeStateVariables(ContractDefinition const& _contract) void SMTEncoder::createLocalVariables(FunctionDefinition const& _function) { - for (auto const& variable: _function.localVariables()) + for (auto const& variable: localVariablesIncludingModifiers(_function)) createVariable(*variable); for (auto const& param: _function.parameters()) @@ -1649,7 +2168,7 @@ void SMTEncoder::createLocalVariables(FunctionDefinition const& _function) void SMTEncoder::initializeLocalVariables(FunctionDefinition const& _function) { - for (auto const& variable: _function.localVariables()) + for (auto const& variable: localVariablesIncludingModifiers(_function)) { solAssert(m_context.knownVariable(*variable), ""); m_context.setZeroValue(*variable); @@ -1684,30 +2203,41 @@ void SMTEncoder::resetReferences(VariableDeclaration const& _varDecl) if (_var.isStateVariable() && _varDecl.isStateVariable()) return false; - TypePointer prefix = _var.type(); - TypePointer originalType = typeWithoutPointer(_varDecl.type()); - while ( - prefix->category() == Type::Category::Mapping || - prefix->category() == Type::Category::Array - ) + return sameTypeOrSubtype(_var.type(), _varDecl.type()); + }); +} + +void SMTEncoder::resetReferences(TypePointer _type) +{ + m_context.resetVariables([&](VariableDeclaration const& _var) { + return sameTypeOrSubtype(_var.type(), _type); + }); +} + +bool SMTEncoder::sameTypeOrSubtype(TypePointer _a, TypePointer _b) +{ + TypePointer prefix = _a; + while ( + prefix->category() == Type::Category::Mapping || + prefix->category() == Type::Category::Array + ) + { + if (*typeWithoutPointer(_b) == *typeWithoutPointer(prefix)) + return true; + if (prefix->category() == Type::Category::Mapping) { - if (*originalType == *typeWithoutPointer(prefix)) - return true; - if (prefix->category() == Type::Category::Mapping) - { - auto mapPrefix = dynamic_cast(prefix); - solAssert(mapPrefix, ""); - prefix = mapPrefix->valueType(); - } - else - { - auto arrayPrefix = dynamic_cast(prefix); - solAssert(arrayPrefix, ""); - prefix = arrayPrefix->baseType(); - } + auto mapPrefix = dynamic_cast(prefix); + solAssert(mapPrefix, ""); + prefix = mapPrefix->valueType(); } - return false; - }); + else + { + auto arrayPrefix = dynamic_cast(prefix); + solAssert(arrayPrefix, ""); + prefix = arrayPrefix->baseType(); + } + } + return false; } TypePointer SMTEncoder::typeWithoutPointer(TypePointer const& _type) @@ -1821,6 +2351,14 @@ void SMTEncoder::pushPathCondition(smtutil::Expression const& _e) m_pathConditions.push_back(currentPathConditions() && _e); } +void SMTEncoder::setPathCondition(smtutil::Expression const& _e) +{ + if (m_pathConditions.empty()) + m_pathConditions.push_back(_e); + else + m_pathConditions.back() = _e; +} + smtutil::Expression SMTEncoder::currentPathConditions() { if (m_pathConditions.empty()) @@ -1832,7 +2370,7 @@ SecondarySourceLocation SMTEncoder::callStackMessage(vector cons { SecondarySourceLocation callStackLocation; solAssert(!_callStack.empty(), ""); - callStackLocation.append("Callstack: ", SourceLocation()); + callStackLocation.append("Callstack:", SourceLocation()); for (auto const& call: _callStack | boost::adaptors::reversed) if (call.second) callStackLocation.append("", call.second->location()); @@ -1887,15 +2425,16 @@ void SMTEncoder::resetVariableIndices(VariableIndices const& _indices) void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function) { solAssert(_contract, ""); - for (auto var: _contract->stateVariablesIncludingInherited()) + for (auto var: stateVariablesIncludingInheritedAndPrivate(*_contract)) m_context.variable(*var)->resetIndex(); if (_function) { for (auto const& var: _function->parameters() + _function->returnParameters()) m_context.variable(*var)->resetIndex(); - for (auto const& var: _function->localVariables()) + for (auto const& var: localVariablesIncludingModifiers(*_function)) m_context.variable(*var)->resetIndex(); } + m_context.state().reset(); } Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) @@ -1906,10 +2445,25 @@ Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess) return base; } -Expression const* SMTEncoder::innermostTuple(TupleExpression const& _tuple) +TypePointer SMTEncoder::keyType(TypePointer _type) +{ + if (auto const* mappingType = dynamic_cast(_type)) + return mappingType->keyType(); + if ( + dynamic_cast(_type) || + dynamic_cast(_type) + ) + return TypeProvider::uint256(); + else + solAssert(false, ""); +} + +Expression const* SMTEncoder::innermostTuple(Expression const& _expr) { - solAssert(!_tuple.isInlineArray(), ""); - TupleExpression const* tuple = &_tuple; + auto const* tuple = dynamic_cast(&_expr); + if (!tuple || tuple->isInlineArray()) + return &_expr; + Expression const* expr = tuple; while (tuple && !tuple->isInlineArray() && tuple->components().size() == 1) { @@ -1928,19 +2482,64 @@ set SMTEncoder::touchedVariables(ASTNode const& _nod return m_variableUsage.touchedVariables(_node, callStack); } -VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _expr) +Declaration const* SMTEncoder::expressionToDeclaration(Expression const& _expr) const { - if (auto identifier = dynamic_cast(&_expr)) - { - if (auto decl = dynamic_cast(identifier->annotation().referencedDeclaration)) + if (auto const* identifier = dynamic_cast(&_expr)) + return identifier->annotation().referencedDeclaration; + if (auto const* outerMemberAccess = dynamic_cast(&_expr)) + return outerMemberAccess->annotation().referencedDeclaration; + return nullptr; +} + +VariableDeclaration const* SMTEncoder::identifierToVariable(Expression const& _expr) const +{ + // We do not use `expressionToDeclaration` here because we are not interested in + // struct.field, for example. + if (auto const* identifier = dynamic_cast(&_expr)) + if (auto const* varDecl = dynamic_cast(identifier->annotation().referencedDeclaration)) { - solAssert(m_context.knownVariable(*decl), ""); - return decl; + solAssert(m_context.knownVariable(*varDecl), ""); + return varDecl; } + return nullptr; +} + +MemberAccess const* SMTEncoder::isEmptyPush(Expression const& _expr) const +{ + if ( + auto const* funCall = dynamic_cast(&_expr); + funCall && funCall->arguments().empty() + ) + { + auto const& funType = dynamic_cast(*funCall->expression().annotation().type); + if (funType.kind() == FunctionType::Kind::ArrayPush || funType.kind() == FunctionType::Kind::ByteArrayPush) + return &dynamic_cast(funCall->expression()); } return nullptr; } +bool SMTEncoder::isPublicGetter(Expression const& _expr) { + if (!isTrustedExternalCall(&_expr)) + return false; + auto varDecl = dynamic_cast( + dynamic_cast(_expr).annotation().referencedDeclaration + ); + return varDecl != nullptr; +} + +bool SMTEncoder::isTrustedExternalCall(Expression const* _expr) { + auto memberAccess = dynamic_cast(_expr); + if (!memberAccess) + return false; + + auto identifier = dynamic_cast(&memberAccess->expression()); + return identifier && + identifier->name() == "this" && + identifier->annotation().referencedDeclaration && + dynamic_cast(identifier->annotation().referencedDeclaration) + ; +} + string SMTEncoder::extraComment() { string extra; @@ -1955,16 +2554,16 @@ string SMTEncoder::extraComment() FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall const& _funCall) { - if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + if (*_funCall.annotation().kind != FunctionCallKind::FunctionCall) return nullptr; FunctionDefinition const* funDef = nullptr; Expression const* calledExpr = &_funCall.expression(); - if (TupleExpression const* fun = dynamic_cast(&_funCall.expression())) + if (TupleExpression const* fun = dynamic_cast(calledExpr)) { solAssert(fun->components().size() == 1, ""); - calledExpr = fun->components().front().get(); + calledExpr = innermostTuple(*calledExpr); } if (Identifier const* fun = dynamic_cast(calledExpr)) @@ -1975,6 +2574,110 @@ FunctionDefinition const* SMTEncoder::functionCallToDefinition(FunctionCall cons return funDef; } +vector SMTEncoder::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract) +{ + return fold( + _contract.annotation().linearizedBaseContracts, + vector{}, + [](auto&& _acc, auto _contract) { return _acc + _contract->stateVariables(); } + ); +} + +vector SMTEncoder::stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function) +{ + return stateVariablesIncludingInheritedAndPrivate(dynamic_cast(*_function.scope())); +} + +vector SMTEncoder::localVariablesIncludingModifiers(FunctionDefinition const& _function) +{ + return _function.localVariables() + modifiersVariables(_function); +} + +vector SMTEncoder::modifiersVariables(FunctionDefinition const& _function) +{ + struct BlockVars: ASTConstVisitor + { + BlockVars(Block const& _block) { _block.accept(*this); } + void endVisit(VariableDeclaration const& _var) { vars.push_back(&_var); } + vector vars; + }; + + vector vars; + set visited; + for (auto invok: _function.modifiers()) + { + if (!invok) + continue; + auto decl = invok->name().annotation().referencedDeclaration; + auto const* modifier = dynamic_cast(decl); + if (!modifier || visited.count(modifier)) + continue; + + visited.insert(modifier); + if (modifier->isImplemented()) + { + vars += applyMap(modifier->parameters(), [](auto _var) { return _var.get(); }); + vars += BlockVars(modifier->body()).vars; + } + } + return vars; +} + +SourceUnit const* SMTEncoder::sourceUnitContaining(Scopable const& _scopable) +{ + for (auto const* s = &_scopable; s; s = dynamic_cast(s->scope())) + if (auto const* source = dynamic_cast(s->scope())) + return source; + solAssert(false, ""); +} + +map>> SMTEncoder::baseArguments(ContractDefinition const& _contract) +{ + map>> baseArgs; + + for (auto contract: _contract.annotation().linearizedBaseContracts) + { + /// Collect base contracts and potential constructor arguments. + for (auto specifier: contract->baseContracts()) + { + solAssert(specifier, ""); + auto const& base = dynamic_cast(*specifier->name().annotation().referencedDeclaration); + if (auto args = specifier->arguments()) + baseArgs[&base] = *args; + } + /// Collect base constructor arguments given as constructor modifiers. + if (auto constructor = contract->constructor()) + for (auto mod: constructor->modifiers()) + { + auto decl = mod->name().annotation().referencedDeclaration; + if (auto base = dynamic_cast(decl)) + { + solAssert(!baseArgs.count(base), ""); + if (auto args = mod->arguments()) + baseArgs[base] = *args; + } + } + } + + return baseArgs; +} + +RationalNumberType const* SMTEncoder::isConstant(Expression const& _expr) +{ + if (auto type = dynamic_cast(_expr.annotation().type)) + return type; + + // _expr may not be constant evaluable. + // In that case we ignore any warnings emitted by the constant evaluator, + // as it will return nullptr in case of failure. + ErrorList l; + ErrorReporter e(l); + if (auto t = ConstantEvaluator::evaluate(e, _expr)) + return TypeProvider::rationalNumber(t->value); + + return nullptr; +} + void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) { FunctionDefinition const* funDef = functionCallToDefinition(_funCall); @@ -2013,11 +2716,12 @@ vector SMTEncoder::symbolicArguments(FunctionCall const& _f auto const& funType = dynamic_cast(calledExpr->annotation().type); solAssert(funType, ""); + vector> arguments = _funCall.sortedArguments(); auto const& functionParams = function->parameters(); - auto const& arguments = _funCall.arguments(); unsigned firstParam = 0; if (funType->bound()) { + calledExpr = innermostTuple(*calledExpr); auto const& boundFunction = dynamic_cast(calledExpr); solAssert(boundFunction, ""); args.push_back(expr(boundFunction->expression(), functionParams.front()->type())); diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index ec097a2c976e..14a1556d59bb 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -36,6 +36,7 @@ #include #include #include +#include namespace solidity::langutil { @@ -51,16 +52,41 @@ class SMTEncoder: public ASTConstVisitor public: SMTEncoder(smt::EncodingContext& _context); + /// @returns true if engine should proceed with analysis. + bool analyze(SourceUnit const& _sources); + /// @returns the leftmost identifier in a multi-d IndexAccess. static Expression const* leftmostBase(IndexAccess const& _indexAccess); - /// @returns the innermost element in a chain of 1-tuples. - static Expression const* innermostTuple(TupleExpression const& _tuple); + /// @returns the key type in _type. + /// _type must allow IndexAccess, that is, + /// it must be either ArrayType or MappingType + static TypePointer keyType(TypePointer _type); + + /// @returns the innermost element in a chain of 1-tuples if applicable, + /// otherwise _expr. + static Expression const* innermostTuple(Expression const& _expr); /// @returns the FunctionDefinition of a FunctionCall /// if possible or nullptr. static FunctionDefinition const* functionCallToDefinition(FunctionCall const& _funCall); + static std::vector stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); + static std::vector stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); + + static std::vector localVariablesIncludingModifiers(FunctionDefinition const& _function); + static std::vector modifiersVariables(FunctionDefinition const& _function); + + /// @returns the SourceUnit that contains _scopable. + static SourceUnit const* sourceUnitContaining(Scopable const& _scopable); + + /// @returns the arguments for each base constructor call in the hierarchy of @a _contract. + std::map>> baseArguments(ContractDefinition const& _contract); + + /// @returns a valid RationalNumberType pointer if _expr has type + /// RationalNumberType or can be const evaluated, and nullptr otherwise. + static RationalNumberType const* isConstant(Expression const& _expr); + protected: // TODO: Check that we do not have concurrent reads and writes to a variable, // because the order of expression evaluation is undefined @@ -73,16 +99,18 @@ class SMTEncoder: public ASTConstVisitor bool visit(FunctionDefinition const& _node) override; void endVisit(FunctionDefinition const& _node) override; bool visit(PlaceholderStatement const& _node) override; - bool visit(IfStatement const& _node) override; + bool visit(IfStatement const&) override { return false; } bool visit(WhileStatement const&) override { return false; } bool visit(ForStatement const&) override { return false; } void endVisit(VariableDeclarationStatement const& _node) override; + bool visit(Assignment const& _node) override; void endVisit(Assignment const& _node) override; void endVisit(TupleExpression const& _node) override; bool visit(UnaryOperation const& _node) override; void endVisit(UnaryOperation const& _node) override; bool visit(BinaryOperation const& _node) override; void endVisit(BinaryOperation const& _node) override; + bool visit(Conditional const& _node) override; void endVisit(FunctionCall const& _node) override; bool visit(ModifierInvocation const& _node) override; void endVisit(Identifier const& _node) override; @@ -97,6 +125,9 @@ class SMTEncoder: public ASTConstVisitor void endVisit(Continue const&) override {} bool visit(TryCatchClause const& _node) override; + virtual void pushInlineFrame(CallableDeclaration const&); + virtual void popInlineFrame(CallableDeclaration const&); + /// Do not visit subtree if node is a RationalNumber. /// Symbolic _expr is the rational literal. bool shortcutRationalNumber(Expression const& _expr); @@ -111,17 +142,33 @@ class SMTEncoder: public ASTConstVisitor TypePointer const& _commonType, Expression const& _expression ); + + smtutil::Expression bitwiseOperation( + Token _op, + smtutil::Expression const& _left, + smtutil::Expression const& _right, + TypePointer const& _commonType + ); + void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); void bitwiseOperation(BinaryOperation const& _op); + void bitwiseNotOperation(UnaryOperation const& _op); void initContract(ContractDefinition const& _contract); void initFunction(FunctionDefinition const& _function); void visitAssert(FunctionCall const& _funCall); void visitRequire(FunctionCall const& _funCall); + void visitCryptoFunction(FunctionCall const& _funCall); void visitGasLeft(FunctionCall const& _funCall); + virtual void visitAddMulMod(FunctionCall const& _funCall); + void visitObjectCreation(FunctionCall const& _funCall); void visitTypeConversion(FunctionCall const& _funCall); + void visitStructConstructorCall(FunctionCall const& _funCall); void visitFunctionIdentifier(Identifier const& _identifier); + void visitPublicGetter(FunctionCall const& _funCall); + + bool isPublicGetter(Expression const& _expr); /// Encodes a modifier or function body according to the modifier /// visit depth. @@ -140,8 +187,8 @@ class SMTEncoder: public ASTConstVisitor /// to variable of some SMT array type /// while aliasing is not supported. void arrayAssignment(); - /// Handles assignment to SMT array index. - void arrayIndexAssignment(Expression const& _expr, smtutil::Expression const& _rightHandSide); + /// Handles assignments to index or member access. + void indexOrMemberAssignment(Expression const& _expr, smtutil::Expression const& _rightHandSide); void arrayPush(FunctionCall const& _funCall); void arrayPop(FunctionCall const& _funCall); @@ -150,9 +197,23 @@ class SMTEncoder: public ASTConstVisitor /// an empty array. virtual void makeArrayPopVerificationTarget(FunctionCall const&) {} - /// Division expression in the given type. Requires special treatment because - /// of rounding for signed division. - smtutil::Expression division(smtutil::Expression _left, smtutil::Expression _right, IntegerType const& _type); + void addArrayLiteralAssertions( + smt::SymbolicArrayVariable& _symArray, + std::vector const& _elementValues + ); + + /// @returns a pair of expressions representing _left / _right and _left mod _right, respectively. + /// Uses slack variables and additional constraints to express the results using only operations + /// more friendly to the SMT solver (multiplication, addition, subtraction and comparison). + std::pair divModWithSlacks( + smtutil::Expression _left, + smtutil::Expression _right, + IntegerType const& _type + ); + + /// Handles the actual assertion of the new value to the encoding context. + /// Other assignment methods should use this one in the end. + virtual void assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value); void assignment(VariableDeclaration const& _variable, Expression const& _value); /// Handles assignments to variables of different types. @@ -162,8 +223,7 @@ class SMTEncoder: public ASTConstVisitor void assignment( Expression const& _left, smtutil::Expression const& _right, - TypePointer const& _type, - langutil::SourceLocation const& _location + TypePointer const& _type ); /// Handle assignments between tuples. void tupleAssignment(Expression const& _left, Expression const& _right); @@ -175,9 +235,10 @@ class SMTEncoder: public ASTConstVisitor /// Visits the branch given by the statement, pushes and pops the current path conditions. /// @param _condition if present, asserts that this condition is true within the branch. - /// @returns the variable indices after visiting the branch. - VariableIndices visitBranch(ASTNode const* _statement, smtutil::Expression const* _condition = nullptr); - VariableIndices visitBranch(ASTNode const* _statement, smtutil::Expression _condition); + /// @returns the variable indices after visiting the branch and the expression representing + /// the path condition at the end of the branch. + std::pair visitBranch(ASTNode const* _statement, smtutil::Expression const* _condition = nullptr); + std::pair visitBranch(ASTNode const* _statement, smtutil::Expression _condition); using CallStackEntry = std::pair; @@ -190,8 +251,12 @@ class SMTEncoder: public ASTConstVisitor /// Resets all references/pointers that have the same type or have /// a subexpression of the same type as _varDecl. void resetReferences(VariableDeclaration const& _varDecl); + /// Resets all references/pointers that have type _type. + void resetReferences(TypePointer _type); /// @returns the type without storage pointer information if it has it. TypePointer typeWithoutPointer(TypePointer const& _type); + /// @returns whether _a or a subtype of _a is the same as _b. + bool sameTypeOrSubtype(TypePointer _a, TypePointer _b); /// Given two different branches and the touched variables, /// merge the touched variables into after-branch ite variables @@ -215,6 +280,8 @@ class SMTEncoder: public ASTConstVisitor /// Creates the expression and sets its value. void defineExpr(Expression const& _e, smtutil::Expression _value); + /// Overwrites the current path condition + void setPathCondition(smtutil::Expression const& _e); /// Adds a new path condition void pushPathCondition(smtutil::Expression const& _e); /// Remove the last path condition @@ -241,8 +308,20 @@ class SMTEncoder: public ASTConstVisitor /// @returns variables that are touched in _node's subtree. std::set touchedVariables(ASTNode const& _node); - /// @returns the VariableDeclaration referenced by an Identifier or nullptr. - VariableDeclaration const* identifierToVariable(Expression const& _expr); + /// @returns the declaration referenced by _expr, if any, + /// and nullptr otherwise. + Declaration const* expressionToDeclaration(Expression const& _expr) const; + + /// @returns the VariableDeclaration referenced by an Expression or nullptr. + VariableDeclaration const* identifierToVariable(Expression const& _expr) const; + + /// @returns the MemberAccess .push if _expr is an empty array push call, + /// otherwise nullptr. + MemberAccess const* isEmptyPush(Expression const& _expr) const; + + /// @returns true if the given identifier is a contract which is known and trusted. + /// This means we don't have to abstract away effects of external function calls to this contract. + static bool isTrustedExternalCall(Expression const* _expr); /// Creates symbolic expressions for the returned values /// and set them as the components of the symbolic tuple. diff --git a/libsolidity/formal/SymbolicState.cpp b/libsolidity/formal/SymbolicState.cpp index 06fe1bd2aa51..5d38eac0f579 100644 --- a/libsolidity/formal/SymbolicState.cpp +++ b/libsolidity/formal/SymbolicState.cpp @@ -18,55 +18,135 @@ #include +#include #include using namespace std; using namespace solidity; +using namespace solidity::smtutil; using namespace solidity::frontend::smt; -SymbolicState::SymbolicState(EncodingContext& _context): +BlockchainVariable::BlockchainVariable( + string _name, + map _members, + EncodingContext& _context +): + m_name(move(_name)), + m_members(move(_members)), m_context(_context) { + vector members; + vector sorts; + for (auto const& [component, sort]: m_members) + { + members.emplace_back(component); + sorts.emplace_back(sort); + m_componentIndices[component] = static_cast(members.size() - 1); + } + m_tuple = make_unique( + make_shared(m_name + "_type", members, sorts), + m_name, + m_context + ); +} + +smtutil::Expression BlockchainVariable::member(string const& _member) const +{ + return m_tuple->component(m_componentIndices.at(_member)); +} + +smtutil::Expression BlockchainVariable::assignMember(string const& _member, smtutil::Expression const& _value) +{ + vector args; + for (auto const& m: m_members) + if (m.first == _member) + args.emplace_back(_value); + else + args.emplace_back(member(m.first)); + m_tuple->increaseIndex(); + auto tuple = m_tuple->currentValue(); + auto sortExpr = smtutil::Expression(make_shared(tuple.sort), tuple.name); + m_context.addAssertion(tuple == smtutil::Expression::tuple_constructor(sortExpr, args)); + return m_tuple->currentValue(); } void SymbolicState::reset() { + m_error.resetIndex(); m_thisAddress.resetIndex(); - m_balances.resetIndex(); + m_state.reset(); + m_tx.reset(); + m_crypto.reset(); } -// Blockchain +smtutil::Expression SymbolicState::balances() const +{ + return m_state.member("balances"); +} -smtutil::Expression SymbolicState::thisAddress() +smtutil::Expression SymbolicState::balance() const { - return m_thisAddress.currentValue(); + return balance(thisAddress()); } -smtutil::Expression SymbolicState::balance() +smtutil::Expression SymbolicState::balance(smtutil::Expression _address) const { - return balance(m_thisAddress.currentValue()); + return smtutil::Expression::select(balances(), move(_address)); } -smtutil::Expression SymbolicState::balance(smtutil::Expression _address) +smtutil::Expression SymbolicState::blockhash(smtutil::Expression _blockNumber) const { - return smtutil::Expression::select(m_balances.elements(), move(_address)); + return smtutil::Expression::select(m_tx.member("blockhash"), move(_blockNumber)); } void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value) { - unsigned indexBefore = m_balances.index(); + unsigned indexBefore = m_state.index(); addBalance(_from, 0 - _value); addBalance(_to, move(_value)); - unsigned indexAfter = m_balances.index(); + unsigned indexAfter = m_state.index(); solAssert(indexAfter > indexBefore, ""); - m_balances.increaseIndex(); + m_state.newVar(); /// Do not apply the transfer operation if _from == _to. - auto newBalances = smtutil::Expression::ite( + auto newState = smtutil::Expression::ite( move(_from) == move(_to), - m_balances.valueAtIndex(indexBefore), - m_balances.valueAtIndex(indexAfter) + m_state.value(indexBefore), + m_state.value(indexAfter) ); - m_context.addAssertion(m_balances.currentValue() == newBalances); + m_context.addAssertion(m_state.value() == newState); +} + +smtutil::Expression SymbolicState::txMember(string const& _member) const +{ + return m_tx.member(_member); +} + +smtutil::Expression SymbolicState::txConstraints(FunctionDefinition const& _function) const +{ + smtutil::Expression conj = smt::symbolicUnknownConstraints(m_tx.member("block.coinbase"), TypeProvider::uint(160)) && + smt::symbolicUnknownConstraints(m_tx.member("msg.sender"), TypeProvider::uint(160)) && + smt::symbolicUnknownConstraints(m_tx.member("tx.origin"), TypeProvider::uint(160)); + + if (_function.isPartOfExternalInterface()) + { + auto sig = TypeProvider::function(_function)->externalIdentifier(); + conj = conj && m_tx.member("msg.sig") == sig; + + auto b0 = sig >> (3 * 8); + auto b1 = (sig & 0x00ff0000) >> (2 * 8); + auto b2 = (sig & 0x0000ff00) >> (1 * 8); + auto b3 = (sig & 0x000000ff); + auto data = smtutil::Expression::tuple_get(m_tx.member("msg.data"), 0); + conj = conj && smtutil::Expression::select(data, 0) == b0; + conj = conj && smtutil::Expression::select(data, 1) == b1; + conj = conj && smtutil::Expression::select(data, 2) == b2; + conj = conj && smtutil::Expression::select(data, 3) == b3; + auto length = smtutil::Expression::tuple_get(m_tx.member("msg.data"), 1); + // TODO add ABI size of function input parameters here \/ + conj = conj && length >= 4; + } + + return conj; } /// Private helpers. @@ -74,13 +154,9 @@ void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value) { auto newBalances = smtutil::Expression::store( - m_balances.elements(), + balances(), _address, balance(_address) + move(_value) ); - auto oldLength = m_balances.length(); - m_balances.increaseIndex(); - m_context.addAssertion(m_balances.elements() == newBalances); - m_context.addAssertion(m_balances.length() == oldLength); + m_state.assignMember("balances", newBalances); } - diff --git a/libsolidity/formal/SymbolicState.h b/libsolidity/formal/SymbolicState.h index aaf6971fa975..16e376b050fb 100644 --- a/libsolidity/formal/SymbolicState.h +++ b/libsolidity/formal/SymbolicState.h @@ -18,6 +18,7 @@ #pragma once +#include #include #include @@ -27,45 +28,189 @@ namespace solidity::frontend::smt { class EncodingContext; +class SymbolicAddressVariable; +class SymbolicArrayVariable; + +class BlockchainVariable +{ +public: + BlockchainVariable(std::string _name, std::map _members, EncodingContext& _context); + /// @returns the variable data as a tuple. + smtutil::Expression value() const { return m_tuple->currentValue(); } + smtutil::Expression value(unsigned _idx) const { return m_tuple->valueAtIndex(_idx); } + smtutil::SortPointer const& sort() const { return m_tuple->sort(); } + unsigned index() const { return m_tuple->index(); } + void newVar() { m_tuple->increaseIndex(); } + void reset() { m_tuple->resetIndex(); } + + /// @returns the symbolic _member. + smtutil::Expression member(std::string const& _member) const; + /// Generates a new tuple where _member is assigned _value. + smtutil::Expression assignMember(std::string const& _member, smtutil::Expression const& _value); + +private: + std::string const m_name; + std::map const m_members; + EncodingContext& m_context; + std::map m_componentIndices; + std::unique_ptr m_tuple; +}; /** - * Symbolic representation of the blockchain state. + * Symbolic representation of the blockchain context: + * - error flag + * - this (the address of the currently executing contract) + * - state, represented as a tuple of: + * - balances + * - TODO: potentially storage of contracts + * - block and transaction properties, represented as a tuple of: + * - blockhash + * - block coinbase + * - block difficulty + * - block gaslimit + * - block number + * - block timestamp + * - TODO gasleft + * - msg data + * - msg sender + * - msg sig + * - msg value + * - tx gasprice + * - tx origin */ class SymbolicState { public: - SymbolicState(EncodingContext& _context); + SymbolicState(EncodingContext& _context): m_context(_context) {} void reset(); - /// Blockchain. + /// Error flag. //@{ - /// Value of `this` address. - smtutil::Expression thisAddress(); + SymbolicIntVariable& errorFlag() { return m_error; } + smtutil::SortPointer const& errorFlagSort() const { return m_error.sort(); } + //@} + + /// This. + //@{ + /// @returns the symbolic value of the currently executing contract's address. + smtutil::Expression thisAddress() const { return m_thisAddress.currentValue(); } + smtutil::Expression thisAddress(unsigned _idx) const { return m_thisAddress.valueAtIndex(_idx); } + smtutil::SortPointer const& thisAddressSort() const { return m_thisAddress.sort(); } + //@} + + /// Blockchain state. + //@{ + smtutil::Expression state() const { return m_state.value(); } + smtutil::Expression state(unsigned _idx) const { return m_state.value(_idx); } + smtutil::SortPointer const& stateSort() const { return m_state.sort(); } + void newState() { m_state.newVar(); } + /// @returns the symbolic balances. + smtutil::Expression balances() const; /// @returns the symbolic balance of address `this`. - smtutil::Expression balance(); + smtutil::Expression balance() const; /// @returns the symbolic balance of an address. - smtutil::Expression balance(smtutil::Expression _address); + smtutil::Expression balance(smtutil::Expression _address) const; + /// Transfer _value from _from to _to. void transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value); //@} + /// Transaction data. + //@{ + /// @returns the tx data as a tuple. + smtutil::Expression tx() const { return m_tx.value(); } + smtutil::Expression tx(unsigned _idx) const { return m_tx.value(_idx); } + smtutil::SortPointer const& txSort() const { return m_tx.sort(); } + void newTx() { m_tx.newVar(); } + smtutil::Expression txMember(std::string const& _member) const; + smtutil::Expression txConstraints(FunctionDefinition const& _function) const; + smtutil::Expression blockhash(smtutil::Expression _blockNumber) const; + //@} + + /// Crypto functions. + //@{ + /// @returns the crypto functions represented as a tuple of arrays. + smtutil::Expression crypto() const { return m_crypto.value(); } + smtutil::Expression crypto(unsigned _idx) const { return m_crypto.value(_idx); } + smtutil::SortPointer const& cryptoSort() const { return m_crypto.sort(); } + void newCrypto() { m_crypto.newVar(); } + smtutil::Expression cryptoFunction(std::string const& _member) const { return m_crypto.member(_member); } + //@} + private: /// Adds _value to _account's balance. void addBalance(smtutil::Expression _account, smtutil::Expression _value); EncodingContext& m_context; - /// Symbolic `this` address. + SymbolicIntVariable m_error{ + TypeProvider::uint256(), + TypeProvider::uint256(), + "error", + m_context + }; + SymbolicAddressVariable m_thisAddress{ "this", m_context }; - /// Symbolic balances. - SymbolicArrayVariable m_balances{ - std::make_shared(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort), - "balances", + BlockchainVariable m_state{ + "state", + {{"balances", std::make_shared(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)}}, + m_context + }; + + BlockchainVariable m_tx{ + "tx", + { + {"blockhash", std::make_shared(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort)}, + {"block.coinbase", smt::smtSort(*TypeProvider::address())}, + {"block.difficulty", smtutil::SortProvider::uintSort}, + {"block.gaslimit", smtutil::SortProvider::uintSort}, + {"block.number", smtutil::SortProvider::uintSort}, + {"block.timestamp", smtutil::SortProvider::uintSort}, + // TODO gasleft + {"msg.data", smt::smtSort(*TypeProvider::bytesMemory())}, + {"msg.sender", smt::smtSort(*TypeProvider::address())}, + {"msg.sig", smtutil::SortProvider::uintSort}, + {"msg.value", smtutil::SortProvider::uintSort}, + {"tx.gasprice", smtutil::SortProvider::uintSort}, + {"tx.origin", smt::smtSort(*TypeProvider::address())} + }, + m_context + }; + + BlockchainVariable m_crypto{ + "crypto", + { + {"keccak256", std::make_shared( + smt::smtSort(*TypeProvider::bytesStorage()), + smtSort(*TypeProvider::fixedBytes(32)) + )}, + {"sha256", std::make_shared( + smt::smtSort(*TypeProvider::bytesStorage()), + smtSort(*TypeProvider::fixedBytes(32)) + )}, + {"ripemd160", std::make_shared( + smt::smtSort(*TypeProvider::bytesStorage()), + smtSort(*TypeProvider::fixedBytes(20)) + )}, + {"ecrecover", std::make_shared( + std::make_shared( + "ecrecover_input_type", + std::vector{"hash", "v", "r", "s"}, + std::vector{ + smt::smtSort(*TypeProvider::fixedBytes(32)), + smt::smtSort(*TypeProvider::uint(8)), + smt::smtSort(*TypeProvider::fixedBytes(32)), + smt::smtSort(*TypeProvider::fixedBytes(32)) + } + ), + smtSort(*TypeProvider::address()) + )} + }, m_context }; }; diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index b149cb69c340..6fdd59d969d8 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -18,6 +18,8 @@ #include +#include + #include #include #include @@ -25,6 +27,7 @@ #include using namespace std; +using namespace solidity::util; using namespace solidity::smtutil; namespace solidity::frontend::smt @@ -32,7 +35,7 @@ namespace solidity::frontend::smt SortPointer smtSort(frontend::Type const& _type) { - switch (smtKind(_type.category())) + switch (smtKind(_type)) { case Kind::Int: if (auto const* intType = dynamic_cast(&_type)) @@ -63,13 +66,13 @@ SortPointer smtSort(frontend::Type const& _type) case Kind::Array: { shared_ptr array; - if (isMapping(_type.category())) + if (isMapping(_type)) { auto mapType = dynamic_cast(&_type); solAssert(mapType, ""); array = make_shared(smtSortAbstractFunction(*mapType->keyType()), smtSortAbstractFunction(*mapType->valueType())); } - else if (isStringLiteral(_type.category())) + else if (isStringLiteral(_type)) { auto stringLitType = dynamic_cast(&_type); solAssert(stringLitType, ""); @@ -92,13 +95,35 @@ SortPointer smtSort(frontend::Type const& _type) string tupleName; if ( auto arrayType = dynamic_cast(&_type); - (arrayType && arrayType->isString()) || + (arrayType && (arrayType->isString() || arrayType->isByteArray())) || _type.category() == frontend::Type::Category::ArraySlice || _type.category() == frontend::Type::Category::StringLiteral ) - tupleName = "bytes_tuple"; + tupleName = "bytes"; + else if (auto arrayType = dynamic_cast(&_type)) + { + auto baseType = arrayType->baseType(); + // Solidity allows implicit conversion also when assigning arrays. + // So if the base type potentially has a size, that size cannot go + // in the tuple's name. + if (auto tupleSort = dynamic_pointer_cast(array->range)) + tupleName = tupleSort->name; + else if ( + baseType->category() == frontend::Type::Category::Integer || + baseType->category() == frontend::Type::Category::FixedPoint + ) + tupleName = "uint"; + else if (baseType->category() == frontend::Type::Category::FixedBytes) + tupleName = "fixedbytes"; + else + tupleName = arrayType->baseType()->toString(true); + + tupleName += "[]"; + } else - tupleName = _type.toString(true) + "_tuple"; + tupleName = _type.toString(true); + + tupleName += "_tuple"; return make_shared( tupleName, @@ -108,18 +133,32 @@ SortPointer smtSort(frontend::Type const& _type) } case Kind::Tuple: { - auto tupleType = dynamic_cast(&_type); - solAssert(tupleType, ""); vector members; - auto const& tupleName = _type.identifier(); - auto const& components = tupleType->components(); - for (unsigned i = 0; i < components.size(); ++i) - members.emplace_back(tupleName + "_accessor_" + to_string(i)); - return make_shared( - tupleName, - members, - smtSortAbstractFunction(tupleType->components()) - ); + auto const& tupleName = _type.toString(true); + vector sorts; + + if (auto const* tupleType = dynamic_cast(&_type)) + { + auto const& components = tupleType->components(); + for (unsigned i = 0; i < components.size(); ++i) + members.emplace_back(tupleName + "_accessor_" + to_string(i)); + sorts = smtSortAbstractFunction(tupleType->components()); + } + else if (auto const* structType = dynamic_cast(&_type)) + { + solAssert(!structType->recursive(), ""); + auto const& structMembers = structType->structDefinition().members(); + for (auto member: structMembers) + members.emplace_back(tupleName + "_accessor_" + member->name()); + sorts = smtSortAbstractFunction(applyMap( + structMembers, + [](auto var) { return var->type(); } + )); + } + else + solAssert(false, ""); + + return make_shared(tupleName, members, sorts); } default: // Abstract case. @@ -137,7 +176,7 @@ vector smtSort(vector const& _types) SortPointer smtSortAbstractFunction(frontend::Type const& _type) { - if (isFunction(_type.category())) + if (isFunction(_type)) return SortProvider::uintSort; return smtSort(_type); } @@ -153,35 +192,36 @@ vector smtSortAbstractFunction(vector const& return sorts; } -Kind smtKind(frontend::Type::Category _category) +Kind smtKind(frontend::Type const& _type) { - if (isNumber(_category)) + if (isNumber(_type)) return Kind::Int; - else if (isBool(_category)) + else if (isBool(_type)) return Kind::Bool; - else if (isFunction(_category)) + else if (isFunction(_type)) return Kind::Function; - else if (isMapping(_category) || isArray(_category)) + else if (isMapping(_type) || isArray(_type)) return Kind::Array; - else if (isTuple(_category)) + else if (isTuple(_type) || isNonRecursiveStruct(_type)) return Kind::Tuple; // Abstract case. return Kind::Int; } -bool isSupportedType(frontend::Type::Category _category) +bool isSupportedType(frontend::Type const& _type) { - return isNumber(_category) || - isBool(_category) || - isMapping(_category) || - isArray(_category) || - isTuple(_category); + return isNumber(_type) || + isBool(_type) || + isMapping(_type) || + isArray(_type) || + isTuple(_type) || + isNonRecursiveStruct(_type); } -bool isSupportedTypeDeclaration(frontend::Type::Category _category) +bool isSupportedTypeDeclaration(frontend::Type const& _type) { - return isSupportedType(_category) || - isFunction(_category); + return isSupportedType(_type) || + isFunction(_type); } pair> newSymbolicVariable( @@ -198,9 +238,9 @@ pair> newSymbolicVariable( abstract = true; var = make_shared(frontend::TypeProvider::uint256(), type, _uniqueName, _context); } - else if (isBool(_type.category())) + else if (isBool(_type)) var = make_shared(type, _uniqueName, _context); - else if (isFunction(_type.category())) + else if (isFunction(_type)) { auto const& fType = dynamic_cast(type); auto const& paramsIn = fType->parameterTypes(); @@ -223,21 +263,21 @@ pair> newSymbolicVariable( else var = make_shared(type, _uniqueName, _context); } - else if (isInteger(_type.category())) + else if (isInteger(_type)) var = make_shared(type, type, _uniqueName, _context); - else if (isFixedPoint(_type.category())) + else if (isFixedPoint(_type)) var = make_shared(type, type, _uniqueName, _context); - else if (isFixedBytes(_type.category())) + else if (isFixedBytes(_type)) { auto fixedBytesType = dynamic_cast(type); solAssert(fixedBytesType, ""); var = make_shared(type, fixedBytesType->numBytes(), _uniqueName, _context); } - else if (isAddress(_type.category()) || isContract(_type.category())) + else if (isAddress(_type) || isContract(_type)) var = make_shared(_uniqueName, _context); - else if (isEnum(_type.category())) + else if (isEnum(_type)) var = make_shared(type, _uniqueName, _context); - else if (isRational(_type.category())) + else if (isRational(_type)) { auto rational = dynamic_cast(&_type); solAssert(rational, ""); @@ -246,111 +286,126 @@ pair> newSymbolicVariable( else var = make_shared(type, type, _uniqueName, _context); } - else if (isMapping(_type.category()) || isArray(_type.category())) + else if (isMapping(_type) || isArray(_type)) var = make_shared(type, type, _uniqueName, _context); - else if (isTuple(_type.category())) + else if (isTuple(_type)) var = make_shared(type, _uniqueName, _context); - else if (isStringLiteral(_type.category())) + else if (isStringLiteral(_type)) { auto stringType = TypeProvider::stringMemory(); var = make_shared(stringType, type, _uniqueName, _context); } + else if (isNonRecursiveStruct(_type)) + var = make_shared(type, _uniqueName, _context); else solAssert(false, ""); return make_pair(abstract, var); } -bool isSupportedType(frontend::Type const& _type) +bool isInteger(frontend::Type const& _type) { - return isSupportedType(_type.category()); + return _type.category() == frontend::Type::Category::Integer; } -bool isSupportedTypeDeclaration(frontend::Type const& _type) +bool isFixedPoint(frontend::Type const& _type) { - return isSupportedTypeDeclaration(_type.category()); + return _type.category() == frontend::Type::Category::FixedPoint; } -bool isInteger(frontend::Type::Category _category) +bool isRational(frontend::Type const& _type) { - return _category == frontend::Type::Category::Integer; + return _type.category() == frontend::Type::Category::RationalNumber; } -bool isFixedPoint(frontend::Type::Category _category) +bool isFixedBytes(frontend::Type const& _type) { - return _category == frontend::Type::Category::FixedPoint; + return _type.category() == frontend::Type::Category::FixedBytes; } -bool isRational(frontend::Type::Category _category) +bool isAddress(frontend::Type const& _type) { - return _category == frontend::Type::Category::RationalNumber; + return _type.category() == frontend::Type::Category::Address; } -bool isFixedBytes(frontend::Type::Category _category) +bool isContract(frontend::Type const& _type) { - return _category == frontend::Type::Category::FixedBytes; + return _type.category() == frontend::Type::Category::Contract; } -bool isAddress(frontend::Type::Category _category) +bool isEnum(frontend::Type const& _type) { - return _category == frontend::Type::Category::Address; + return _type.category() == frontend::Type::Category::Enum; } -bool isContract(frontend::Type::Category _category) +bool isNumber(frontend::Type const& _type) { - return _category == frontend::Type::Category::Contract; + return isInteger(_type) || + isFixedPoint(_type) || + isRational(_type) || + isFixedBytes(_type) || + isAddress(_type) || + isContract(_type) || + isEnum(_type); } -bool isEnum(frontend::Type::Category _category) +bool isBool(frontend::Type const& _type) { - return _category == frontend::Type::Category::Enum; + return _type.category() == frontend::Type::Category::Bool; } -bool isNumber(frontend::Type::Category _category) +bool isFunction(frontend::Type const& _type) { - return isInteger(_category) || - isFixedPoint(_category) || - isRational(_category) || - isFixedBytes(_category) || - isAddress(_category) || - isContract(_category) || - isEnum(_category); + return _type.category() == frontend::Type::Category::Function; } -bool isBool(frontend::Type::Category _category) +bool isMapping(frontend::Type const& _type) { - return _category == frontend::Type::Category::Bool; + return _type.category() == frontend::Type::Category::Mapping; } -bool isFunction(frontend::Type::Category _category) +bool isArray(frontend::Type const& _type) { - return _category == frontend::Type::Category::Function; + return _type.category() == frontend::Type::Category::Array || + _type.category() == frontend::Type::Category::StringLiteral || + _type.category() == frontend::Type::Category::ArraySlice; } -bool isMapping(frontend::Type::Category _category) +bool isTuple(frontend::Type const& _type) { - return _category == frontend::Type::Category::Mapping; + return _type.category() == frontend::Type::Category::Tuple; } -bool isArray(frontend::Type::Category _category) +bool isStringLiteral(frontend::Type const& _type) { - return _category == frontend::Type::Category::Array || - _category == frontend::Type::Category::StringLiteral || - _category == frontend::Type::Category::ArraySlice; + return _type.category() == frontend::Type::Category::StringLiteral; } -bool isTuple(frontend::Type::Category _category) +bool isNonRecursiveStruct(frontend::Type const& _type) { - return _category == frontend::Type::Category::Tuple; + auto structType = dynamic_cast(&_type); + return structType && !structType->recursive(); } -bool isStringLiteral(frontend::Type::Category _category) +smtutil::Expression minValue(frontend::IntegerType const& _type) { - return _category == frontend::Type::Category::StringLiteral; + return smtutil::Expression(_type.minValue()); } -smtutil::Expression minValue(frontend::IntegerType const& _type) +smtutil::Expression minValue(frontend::TypePointer _type) { - return smtutil::Expression(_type.minValue()); + solAssert(isNumber(*_type), ""); + if (auto const* intType = dynamic_cast(_type)) + return intType->minValue(); + if (auto const* fixedType = dynamic_cast(_type)) + return fixedType->minIntegerValue(); + if ( + dynamic_cast(_type) || + dynamic_cast(_type) || + dynamic_cast(_type) || + dynamic_cast(_type) + ) + return 0; + solAssert(false, ""); } smtutil::Expression maxValue(frontend::IntegerType const& _type) @@ -358,6 +413,25 @@ smtutil::Expression maxValue(frontend::IntegerType const& _type) return smtutil::Expression(_type.maxValue()); } +smtutil::Expression maxValue(frontend::TypePointer _type) +{ + solAssert(isNumber(*_type), ""); + if (auto const* intType = dynamic_cast(_type)) + return intType->maxValue(); + if (auto const* fixedType = dynamic_cast(_type)) + return fixedType->maxIntegerValue(); + if ( + dynamic_cast(_type) || + dynamic_cast(_type) + ) + return TypeProvider::uint(160)->maxValue(); + if (auto const* enumType = dynamic_cast(_type)) + return enumType->numberOfMembers(); + if (auto const* bytesType = dynamic_cast(_type)) + return TypeProvider::uint(bytesType->numBytes() * 8)->maxValue(); + solAssert(false, ""); +} + void setSymbolicZeroValue(SymbolicVariable const& _variable, EncodingContext& _context) { setSymbolicZeroValue(_variable.currentValue(), _variable.type(), _context); @@ -372,13 +446,13 @@ void setSymbolicZeroValue(smtutil::Expression _expr, frontend::TypePointer const smtutil::Expression zeroValue(frontend::TypePointer const& _type) { solAssert(_type, ""); - if (isSupportedType(_type->category())) + if (isSupportedType(*_type)) { - if (isNumber(_type->category())) + if (isNumber(*_type)) return 0; - if (isBool(_type->category())) + if (isBool(*_type)) return smtutil::Expression(false); - if (isArray(_type->category()) || isMapping(_type->category())) + if (isArray(*_type) || isMapping(*_type)) { auto tupleSort = dynamic_pointer_cast(smtSort(*_type)); solAssert(tupleSort, ""); @@ -399,39 +473,90 @@ smtutil::Expression zeroValue(frontend::TypePointer const& _type) solAssert(zeroArray, ""); return smtutil::Expression::tuple_constructor( - smtutil::Expression(std::make_shared(smtSort(*_type)), _type->toString(true)), + smtutil::Expression(std::make_shared(tupleSort), tupleSort->name), vector{*zeroArray, length} ); } + if (isNonRecursiveStruct(*_type)) + { + auto const* structType = dynamic_cast(_type); + auto structSort = dynamic_pointer_cast(smtSort(*_type)); + return smtutil::Expression::tuple_constructor( + smtutil::Expression(make_shared(structSort), structSort->name), + applyMap( + structType->structDefinition().members(), + [](auto var) { return zeroValue(var->type()); } + ) + ); + } solAssert(false, ""); } // Unsupported types are abstracted as Int. return 0; } +bool isSigned(TypePointer const& _type) +{ + solAssert(smt::isNumber(*_type), ""); + bool isSigned = false; + if (auto const* numberType = dynamic_cast(_type)) + isSigned |= numberType->isNegative(); + else if (auto const* intType = dynamic_cast(_type)) + isSigned |= intType->isSigned(); + else if (auto const* fixedType = dynamic_cast(_type)) + isSigned |= fixedType->isSigned(); + else if ( + dynamic_cast(_type) || + dynamic_cast(_type) || + dynamic_cast(_type) || + dynamic_cast(_type) + ) + return false; + else + solAssert(false, ""); + + return isSigned; +} + +pair typeBvSizeAndSignedness(frontend::TypePointer const& _type) +{ + if (auto const* intType = dynamic_cast(_type)) + return {intType->numBits(), intType->isSigned()}; + else if (auto const* fixedType = dynamic_cast(_type)) + return {fixedType->numBits(), fixedType->isSigned()}; + else if (auto const* fixedBytesType = dynamic_cast(_type)) + return {fixedBytesType->numBytes() * 8, false}; + else + solAssert(false, ""); +} + void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context) { setSymbolicUnknownValue(_variable.currentValue(), _variable.type(), _context); } void setSymbolicUnknownValue(smtutil::Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context) +{ + _context.addAssertion(symbolicUnknownConstraints(_expr, _type)); +} + +smtutil::Expression symbolicUnknownConstraints(smtutil::Expression _expr, frontend::TypePointer const& _type) { solAssert(_type, ""); - if (isEnum(_type->category())) + if (isEnum(*_type)) { auto enumType = dynamic_cast(_type); solAssert(enumType, ""); - _context.addAssertion(_expr >= 0); - _context.addAssertion(_expr < enumType->numberOfMembers()); + return _expr >= 0 && _expr < enumType->numberOfMembers(); } - else if (isInteger(_type->category())) + else if (isInteger(*_type)) { auto intType = dynamic_cast(_type); solAssert(intType, ""); - _context.addAssertion(_expr >= minValue(*intType)); - _context.addAssertion(_expr <= maxValue(*intType)); + return _expr >= minValue(*intType) && _expr <= maxValue(*intType); } + return smtutil::Expression(true); } optional symbolicTypeConversion(TypePointer _from, TypePointer _to) @@ -441,11 +566,13 @@ optional symbolicTypeConversion(TypePointer _from, TypePoin // but they can also be compared/assigned to fixed bytes, in which // case they'd need to be encoded as numbers. if (auto strType = dynamic_cast(_from)) - if (_to->category() == frontend::Type::Category::FixedBytes) + if (auto fixedBytesType = dynamic_cast(_to)) { if (strType->value().empty()) return smtutil::Expression(size_t(0)); - return smtutil::Expression(u256(toHex(util::asBytes(strType->value()), util::HexPrefix::Add))); + auto bytesVec = util::asBytes(strType->value()); + bytesVec.resize(fixedBytesType->numBytes(), 0); + return smtutil::Expression(u256(toHex(bytesVec, util::HexPrefix::Add))); } return std::nullopt; diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h index 401c9f0b6788..cb068c28cfcb 100644 --- a/libsolidity/formal/SymbolicTypes.h +++ b/libsolidity/formal/SymbolicTypes.h @@ -18,7 +18,6 @@ #pragma once -#include #include #include #include @@ -26,6 +25,8 @@ namespace solidity::frontend::smt { +class EncodingContext; + /// Returns the SMT sort that models the Solidity type _type. smtutil::SortPointer smtSort(frontend::Type const& _type); std::vector smtSort(std::vector const& _types); @@ -34,29 +35,30 @@ std::vector smtSort(std::vector con smtutil::SortPointer smtSortAbstractFunction(frontend::Type const& _type); std::vector smtSortAbstractFunction(std::vector const& _types); /// Returns the SMT kind that models the Solidity type type category _category. -smtutil::Kind smtKind(frontend::Type::Category _category); +smtutil::Kind smtKind(frontend::Type const& _type); /// Returns true if type is fully supported (declaration and operations). -bool isSupportedType(frontend::Type::Category _category); +bool isSupportedType(frontend::Type const& _type); bool isSupportedType(frontend::Type const& _type); /// Returns true if type is partially supported (declaration). -bool isSupportedTypeDeclaration(frontend::Type::Category _category); +bool isSupportedTypeDeclaration(frontend::Type const& _type); bool isSupportedTypeDeclaration(frontend::Type const& _type); -bool isInteger(frontend::Type::Category _category); -bool isFixedPoint(frontend::Type::Category _category); -bool isRational(frontend::Type::Category _category); -bool isFixedBytes(frontend::Type::Category _category); -bool isAddress(frontend::Type::Category _category); -bool isContract(frontend::Type::Category _category); -bool isEnum(frontend::Type::Category _category); -bool isNumber(frontend::Type::Category _category); -bool isBool(frontend::Type::Category _category); -bool isFunction(frontend::Type::Category _category); -bool isMapping(frontend::Type::Category _category); -bool isArray(frontend::Type::Category _category); -bool isTuple(frontend::Type::Category _category); -bool isStringLiteral(frontend::Type::Category _category); +bool isInteger(frontend::Type const& _type); +bool isFixedPoint(frontend::Type const& _type); +bool isRational(frontend::Type const& _type); +bool isFixedBytes(frontend::Type const& _type); +bool isAddress(frontend::Type const& _type); +bool isContract(frontend::Type const& _type); +bool isEnum(frontend::Type const& _type); +bool isNumber(frontend::Type const& _type); +bool isBool(frontend::Type const& _type); +bool isFunction(frontend::Type const& _type); +bool isMapping(frontend::Type const& _type); +bool isArray(frontend::Type const& _type); +bool isTuple(frontend::Type const& _type); +bool isStringLiteral(frontend::Type const& _type); +bool isNonRecursiveStruct(frontend::Type const& _type); /// Returns a new symbolic variable, according to _type. /// Also returns whether the type is abstract or not, @@ -64,13 +66,19 @@ bool isStringLiteral(frontend::Type::Category _category); std::pair> newSymbolicVariable(frontend::Type const& _type, std::string const& _uniqueName, EncodingContext& _context); smtutil::Expression minValue(frontend::IntegerType const& _type); +smtutil::Expression minValue(frontend::TypePointer _type); smtutil::Expression maxValue(frontend::IntegerType const& _type); +smtutil::Expression maxValue(frontend::TypePointer _type); smtutil::Expression zeroValue(frontend::TypePointer const& _type); +bool isSigned(frontend::TypePointer const& _type); + +std::pair typeBvSizeAndSignedness(frontend::TypePointer const& type); void setSymbolicZeroValue(SymbolicVariable const& _variable, EncodingContext& _context); void setSymbolicZeroValue(smtutil::Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context); void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context); void setSymbolicUnknownValue(smtutil::Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context); +smtutil::Expression symbolicUnknownConstraints(smtutil::Expression _expr, frontend::TypePointer const& _type); std::optional symbolicTypeConversion(TypePointer _from, TypePointer _to); } diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index b09938e63082..8f78495a9422 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -18,11 +18,15 @@ #include +#include #include #include +#include + using namespace std; using namespace solidity; +using namespace solidity::util; using namespace solidity::smtutil; using namespace solidity::frontend; using namespace solidity::frontend::smt; @@ -118,7 +122,7 @@ SymbolicIntVariable::SymbolicIntVariable( ): SymbolicVariable(_type, _originalType, move(_uniqueName), _context) { - solAssert(isNumber(m_type->category()), ""); + solAssert(isNumber(*m_type), ""); } SymbolicAddressVariable::SymbolicAddressVariable( @@ -218,7 +222,7 @@ SymbolicEnumVariable::SymbolicEnumVariable( ): SymbolicVariable(_type, _type, move(_uniqueName), _context) { - solAssert(isEnum(m_type->category()), ""); + solAssert(isEnum(*m_type), ""); } SymbolicTupleVariable::SymbolicTupleVariable( @@ -228,7 +232,7 @@ SymbolicTupleVariable::SymbolicTupleVariable( ): SymbolicVariable(_type, _type, move(_uniqueName), _context) { - solAssert(isTuple(m_type->category()), ""); + solAssert(isTuple(*m_type), ""); } SymbolicTupleVariable::SymbolicTupleVariable( @@ -241,7 +245,25 @@ SymbolicTupleVariable::SymbolicTupleVariable( solAssert(m_sort->kind == Kind::Tuple, ""); } -vector const& SymbolicTupleVariable::components() +smtutil::Expression SymbolicTupleVariable::currentValue(frontend::TypePointer const& _targetType) const +{ + if (!_targetType || sort() == smtSort(*_targetType)) + return SymbolicVariable::currentValue(); + + auto thisTuple = dynamic_pointer_cast(sort()); + auto otherTuple = dynamic_pointer_cast(smtSort(*_targetType)); + solAssert(thisTuple && otherTuple, ""); + solAssert(thisTuple->components.size() == otherTuple->components.size(), ""); + vector args; + for (size_t i = 0; i < thisTuple->components.size(); ++i) + args.emplace_back(component(i, type(), _targetType)); + return smtutil::Expression::tuple_constructor( + smtutil::Expression(make_shared(smtSort(*_targetType)), ""), + args + ); +} + +vector const& SymbolicTupleVariable::components() const { auto tupleSort = dynamic_pointer_cast(m_sort); solAssert(tupleSort, ""); @@ -252,7 +274,7 @@ smtutil::Expression SymbolicTupleVariable::component( size_t _index, TypePointer _fromType, TypePointer _toType -) +) const { optional conversion = symbolicTypeConversion(_fromType, _toType); if (conversion) @@ -274,7 +296,7 @@ SymbolicArrayVariable::SymbolicArrayVariable( m_context ) { - solAssert(isArray(m_type->category()) || isMapping(m_type->category()), ""); + solAssert(isArray(*m_type) || isMapping(*m_type), ""); } SymbolicArrayVariable::SymbolicArrayVariable( @@ -310,12 +332,71 @@ smtutil::Expression SymbolicArrayVariable::valueAtIndex(unsigned _index) const return m_pair.valueAtIndex(_index); } -smtutil::Expression SymbolicArrayVariable::elements() +smtutil::Expression SymbolicArrayVariable::elements() const { return m_pair.component(0); } -smtutil::Expression SymbolicArrayVariable::length() +smtutil::Expression SymbolicArrayVariable::length() const { return m_pair.component(1); } + +SymbolicStructVariable::SymbolicStructVariable( + frontend::TypePointer _type, + string _uniqueName, + EncodingContext& _context +): + SymbolicVariable(_type, _type, move(_uniqueName), _context) +{ + solAssert(isNonRecursiveStruct(*m_type), ""); + auto const* structType = dynamic_cast(_type); + solAssert(structType, ""); + auto const& members = structType->structDefinition().members(); + for (unsigned i = 0; i < members.size(); ++i) + { + solAssert(members.at(i), ""); + m_memberIndices.emplace(members.at(i)->name(), i); + } +} + +smtutil::Expression SymbolicStructVariable::member(string const& _member) const +{ + return smtutil::Expression::tuple_get(currentValue(), m_memberIndices.at(_member)); +} + +smtutil::Expression SymbolicStructVariable::assignMember(string const& _member, smtutil::Expression const& _memberValue) +{ + auto const* structType = dynamic_cast(m_type); + solAssert(structType, ""); + auto const& structDef = structType->structDefinition(); + auto const& structMembers = structDef.members(); + auto oldMembers = applyMap( + structMembers, + [&](auto _member) { return member(_member->name()); } + ); + increaseIndex(); + for (unsigned i = 0; i < structMembers.size(); ++i) + { + auto const& memberName = structMembers.at(i)->name(); + auto newMember = memberName == _member ? _memberValue : oldMembers.at(i); + m_context.addAssertion(member(memberName) == newMember); + } + + return currentValue(); +} + +smtutil::Expression SymbolicStructVariable::assignAllMembers(vector const& _memberValues) +{ + auto structType = dynamic_cast(m_type); + solAssert(structType, ""); + + auto const& structDef = structType->structDefinition(); + auto const& structMembers = structDef.members(); + solAssert(_memberValues.size() == structMembers.size(), ""); + increaseIndex(); + for (unsigned i = 0; i < _memberValues.size(); ++i) + m_context.addAssertion(_memberValues[i] == member(structMembers[i]->name())); + + return currentValue(); +} diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index bdf36afd06c4..ff637333221a 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -23,6 +23,8 @@ #include #include + +#include #include namespace solidity::frontend::smt @@ -223,12 +225,14 @@ class SymbolicTupleVariable: public SymbolicVariable EncodingContext& _context ); - std::vector const& components(); + smtutil::Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const override; + + std::vector const& components() const; smtutil::Expression component( size_t _index, TypePointer _fromType = nullptr, TypePointer _toType = nullptr - ); + ) const; }; /** @@ -256,8 +260,8 @@ class SymbolicArrayVariable: public SymbolicVariable smtutil::Expression resetIndex() override { SymbolicVariable::resetIndex(); return m_pair.resetIndex(); } smtutil::Expression setIndex(unsigned _index) override { SymbolicVariable::setIndex(_index); return m_pair.setIndex(_index); } smtutil::Expression increaseIndex() override { SymbolicVariable::increaseIndex(); return m_pair.increaseIndex(); } - smtutil::Expression elements(); - smtutil::Expression length(); + smtutil::Expression elements() const; + smtutil::Expression length() const; smtutil::SortPointer tupleSort() { return m_pair.sort(); } @@ -265,4 +269,33 @@ class SymbolicArrayVariable: public SymbolicVariable SymbolicTupleVariable m_pair; }; +/** + * Specialization of SymbolicVariable for Struct. + */ +class SymbolicStructVariable: public SymbolicVariable +{ +public: + SymbolicStructVariable( + frontend::TypePointer _type, + std::string _uniqueName, + EncodingContext& _context + ); + + /// @returns the symbolic expression representing _member. + smtutil::Expression member(std::string const& _member) const; + + /// @returns the symbolic expression representing this struct + /// with field _member updated. + smtutil::Expression assignMember(std::string const& _member, smtutil::Expression const& _memberValue); + + /// @returns the symbolic expression representing this struct + /// with all fields updated with the given values. + smtutil::Expression assignAllMembers(std::vector const& _memberValues); + +private: + std::map m_memberIndices; +}; + + + } diff --git a/libsolidity/formal/VariableUsage.cpp b/libsolidity/formal/VariableUsage.cpp index 1634b3b58f4c..9b1c336d7dd4 100644 --- a/libsolidity/formal/VariableUsage.cpp +++ b/libsolidity/formal/VariableUsage.cpp @@ -60,8 +60,8 @@ void VariableUsage::endVisit(IndexAccess const& _indexAccess) void VariableUsage::endVisit(FunctionCall const& _funCall) { - if (m_inlineFunctionCalls) - if (auto const& funDef = SMTEncoder::functionCallToDefinition(_funCall)) + if (m_inlineFunctionCalls(_funCall)) + if (auto funDef = SMTEncoder::functionCallToDefinition(_funCall)) { solAssert(funDef, ""); if (find(m_callStack.begin(), m_callStack.end(), funDef) == m_callStack.end()) @@ -83,7 +83,7 @@ void VariableUsage::endVisit(FunctionDefinition const&) void VariableUsage::endVisit(ModifierInvocation const& _modifierInv) { - auto const& modifierDef = dynamic_cast(_modifierInv.name()->annotation().referencedDeclaration); + auto const& modifierDef = dynamic_cast(_modifierInv.name().annotation().referencedDeclaration); if (modifierDef) modifierDef->accept(*this); } diff --git a/libsolidity/formal/VariableUsage.h b/libsolidity/formal/VariableUsage.h index 508efbc604a9..3f69ca5a2cc8 100644 --- a/libsolidity/formal/VariableUsage.h +++ b/libsolidity/formal/VariableUsage.h @@ -36,7 +36,7 @@ class VariableUsage: private ASTConstVisitor std::set touchedVariables(ASTNode const& _node, std::vector const& _outerCallstack); /// Sets whether to inline function calls. - void setFunctionInlining(bool _inlineFunction) { m_inlineFunctionCalls = _inlineFunction; } + void setFunctionInlining(std::function _inlineFunctionCalls) { m_inlineFunctionCalls = _inlineFunctionCalls; } private: void endVisit(Identifier const& _node) override; @@ -54,7 +54,7 @@ class VariableUsage: private ASTConstVisitor std::vector m_callStack; CallableDeclaration const* m_lastCall = nullptr; - bool m_inlineFunctionCalls = false; + std::function m_inlineFunctionCalls = [](FunctionCall const&) { return false; }; }; } diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index bed432cd6972..70dbc1b3af5f 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Utilities to handle the Contract ABI (https://solidity.readthedocs.io/en/develop/abi-spec.html) + * Utilities to handle the Contract ABI (https://docs.soliditylang.org/en/develop/abi-spec.html) */ #include diff --git a/libsolidity/interface/ABI.h b/libsolidity/interface/ABI.h index 76b1a4a7594f..d13bbdd8bf82 100644 --- a/libsolidity/interface/ABI.h +++ b/libsolidity/interface/ABI.h @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Utilities to handle the Contract ABI (https://solidity.readthedocs.io/en/develop/abi-spec.html) + * Utilities to handle the Contract ABI (https://docs.soliditylang.org/en/develop/abi-spec.html) */ #pragma once diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index a253d6417d25..05df90516b5e 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -56,7 +57,10 @@ #include #include +#include #include +#include +#include #include #include @@ -85,9 +89,6 @@ static int g_compilerStackCounts = 0; CompilerStack::CompilerStack(ReadCallback::Callback _readFile): m_readFile{std::move(_readFile)}, m_enabledSMTSolvers{smtutil::SMTSolverChoice::All()}, - m_generateIR{false}, - m_generateEwasm{false}, - m_errorList{}, m_errorReporter{m_errorList} { // Because TypeProvider is currently a singleton API, we must ensure that @@ -124,30 +125,44 @@ std::optional CompilerStack::parseRemapping(string con void CompilerStack::setRemappings(vector const& _remappings) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set remappings before parsing.")); for (auto const& remapping: _remappings) solAssert(!remapping.prefix.empty(), ""); m_remappings = _remappings; } +void CompilerStack::setViaIR(bool _viaIR) +{ + if (m_stackState >= ParsedAndImported) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set viaIR before parsing.")); + m_viaIR = _viaIR; +} + void CompilerStack::setEVMVersion(langutil::EVMVersion _version) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set EVM version before parsing.")); m_evmVersion = _version; } +void CompilerStack::setModelCheckerSettings(ModelCheckerSettings _settings) +{ + if (m_stackState >= ParsedAndImported) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set model checking settings before parsing.")); + m_modelCheckerSettings = _settings; +} + void CompilerStack::setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSMTSolvers) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set enabled SMT solvers before parsing.")); m_enabledSMTSolvers = _enabledSMTSolvers; } void CompilerStack::setLibraries(std::map const& _libraries) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set libraries before parsing.")); m_libraries = _libraries; } @@ -161,14 +176,14 @@ void CompilerStack::setOptimiserSettings(bool _optimize, unsigned _runs) void CompilerStack::setOptimiserSettings(OptimiserSettings _settings) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing.")); m_optimiserSettings = std::move(_settings); } void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set revert string settings before parsing.")); solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug, ""); m_revertStrings = _revertStrings; @@ -176,21 +191,21 @@ void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings) void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set use literal sources before parsing.")); m_metadataLiteralSources = _metadataLiteralSources; } void CompilerStack::setMetadataHash(MetadataHash _metadataHash) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set metadata hash before parsing.")); m_metadataHash = _metadataHash; } void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response) { - if (m_stackState >= ParsingPerformed) + if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must add SMTLib2 responses before parsing.")); m_smtlib2Responses[_hash] = _response; } @@ -206,7 +221,9 @@ void CompilerStack::reset(bool _keepSettings) { m_remappings.clear(); m_libraries.clear(); + m_viaIR = false; m_evmVersion = langutil::EVMVersion(); + m_modelCheckerSettings = ModelCheckerSettings{}; m_enabledSMTSolvers = smtutil::SMTSolverChoice::All(); m_generateIR = false; m_generateEwasm = false; @@ -214,6 +231,7 @@ void CompilerStack::reset(bool _keepSettings) m_optimiserSettings = OptimiserSettings::minimal(); m_metadataLiteralSources = false; m_metadataHash = MetadataHash::IPFS; + m_stopAfter = State::CompilationSuccessful; } m_globalContext.reset(); m_sourceOrder.clear(); @@ -247,6 +265,7 @@ bool CompilerStack::parse() vector sourcesToParse; for (auto const& s: m_sources) sourcesToParse.push_back(s.first); + for (size_t i = 0; i < sourcesToParse.size(); ++i) { string const& path = sourcesToParse[i]; @@ -258,19 +277,26 @@ bool CompilerStack::parse() else { source.ast->annotation().path = path; - for (auto const& newSource: loadMissingSources(*source.ast, path)) - { - string const& newPath = newSource.first; - string const& newContents = newSource.second; - m_sources[newPath].scanner = make_shared(CharStream(newContents, newPath)); - sourcesToParse.push_back(newPath); - } + if (m_stopAfter >= ParsedAndImported) + for (auto const& newSource: loadMissingSources(*source.ast, path)) + { + string const& newPath = newSource.first; + string const& newContents = newSource.second; + m_sources[newPath].scanner = make_shared(CharStream(newContents, newPath)); + sourcesToParse.push_back(newPath); + } } } - m_stackState = ParsingPerformed; + if (m_stopAfter <= Parsed) + m_stackState = Parsed; + else + m_stackState = ParsedAndImported; if (!Error::containsOnlyWarnings(m_errorReporter.errors())) m_hasError = true; + + storeContractDefinitions(); + return !m_hasError; } @@ -290,16 +316,22 @@ void CompilerStack::importASTs(map const& _sources) source.scanner = scanner; m_sources[path] = source; } - m_stackState = ParsingPerformed; + m_stackState = ParsedAndImported; m_importedSources = true; + + storeContractDefinitions(); } bool CompilerStack::analyze() { - if (m_stackState != ParsingPerformed || m_stackState >= AnalysisPerformed) + if (m_stackState != ParsedAndImported || m_stackState >= AnalysisPerformed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed.")); resolveImports(); + for (Source const* source: m_sourceOrder) + if (source->ast) + Scoper::assignScopes(*source->ast); + bool noErrors = true; try @@ -328,30 +360,12 @@ bool CompilerStack::analyze() if (source->ast && !resolver.performImports(*source->ast, sourceUnitsByName)) return false; + resolver.warnHomonymDeclarations(); + for (Source const* source: m_sourceOrder) if (source->ast && !resolver.resolveNamesAndTypes(*source->ast)) return false; - // Store contract definitions. - for (Source const* source: m_sourceOrder) - if (source->ast) - for ( - ContractDefinition const* contract: - ASTNode::filteredNodes(source->ast->nodes()) - ) - { - // Note that we now reference contracts by their fully qualified names, and - // thus contracts can only conflict if declared in the same source file. This - // should already cause a double-declaration error elsewhere. - if (!m_contracts.count(contract->fullyQualifiedName())) - m_contracts[contract->fullyQualifiedName()].contract = contract; - else - solAssert( - m_errorReporter.hasErrors(), - "Contract already present (name clash?), but no error was reported." - ); - } - DeclarationTypeChecker declarationTypeChecker(m_errorReporter, m_evmVersion); for (Source const* source: m_sourceOrder) if (source->ast && !declarationTypeChecker.check(*source->ast)) @@ -362,12 +376,10 @@ bool CompilerStack::analyze() // This also calculates whether a contract is abstract, which is needed by the // type checker. ContractLevelChecker contractLevelChecker(m_errorReporter); + for (Source const* source: m_sourceOrder) - if (source->ast) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (!contractLevelChecker.check(*contract)) - noErrors = false; + if (auto sourceAst = source->ast) + noErrors = contractLevelChecker.check(*sourceAst); // Requires ContractLevelChecker DocStringAnalyser docStringAnalyser(m_errorReporter); @@ -394,6 +406,8 @@ bool CompilerStack::analyze() for (Source const* source: m_sourceOrder) if (source->ast && !postTypeChecker.check(*source->ast)) noErrors = false; + if (!postTypeChecker.finalize()) + noErrors = false; } // Check that immutable variables are never read in c'tors and assigned @@ -446,7 +460,7 @@ bool CompilerStack::analyze() if (noErrors) { - ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_readFile, m_enabledSMTSolvers); + ModelChecker modelChecker(m_errorReporter, m_smtlib2Responses, m_modelCheckerSettings, m_readFile, m_enabledSMTSolvers); for (Source const* source: m_sourceOrder) if (source->ast) modelChecker.analyze(*source->ast); @@ -467,9 +481,13 @@ bool CompilerStack::analyze() return !m_hasError; } -bool CompilerStack::parseAndAnalyze() +bool CompilerStack::parseAndAnalyze(State _stopAfter) { + m_stopAfter = _stopAfter; + bool success = parse(); + if (m_stackState >= m_stopAfter) + return success; if (success || m_parserErrorRecovery) success = analyze(); return success; @@ -500,17 +518,22 @@ bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) con return false; } -bool CompilerStack::compile() +bool CompilerStack::compile(State _stopAfter) { + m_stopAfter = _stopAfter; if (m_stackState < AnalysisPerformed) - if (!parseAndAnalyze()) + if (!parseAndAnalyze(_stopAfter)) return false; + if (m_stackState >= m_stopAfter) + return true; + if (m_hasError) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors.")); // Only compile contracts individually which have been requested. map> otherCompilers; + for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (auto contract = dynamic_cast(node.get())) @@ -518,7 +541,17 @@ bool CompilerStack::compile() { try { - compileContract(*contract, otherCompilers); + if (m_viaIR || m_generateIR || m_generateEwasm) + generateIR(*contract); + if (m_generateEvmBytecode) + { + if (m_viaIR) + generateEVMFromIR(*contract); + else + compileContract(*contract, otherCompilers); + } + if (m_generateEwasm) + generateEwasm(*contract); } catch (Error const& _error) { @@ -527,10 +560,28 @@ bool CompilerStack::compile() m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what()); return false; } - if (m_generateIR || m_generateEwasm) - generateIR(*contract); - if (m_generateEwasm) - generateEwasm(*contract); + catch (UnimplementedFeatureError const& _unimplementedError) + { + if ( + SourceLocation const* sourceLocation = + boost::get_error_info(_unimplementedError) + ) + { + string const* comment = _unimplementedError.comment(); + m_errorReporter.error( + 1834_error, + Error::Type::CodeGenerationError, + *sourceLocation, + "Unimplemented feature error" + + ((comment && !comment->empty()) ? ": " + *comment : string{}) + + " in " + + _unimplementedError.lineInfo() + ); + return false; + } + else + throw; + } } m_stackState = CompilationSuccessful; this->link(); @@ -549,7 +600,7 @@ void CompilerStack::link() vector CompilerStack::contractNames() const { - if (m_stackState < AnalysisPerformed) + if (m_stackState < Parsed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); vector contractNames; for (auto const& contract: m_contracts) @@ -576,7 +627,7 @@ evmasm::AssemblyItems const* CompilerStack::assemblyItems(string const& _contrac BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); Contract const& currentContract = contract(_contractName); - return currentContract.compiler ? &contract(_contractName).compiler->assemblyItems() : nullptr; + return currentContract.evmAssembly ? ¤tContract.evmAssembly->items() : nullptr; } evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _contractName) const @@ -585,7 +636,49 @@ evmasm::AssemblyItems const* CompilerStack::runtimeAssemblyItems(string const& _ BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); Contract const& currentContract = contract(_contractName); - return currentContract.compiler ? &contract(_contractName).compiler->runtimeAssemblyItems() : nullptr; + return currentContract.evmRuntimeAssembly ? ¤tContract.evmRuntimeAssembly->items() : nullptr; +} + +Json::Value CompilerStack::generatedSources(string const& _contractName, bool _runtime) const +{ + if (m_stackState != CompilationSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); + + Contract const& c = contract(_contractName); + util::LazyInit const& sources = + _runtime ? + c.runtimeGeneratedSources : + c.generatedSources; + return sources.init([&]{ + Json::Value sources{Json::arrayValue}; + // If there is no compiler, then no bytecode was generated and thus no + // sources were generated. + if (c.compiler) + { + string source = + _runtime ? + c.compiler->runtimeGeneratedYulUtilityCode() : + c.compiler->generatedYulUtilityCode(); + if (!source.empty()) + { + string sourceName = CompilerContext::yulUtilityFileName(); + unsigned sourceIndex = sourceIndices()[sourceName]; + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared(langutil::CharStream(source, sourceName)); + yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); + shared_ptr parserResult = yul::Parser{errorReporter, dialect}.parse(scanner, false); + solAssert(parserResult, ""); + sources[0]["ast"] = yul::AsmJsonConverter{sourceIndex}(*parserResult); + sources[0]["name"] = sourceName; + sources[0]["id"] = sourceIndex; + sources[0]["language"] = "Yul"; + sources[0]["contents"] = move(source); + + } + } + return sources; + }); } string const* CompilerStack::sourceMapping(string const& _contractName) const @@ -697,8 +790,8 @@ string CompilerStack::assemblyString(string const& _contractName, StringMap _sou BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); Contract const& currentContract = contract(_contractName); - if (currentContract.compiler) - return currentContract.compiler->assemblyString(_sourceCodes); + if (currentContract.evmAssembly) + return currentContract.evmAssembly->assemblyString(_sourceCodes); else return string(); } @@ -710,8 +803,8 @@ Json::Value CompilerStack::assemblyJSON(string const& _contractName) const BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); Contract const& currentContract = contract(_contractName); - if (currentContract.compiler) - return currentContract.compiler->assemblyJSON(sourceIndices()); + if (currentContract.evmAssembly) + return currentContract.evmAssembly->assemblyJSON(sourceIndices()); else return Json::Value(); } @@ -730,6 +823,8 @@ map CompilerStack::sourceIndices() const unsigned index = 0; for (auto const& s: m_sources) indices[s.first] = index++; + solAssert(!indices.count(CompilerContext::yulUtilityFileName()), ""); + indices[CompilerContext::yulUtilityFileName()] = index++; return indices; } @@ -824,6 +919,14 @@ string const& CompilerStack::metadata(string const& _contractName) const return metadata(contract(_contractName)); } +bytes CompilerStack::cborMetadata(string const& _contractName) const +{ + if (m_stackState < AnalysisPerformed) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); + + return createCBORMetadata(contract(_contractName)); +} + string const& CompilerStack::metadata(Contract const& _contract) const { if (m_stackState < AnalysisPerformed) @@ -844,7 +947,7 @@ Scanner const& CompilerStack::scanner(string const& _sourceName) const SourceUnit const& CompilerStack::ast(string const& _sourceName) const { - if (m_stackState < ParsingPerformed) + if (m_stackState < Parsed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing not yet performed.")); if (!source(_sourceName).ast && !m_parserErrorRecovery) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); @@ -874,7 +977,7 @@ size_t CompilerStack::functionEntryPoint( evmasm::AssemblyItem tag = compiler->functionEntryLabel(_function); if (tag.type() == evmasm::UndefinedItem) return 0; - evmasm::AssemblyItems const& items = compiler->runtimeAssemblyItems(); + evmasm::AssemblyItems const& items = compiler->runtimeAssembly().items(); for (size_t i = 0; i < items.size(); ++i) if (items.at(i).type() == evmasm::Tag && items.at(i).data() == tag.data()) return i; @@ -917,7 +1020,7 @@ string const& CompilerStack::Source::ipfsUrl() const StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath) { - solAssert(m_stackState < ParsingPerformed, ""); + solAssert(m_stackState < ParsedAndImported, ""); StringMap newSources; try { @@ -961,7 +1064,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string string CompilerStack::applyRemapping(string const& _path, string const& _context) { - solAssert(m_stackState < ParsingPerformed, ""); + solAssert(m_stackState < ParsedAndImported, ""); // Try to find the longest prefix match in all remappings that are active in the current context. auto isPrefixOf = [](string const& _a, string const& _b) { @@ -1003,7 +1106,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context void CompilerStack::resolveImports() { - solAssert(m_stackState == ParsingPerformed, ""); + solAssert(m_stackState == ParsedAndImported, ""); // topological sorting (depth first search) of the import graph, cutting potential cycles vector sourceOrder; @@ -1018,7 +1121,7 @@ void CompilerStack::resolveImports() for (ASTPointer const& node: _source->ast->nodes()) if (ImportDirective const* import = dynamic_cast(node.get())) { - string const& path = import->annotation().absolutePath; + string const& path = *import->annotation().absolutePath; solAssert(m_sources.count(path), ""); import->annotation().sourceUnit = m_sources[path].ast.get(); toposort(&m_sources[path]); @@ -1033,6 +1136,24 @@ void CompilerStack::resolveImports() swap(m_sourceOrder, sourceOrder); } +void CompilerStack::storeContractDefinitions() +{ + for (auto const& pair: m_sources) + if (pair.second.ast) + for ( + ContractDefinition const* contract: + ASTNode::filteredNodes(pair.second.ast->nodes()) + ) + { + string fullyQualifiedName = *pair.second.ast->annotation().path + ":" + contract->name(); + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // should already cause a double-declaration error elsewhere. + if (!m_contracts.count(fullyQualifiedName)) + m_contracts[fullyQualifiedName].contract = contract; + } +} + namespace { bool onlySafeExperimentalFeaturesActivated(set const& features) @@ -1053,20 +1174,21 @@ void CompilerStack::compileContract( if (m_hasError) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors.")); - if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed()) + if (_otherCompilers.count(&_contract)) return; + for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _otherCompilers); + if (!_contract.canBeDeployed()) + return; + Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); shared_ptr compiler = make_shared(m_evmVersion, m_revertStrings, m_optimiserSettings); compiledContract.compiler = compiler; - bytes cborEncodedMetadata = createCBORMetadata( - metadata(compiledContract), - !onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures) - ); + bytes cborEncodedMetadata = createCBORMetadata(compiledContract); try { @@ -1078,20 +1200,25 @@ void CompilerStack::compileContract( solAssert(false, "Optimizer exception during compilation"); } + compiledContract.evmAssembly = compiler->assemblyPtr(); + solAssert(compiledContract.evmAssembly, ""); try { // Assemble deployment (incl. runtime) object. - compiledContract.object = compiler->assembledObject(); + compiledContract.object = compiledContract.evmAssembly->assemble(); } catch(evmasm::AssemblyException const&) { solAssert(false, "Assembly exception for bytecode"); } + solAssert(compiledContract.object.immutableReferences.empty(), "Leftover immutables."); + compiledContract.evmRuntimeAssembly = compiler->runtimeAssemblyPtr(); + solAssert(compiledContract.evmRuntimeAssembly, ""); try { // Assemble runtime object. - compiledContract.runtimeObject = compiler->runtimeObject(); + compiledContract.runtimeObject = compiledContract.evmRuntimeAssembly->assemble(); } catch(evmasm::AssemblyException const&) { @@ -1123,32 +1250,72 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) if (m_hasError) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateIR with errors.")); - if (!_contract.canBeDeployed()) - return; - - map otherYulSources; - Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); if (!compiledContract.yulIR.empty()) return; + if (!*_contract.sourceUnit().annotation().useABICoderV2) + m_errorReporter.warning( + 2066_error, + _contract.location(), + "Contract requests the ABI coder v1, which is incompatible with the IR. " + "Using ABI coder v2 instead." + ); + string dependenciesSource; for (auto const* dependency: _contract.annotation().contractDependencies) - { generateIR(*dependency); - otherYulSources.emplace(dependency, m_contracts.at(dependency->fullyQualifiedName()).yulIR); - } + + if (!_contract.canBeDeployed()) + return; + + map otherYulSources; + for (auto const& pair: m_contracts) + otherYulSources.emplace(pair.second.contract, pair.second.yulIR); IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings); tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources); } +void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) +{ + solAssert(m_stackState >= AnalysisPerformed, ""); + if (m_hasError) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEVMFromIR with errors.")); + + if (!_contract.canBeDeployed()) + return; + + Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); + solAssert(!compiledContract.yulIROptimized.empty(), ""); + if (!compiledContract.object.bytecode.empty()) + return; + + // Re-parse the Yul IR in EVM dialect + yul::AssemblyStack stack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings); + stack.parseAndAnalyze("", compiledContract.yulIROptimized); + stack.optimize(); + + //cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl; + + // TODO: support passing metadata + auto result = stack.assemble(yul::AssemblyStack::Machine::EVM); + compiledContract.object = std::move(*result.bytecode); + // TODO: support runtimeObject + // TODO: add EIP-170 size check for runtimeObject + // TODO: refactor assemblyItems, runtimeAssemblyItems, generatedSources, + // assemblyString, assemblyJSON, and functionEntryPoints to work with this code path +} + void CompilerStack::generateEwasm(ContractDefinition const& _contract) { solAssert(m_stackState >= AnalysisPerformed, ""); if (m_hasError) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called generateEwasm with errors.")); + if (!_contract.canBeDeployed()) + return; + Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); solAssert(!compiledContract.yulIROptimized.empty(), ""); if (!compiledContract.ewasm.empty()) @@ -1219,9 +1386,9 @@ string CompilerStack::createMetadata(Contract const& _contract) const /// All the source files (including self), which should be included in the metadata. set referencedSources; - referencedSources.insert(_contract.contract->sourceUnit().annotation().path); + referencedSources.insert(*_contract.contract->sourceUnit().annotation().path); for (auto const sourceUnit: _contract.contract->sourceUnit().referencedSourceUnits(true)) - referencedSources.insert(sourceUnit->annotation().path); + referencedSources.insert(*sourceUnit->annotation().path); meta["sources"] = Json::objectValue; for (auto const& s: m_sources) @@ -1285,9 +1452,11 @@ string CompilerStack::createMetadata(Contract const& _contract) const static vector hashes{"ipfs", "bzzr1", "none"}; meta["settings"]["metadata"]["bytecodeHash"] = hashes.at(unsigned(m_metadataHash)); + if (m_viaIR) + meta["settings"]["viaIR"] = m_viaIR; meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = - _contract.contract->annotation().canonicalName; + *_contract.contract->annotation().canonicalName; meta["settings"]["remappings"] = Json::arrayValue; set remappings; @@ -1333,7 +1502,7 @@ class MetadataCBOREncoder bytes serialise() const { - unsigned size = m_data.size() + 1; + size_t size = m_data.size() + 1; solAssert(size <= 0xffff, "Metadata too large."); solAssert(m_entryCount <= 0x1f, "Too many map entries."); @@ -1349,7 +1518,7 @@ class MetadataCBOREncoder private: void pushTextString(string const& key) { - unsigned length = key.size(); + size_t length = key.size(); if (length < 24) { m_data += bytes{static_cast(0x60 + length)}; @@ -1365,7 +1534,7 @@ class MetadataCBOREncoder } void pushByteString(bytes const& key) { - unsigned length = key.size(); + size_t length = key.size(); if (length < 24) { m_data += bytes{static_cast(0x40 + length)}; @@ -1390,18 +1559,24 @@ class MetadataCBOREncoder bytes m_data; }; -bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimentalMode) +bytes CompilerStack::createCBORMetadata(Contract const& _contract) const { + bool const experimentalMode = !onlySafeExperimentalFeaturesActivated( + _contract.contract->sourceUnit().annotation().experimentalFeatures + ); + + string meta = metadata(_contract); + MetadataCBOREncoder encoder; if (m_metadataHash == MetadataHash::IPFS) - encoder.pushBytes("ipfs", util::ipfsHash(_metadata)); + encoder.pushBytes("ipfs", util::ipfsHash(meta)); else if (m_metadataHash == MetadataHash::Bzzr1) - encoder.pushBytes("tron", util::bzzr1Hash(_metadata).asBytes()); + encoder.pushBytes("tron", util::bzzr1Hash(meta).asBytes()); else solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); - if (_experimentalMode) + if (experimentalMode || m_viaIR) encoder.pushBool("experimental", true); if (m_release) encoder.pushBytes("solc", VersionCompactBytes); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index ce0f949b9ef5..631f574cda89 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -29,6 +29,8 @@ #include #include +#include + #include #include @@ -90,7 +92,8 @@ class CompilerStack: boost::noncopyable enum State { Empty, SourcesSet, - ParsingPerformed, + Parsed, + ParsedAndImported, AnalysisPerformed, CompilationSuccessful }; @@ -159,11 +162,17 @@ class CompilerStack: boost::noncopyable m_parserErrorRecovery = _wantErrorRecovery; } + /// Sets the pipeline to go through the Yul IR or not. + /// Must be set before parsing. + void setViaIR(bool _viaIR); + /// Set the EVM version used before running compile. /// When called without an argument it will revert to the default version. /// Must be set before parsing. void setEVMVersion(langutil::EVMVersion _version = langutil::EVMVersion{}); + /// Set model checker settings. + void setModelCheckerSettings(ModelCheckerSettings _settings); /// Set which SMT solvers should be enabled. void setSMTSolverChoice(smtutil::SMTSolverChoice _enabledSolvers); @@ -176,6 +185,9 @@ class CompilerStack: boost::noncopyable m_requestedContractNames = _contractNames; } + /// Enable EVM Bytecode generation. This is enabled by default. + void enableEvmBytecodeGeneration(bool _enable = true) { m_generateEvmBytecode = _enable; } + /// Enable experimental generation of Yul IR code. void enableIRGeneration(bool _enable = true) { m_generateIR = _enable; } @@ -213,11 +225,11 @@ class CompilerStack: boost::noncopyable /// Parses and analyzes all source units that were added /// @returns false on error. - bool parseAndAnalyze(); + bool parseAndAnalyze(State _stopAfter = State::CompilationSuccessful); /// Compiles the source units that were previously added and parsed. /// @returns false on error. - bool compile(); + bool compile(State _stopAfter = State::CompilationSuccessful); /// @returns the list of sources (paths) used std::vector sourceNames() const; @@ -274,6 +286,10 @@ class CompilerStack: boost::noncopyable /// @returns runtime contract assembly items evmasm::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName) const; + /// @returns an array containing all utility sources generated during compilation. + /// Format: [ { name: string, id: number, language: "Yul", contents: string }, ... ] + Json::Value generatedSources(std::string const& _contractName, bool _runtime = false) const; + /// @returns the string that provides a mapping between bytecode and sourcecode or a nullptr /// if the contract does not (yet) have bytecode. std::string const* sourceMapping(std::string const& _contractName) const; @@ -314,6 +330,9 @@ class CompilerStack: boost::noncopyable /// @returns the Contract Metadata std::string const& metadata(std::string const& _contractName) const; + /// @returns the cbor-encoded metadata. + bytes cborMetadata(std::string const& _contractName) const; + /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions Json::Value gasEstimates(std::string const& _contractName) const; @@ -339,6 +358,8 @@ class CompilerStack: boost::noncopyable { ContractDefinition const* contract = nullptr; std::shared_ptr compiler; + std::shared_ptr evmAssembly; + std::shared_ptr evmRuntimeAssembly; evmasm::LinkerObject object; ///< Deployment object (includes the runtime sub-object). evmasm::LinkerObject runtimeObject; ///< Runtime object. std::string yulIR; ///< Experimental Yul IR code. @@ -350,6 +371,8 @@ class CompilerStack: boost::noncopyable util::LazyInit storageLayout; util::LazyInit userDocumentation; util::LazyInit devDocumentation; + util::LazyInit generatedSources; + util::LazyInit runtimeGeneratedSources; mutable std::optional sourceMapping; mutable std::optional runtimeSourceMapping; }; @@ -361,6 +384,9 @@ class CompilerStack: boost::noncopyable std::string applyRemapping(std::string const& _path, std::string const& _context); void resolveImports(); + /// Store the contract definitions in m_contracts. + void storeContractDefinitions(); + /// @returns true if the source is requested to be compiled. bool isRequestedSource(std::string const& _sourceName) const; @@ -379,7 +405,12 @@ class CompilerStack: boost::noncopyable /// The IR is stored but otherwise unused. void generateIR(ContractDefinition const& _contract); + /// Generate EVM representation for a single contract. + /// Depends on output generated by generateIR. + void generateEVMFromIR(ContractDefinition const& _contract); + /// Generate Ewasm representation for a single contract. + /// Depends on output generated by generateIR. void generateEwasm(ContractDefinition const& _contract); /// Links all the known library addresses in the available objects. Any unknown @@ -402,7 +433,7 @@ class CompilerStack: boost::noncopyable std::string createMetadata(Contract const& _contract) const; /// @returns the metadata CBOR for the given serialised metadata JSON. - bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode); + bytes createCBORMetadata(Contract const& _contract) const; /// @returns the contract ABI as a JSON object. /// This will generate the JSON object and store it in the Contract object if it is not present yet. @@ -434,11 +465,15 @@ class CompilerStack: boost::noncopyable ReadCallback::Callback m_readFile; OptimiserSettings m_optimiserSettings; RevertStrings m_revertStrings = RevertStrings::Default; + State m_stopAfter = State::CompilationSuccessful; + bool m_viaIR = false; langutil::EVMVersion m_evmVersion; + ModelCheckerSettings m_modelCheckerSettings; smtutil::SMTSolverChoice m_enabledSMTSolvers; std::map> m_requestedContractNames; - bool m_generateIR; - bool m_generateEwasm; + bool m_generateEvmBytecode = true; + bool m_generateIR = false; + bool m_generateEwasm = false; std::map m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum /// "context:prefix=target" diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 2b56977ff848..8b7e79840e81 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -42,93 +42,6 @@ using namespace solidity::evmasm; using namespace solidity::frontend; using namespace solidity::langutil; -GasEstimator::ASTGasConsumptionSelfAccumulated GasEstimator::structuralEstimation( - AssemblyItems const& _items, - vector const& _ast -) const -{ - solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); - map particularCosts; - - ControlFlowGraph cfg(_items); - for (BasicBlock const& block: cfg.optimisedBlocks()) - { - solAssert(!!block.startState, ""); - GasMeter meter(block.startState->copy(), m_evmVersion); - auto const end = _items.begin() + static_cast(block.end); - for (auto iter = _items.begin() + static_cast(block.begin); iter != end; ++iter) - particularCosts[iter->location()] += meter.estimateMax(*iter); - } - - set finestNodes = finestNodesAtLocation(_ast); - ASTGasConsumptionSelfAccumulated gasCosts; - auto onNode = [&](ASTNode const& _node) - { - if (!finestNodes.count(&_node)) - return true; - gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.location()]; - return true; - }; - auto onEdge = [&](ASTNode const& _parent, ASTNode const& _child) - { - gasCosts[&_parent][1] += gasCosts[&_child][1]; - }; - ASTReduce folder(onNode, onEdge); - for (ASTNode const* ast: _ast) - ast->accept(folder); - - return gasCosts; -} - -map GasEstimator::breakToStatementLevel( - ASTGasConsumptionSelfAccumulated const& _gasCosts, - vector const& _roots -) -{ - solAssert(std::count(_roots.begin(), _roots.end(), nullptr) == 0, ""); - // first pass: statementDepth[node] is the distance from the deepend statement to node - // in direction of the tree root (or undefined if not possible) - map statementDepth; - auto onNodeFirstPass = [&](ASTNode const& _node) - { - if (dynamic_cast(&_node)) - statementDepth[&_node] = 0; - return true; - }; - auto onEdgeFirstPass = [&](ASTNode const& _parent, ASTNode const& _child) - { - if (statementDepth.count(&_child)) - statementDepth[&_parent] = max(statementDepth[&_parent], statementDepth[&_child] + 1); - }; - ASTReduce firstPass(onNodeFirstPass, onEdgeFirstPass); - for (ASTNode const* node: _roots) - node->accept(firstPass); - - // we use the location of a node if - // - its statement depth is 0 or - // - its statement depth is undefined but the parent's statement depth is at least 1 - map gasCosts; - auto onNodeSecondPass = [&](ASTNode const& _node) - { - return statementDepth.count(&_node); - }; - auto onEdgeSecondPass = [&](ASTNode const& _parent, ASTNode const& _child) - { - bool useNode = false; - if (statementDepth.count(&_child)) - useNode = statementDepth[&_child] == 0; - else - useNode = statementDepth.count(&_parent) && statementDepth.at(&_parent) > 0; - if (useNode) - gasCosts[&_child] = _gasCosts.at(&_child)[1]; - }; - ASTReduce secondPass(onNodeSecondPass, onEdgeSecondPass); - for (ASTNode const* node: _roots) - node->accept(secondPass); - // gasCosts should only contain non-overlapping locations - return gasCosts; -} - GasEstimator::GasConsumption GasEstimator::functionalEstimation( AssemblyItems const& _items, string const& _signature diff --git a/libsolidity/interface/GasEstimator.h b/libsolidity/interface/GasEstimator.h index f814c8b8d2bf..5f564cb0ace7 100644 --- a/libsolidity/interface/GasEstimator.h +++ b/libsolidity/interface/GasEstimator.h @@ -48,22 +48,6 @@ struct GasEstimator explicit GasEstimator(langutil::EVMVersion _evmVersion): m_evmVersion(_evmVersion) {} - /// Estimates the gas consumption for every assembly item in the given assembly and stores - /// it by source location. - /// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs. - ASTGasConsumptionSelfAccumulated structuralEstimation( - evmasm::AssemblyItems const& _items, - std::vector const& _ast - ) const; - /// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that - /// the following source locations are part of the mapping: - /// 1. source locations of statements that do not contain other statements - /// 2. maximal source locations that do not overlap locations coming from the first rule - static ASTGasConsumption breakToStatementLevel( - ASTGasConsumptionSelfAccumulated const& _gasCosts, - std::vector const& _roots - ); - /// @returns the estimated gas consumption by the (public or external) function with the /// given signature. If no signature is given, estimates the maximum gas usage. GasConsumption functionalEstimation( diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h index 67007fc4c54c..4b1cd7c4ee56 100644 --- a/libsolidity/interface/OptimiserSettings.h +++ b/libsolidity/interface/OptimiserSettings.h @@ -41,7 +41,7 @@ struct OptimiserSettings // should have good "compilability" property here. - "eul" // Run functional expression inliner + "Tpeul" // Run functional expression inliner "xarulrul" // Prune a bit more in SSA "xarrcL" // Turn into SSA again and simplify "gvif" // Run full inliner diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index b1e355fabfbc..fe23c7b919a1 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -117,6 +117,7 @@ Json::Value formatErrorWithException( ) { string message; + // TODO: consider enabling color string formattedMessage = SourceReferenceFormatter::formatExceptionInformation(_exception, _type); if (string const* description = boost::get_error_info(_exception)) @@ -171,16 +172,21 @@ bool hashMatchesContent(string const& _hash, string const& _content) bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesExperimental) { static set experimental{"ir", "irOptimized", "wast", "ewasm", "ewasm.wast"}; - for (auto const& artifact: _outputSelection) - /// @TODO support sub-matching, e.g "evm" matches "evm.assembly" - if (artifact == _artifact) + for (auto const& selectedArtifactJson: _outputSelection) + { + string const& selectedArtifact = selectedArtifactJson.asString(); + if ( + _artifact == selectedArtifact || + boost::algorithm::starts_with(_artifact, selectedArtifact + ".") + ) return true; - else if (artifact == "*") + else if (selectedArtifact == "*") { // "ir", "irOptimized", "wast" and "ewasm.wast" can only be matched by "*" if activated. if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental) return true; } + } return false; } @@ -230,6 +236,16 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil return false; } +/// @returns all artifact names of the EVM object, either for creation or deploy time. +vector evmObjectComponents(string const& _objectKind) +{ + solAssert(_objectKind == "bytecode" || _objectKind == "deployedBytecode", ""); + vector components{"", ".object", ".opcodes", ".sourceMap", ".generatedSources", ".linkReferences"}; + if (_objectKind == "deployedBytecode") + components.push_back(".immutableReferences"); + return util::applyMap(components, [&](auto const& _s) { return "evm." + _objectKind + _s; }); +} + /// @returns true if any binary was requested, i.e. we actually have to perform compilation. bool isBinaryRequested(Json::Value const& _outputSelection) { @@ -237,17 +253,12 @@ bool isBinaryRequested(Json::Value const& _outputSelection) return false; // This does not include "evm.methodIdentifiers" on purpose! - static vector const outputsThatRequireBinaries{ + static vector const outputsThatRequireBinaries = vector{ "*", "ir", "irOptimized", "wast", "wasm", "ewasm.wast", "ewasm.wasm", - "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", - "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", - "evm.deployedBytecode.immutableReferences", - "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", - "evm.bytecode.linkReferences", "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" - }; + } + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode"); for (auto const& fileRequests: _outputSelection) for (auto const& requests: fileRequests) @@ -257,6 +268,25 @@ bool isBinaryRequested(Json::Value const& _outputSelection) return false; } +/// @returns true if EVM bytecode was requested, i.e. we have to run the old code generator. +bool isEvmBytecodeRequested(Json::Value const& _outputSelection) +{ + if (!_outputSelection.isObject()) + return false; + + static vector const outputsThatRequireEvmBinaries = vector{ + "*", + "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" + } + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode"); + + for (auto const& fileRequests: _outputSelection) + for (auto const& requests: fileRequests) + for (auto const& output: outputsThatRequireEvmBinaries) + if (isArtifactRequested(requests, output, false)) + return true; + return false; +} + /// @returns true if any Ewasm code was requested. Note that as an exception, '*' does not /// yet match "ewasm.wast" or "ewasm" bool isEwasmRequested(Json::Value const& _outputSelection) @@ -299,10 +329,12 @@ Json::Value formatLinkReferences(std::map const& linkRefere for (auto const& ref: linkReferences) { string const& fullname = ref.second; + + // If the link reference does not contain a colon, assume that the file name is missing and + // the whole string represents the library name. size_t colon = fullname.rfind(':'); - solAssert(colon != string::npos, ""); - string file = fullname.substr(0, colon); - string name = fullname.substr(colon + 1); + string file = (colon != string::npos ? fullname.substr(0, colon) : ""); + string name = (colon != string::npos ? fullname.substr(colon + 1) : fullname); Json::Value fileObject = ret.get(file, Json::objectValue); Json::Value libraryArray = fileObject.get(name, Json::arrayValue); @@ -340,15 +372,27 @@ Json::Value formatImmutableReferences(map>> co return ret; } -Json::Value collectEVMObject(evmasm::LinkerObject const& _object, string const* _sourceMap, bool _runtimeObject) +Json::Value collectEVMObject( + evmasm::LinkerObject const& _object, + string const* _sourceMap, + Json::Value _generatedSources, + bool _runtimeObject, + function const& _artifactRequested +) { Json::Value output = Json::objectValue; - output["object"] = _object.toHex(); - output["opcodes"] = evmasm::disassemble(_object.bytecode); - output["sourceMap"] = _sourceMap ? *_sourceMap : ""; - output["linkReferences"] = formatLinkReferences(_object.linkReferences); - if (_runtimeObject) + if (_artifactRequested("object")) + output["object"] = _object.toHex(); + if (_artifactRequested("opcodes")) + output["opcodes"] = evmasm::disassemble(_object.bytecode); + if (_artifactRequested("sourceMap")) + output["sourceMap"] = _sourceMap ? *_sourceMap : ""; + if (_artifactRequested("linkReferences")) + output["linkReferences"] = formatLinkReferences(_object.linkReferences); + if (_runtimeObject && _artifactRequested("immutableReferences")) output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences); + if (_artifactRequested("generatedSources")) + output["generatedSources"] = move(_generatedSources); return output; } @@ -384,10 +428,16 @@ std::optional checkAuxiliaryInputKeys(Json::Value const& _input) std::optional checkSettingsKeys(Json::Value const& _input) { - static set keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "optimizer", "outputSelection", "remappings"}; + static set keys{"parserErrorRecovery", "debug", "evmVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; return checkKeys(_input, keys, "settings"); } +std::optional checkModelCheckerSettingsKeys(Json::Value const& _input) +{ + static set keys{"engine", "timeout"}; + return checkKeys(_input, keys, "modelChecker"); +} + std::optional checkOptimizerKeys(Json::Value const& _input) { static set keys{"details", "enabled", "runs"}; @@ -497,6 +547,7 @@ std::optional checkOutputSelection(Json::Value const& _outputSelect return std::nullopt; } + /// Validates the optimizer settings and returns them in a parsed object. /// On error returns the json-formatted error message. std::variant parseOptimizerSettings(Json::Value const& _jsonInput) @@ -694,6 +745,17 @@ std::variant StandardCompiler: if (auto result = checkSettingsKeys(settings)) return *result; + if (settings.isMember("stopAfter")) + { + if (!settings["stopAfter"].isString()) + return formatFatalError("JSONError", "\"settings.stopAfter\" must be a string."); + + if (settings["stopAfter"].asString() != "parsing") + return formatFatalError("JSONError", "Invalid value for \"settings.stopAfter\". Only valid value is \"parsing\"."); + + ret.stopAfter = CompilerStack::State::Parsed; + } + if (settings.isMember("parserErrorRecovery")) { if (!settings["parserErrorRecovery"].isBool()) @@ -701,6 +763,13 @@ std::variant StandardCompiler: ret.parserErrorRecovery = settings["parserErrorRecovery"].asBool(); } + if (settings.isMember("viaIR")) + { + if (!settings["viaIR"].isBool()) + return formatFatalError("JSONError", "\"settings.viaIR\" must be a Boolean."); + ret.viaIR = settings["viaIR"].asBool(); + } + if (settings.isMember("evmVersion")) { if (!settings["evmVersion"].isString()) @@ -782,8 +851,7 @@ std::variant StandardCompiler: try { - // @TODO use libraries only for the given source - ret.libraries[library] = util::h160(address); + ret.libraries[sourceName + ":" + library] = util::h160(address); } catch (util::BadHexCharacter const&) { @@ -819,6 +887,34 @@ std::variant StandardCompiler: ret.outputSelection = std::move(outputSelection); + if (ret.stopAfter != CompilerStack::State::CompilationSuccessful && isBinaryRequested(ret.outputSelection)) + return formatFatalError( + "JSONError", + "Requested output selection conflicts with \"settings.stopAfter\"." + ); + + Json::Value const& modelCheckerSettings = settings.get("modelChecker", Json::Value()); + + if (auto result = checkModelCheckerSettingsKeys(modelCheckerSettings)) + return *result; + + if (modelCheckerSettings.isMember("engine")) + { + if (!modelCheckerSettings["engine"].isString()) + return formatFatalError("JSONError", "settings.modelChecker.engine must be a string."); + std::optional engine = ModelCheckerEngine::fromString(modelCheckerSettings["engine"].asString()); + if (!engine) + return formatFatalError("JSONError", "Invalid model checker engine requested."); + ret.modelCheckerSettings.engine = *engine; + } + + if (modelCheckerSettings.isMember("timeout")) + { + if (!modelCheckerSettings["timeout"].isUInt()) + return formatFatalError("JSONError", "settings.modelChecker.timeout must be an unsigned integer."); + ret.modelCheckerSettings.timeout = modelCheckerSettings["timeout"].asUInt(); + } + return { std::move(ret) }; } @@ -830,6 +926,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting compilerStack.setSources(sourceList); for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses) compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second); + compilerStack.setViaIR(_inputsAndSettings.viaIR); compilerStack.setEVMVersion(_inputsAndSettings.evmVersion); compilerStack.setParserErrorRecovery(_inputsAndSettings.parserErrorRecovery); compilerStack.setRemappings(_inputsAndSettings.remappings); @@ -839,9 +936,10 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources); compilerStack.setMetadataHash(_inputsAndSettings.metadataHash); compilerStack.setRequestedContractNames(requestedContractNames(_inputsAndSettings.outputSelection)); + compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings); + compilerStack.enableEvmBytecodeGeneration(isEvmBytecodeRequested(_inputsAndSettings.outputSelection)); compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection)); - compilerStack.enableEwasmGeneration(isEwasmRequested(_inputsAndSettings.outputSelection)); Json::Value errors = std::move(_inputsAndSettings.errors); @@ -853,7 +951,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting if (binariesRequested) compilerStack.compile(); else - compilerStack.parseAndAnalyze(); + compilerStack.parseAndAnalyze(_inputsAndSettings.stopAfter); for (auto const& error: compilerStack.errors()) { @@ -975,7 +1073,10 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting analysisPerformed = false; /// Inconsistent state - stop here to receive error reports from users - if (((binariesRequested && !compilationSuccess) || !analysisPerformed) && errors.empty()) + if ( + ((binariesRequested && !compilationSuccess) || !analysisPerformed) && + (errors.empty() && _inputsAndSettings.stopAfter >= CompilerStack::State::AnalysisPerformed) + ) return formatFatalError("InternalCompilerError", "No error reported, but compilation failed."); Json::Value output = Json::objectValue; @@ -991,16 +1092,15 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting output["sources"] = Json::objectValue; unsigned sourceIndex = 0; - for (string const& sourceName: analysisPerformed ? compilerStack.sourceNames() : vector()) - { - Json::Value sourceResult = Json::objectValue; - sourceResult["id"] = sourceIndex++; - if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesExperimental)) - sourceResult["ast"] = ASTJsonConverter(false, compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName)); - if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "legacyAST", wildcardMatchesExperimental)) - sourceResult["legacyAST"] = ASTJsonConverter(true, compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName)); - output["sources"][sourceName] = sourceResult; - } + if (compilerStack.state() >= CompilerStack::State::Parsed && (!compilerStack.hasError() || _inputsAndSettings.parserErrorRecovery)) + for (string const& sourceName: compilerStack.sourceNames()) + { + Json::Value sourceResult = Json::objectValue; + sourceResult["id"] = sourceIndex++; + if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "ast", wildcardMatchesExperimental)) + sourceResult["ast"] = ASTJsonConverter(compilerStack.state(), compilerStack.sourceIndices()).toJson(compilerStack.ast(sourceName)); + output["sources"][sourceName] = sourceResult; + } Json::Value contractsOutput = Json::objectValue; for (string const& contractName: analysisPerformed ? compilerStack.contractNames() : vector()) @@ -1050,26 +1150,42 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting _inputsAndSettings.outputSelection, file, name, - { "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" }, + evmObjectComponents("bytecode"), wildcardMatchesExperimental )) evmData["bytecode"] = collectEVMObject( compilerStack.object(contractName), compilerStack.sourceMapping(contractName), - false + compilerStack.generatedSources(contractName), + false, + [&](string const& _element) { return isArtifactRequested( + _inputsAndSettings.outputSelection, + file, + name, + "evm.bytecode." + _element, + wildcardMatchesExperimental + ); } ); if (compilationSuccess && isArtifactRequested( _inputsAndSettings.outputSelection, file, name, - { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", "evm.deployedBytecode.immutableReferences" }, + evmObjectComponents("deployedBytecode"), wildcardMatchesExperimental )) evmData["deployedBytecode"] = collectEVMObject( compilerStack.runtimeObject(contractName), compilerStack.runtimeSourceMapping(contractName), - true + compilerStack.generatedSources(contractName, true), + true, + [&](string const& _element) { return isArtifactRequested( + _inputsAndSettings.outputSelection, + file, + name, + "evm.deployedBytecode." + _element, + wildcardMatchesExperimental + ); } ); if (!evmData.empty()) @@ -1097,8 +1213,6 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) return formatFatalError("JSONError", "Yul mode does not support smtlib2responses."); if (!_inputsAndSettings.remappings.empty()) return formatFatalError("JSONError", "Field \"settings.remappings\" cannot be used for Yul."); - if (!_inputsAndSettings.libraries.empty()) - return formatFatalError("JSONError", "Field \"settings.libraries\" cannot be used for Yul."); if (_inputsAndSettings.revertStrings != RevertStrings::Default) return formatFatalError("JSONError", "Field \"settings.debug.revertStrings\" cannot be used for Yul."); @@ -1151,25 +1265,37 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) MachineAssemblyObject runtimeObject; tie(object, runtimeObject) = stack.assembleAndGuessRuntime(); + if (object.bytecode) + object.bytecode->link(_inputsAndSettings.libraries); + if (runtimeObject.bytecode) + runtimeObject.bytecode->link(_inputsAndSettings.libraries); + for (string const& objectKind: vector{"bytecode", "deployedBytecode"}) - { - auto artifacts = util::applyMap( - vector{"", ".object", ".opcodes", ".sourceMap", ".linkReferences"}, - [&](auto const& _s) { return "evm." + objectKind + _s; } - ); if (isArtifactRequested( _inputsAndSettings.outputSelection, sourceName, contractName, - artifacts, + evmObjectComponents(objectKind), wildcardMatchesExperimental )) { MachineAssemblyObject const& o = objectKind == "bytecode" ? object : runtimeObject; if (o.bytecode) - output["contracts"][sourceName][contractName]["evm"][objectKind] = collectEVMObject(*o.bytecode, o.sourceMappings.get(), false); + output["contracts"][sourceName][contractName]["evm"][objectKind] = + collectEVMObject( + *o.bytecode, + o.sourceMappings.get(), + Json::arrayValue, + false, + [&](string const& _element) { return isArtifactRequested( + _inputsAndSettings.outputSelection, + sourceName, + contractName, + "evm." + objectKind + "." + _element, + wildcardMatchesExperimental + ); } + ); } - } if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental)) output["contracts"][sourceName][contractName]["irOptimized"] = stack.print(); diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index cf5be94a01e7..c474ac721924 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -60,6 +60,7 @@ class StandardCompiler: boost::noncopyable std::string language; Json::Value errors; bool parserErrorRecovery = false; + CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; std::map sources; std::map smtLib2Responses; langutil::EVMVersion evmVersion; @@ -70,6 +71,8 @@ class StandardCompiler: boost::noncopyable bool metadataLiteralSources = false; CompilerStack::MetadataHash metadataHash = CompilerStack::MetadataHash::IPFS; Json::Value outputSelection; + ModelCheckerSettings modelCheckerSettings = ModelCheckerSettings{}; + bool viaIR = false; }; /// Parses the input json (and potentially invokes the read callback) and either returns diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 1059a1ea9cca..ac6652bb8de8 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -107,8 +108,21 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) case Token::Enum: nodes.push_back(parseEnumDefinition()); break; + case Token::Function: + nodes.push_back(parseFunctionDefinition(true)); + break; default: - fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition."); + // Constant variable. + if (variableDeclarationStart() && m_scanner->peekNextToken() != Token::EOS) + { + VarDeclParserOptions options; + options.kind = VarDeclKind::FileLevel; + options.allowInitialValue = true; + nodes.push_back(parseVariableDeclaration(options)); + expectToken(Token::Semicolon); + } + else + fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition."); } } solAssert(m_recursionDepth == 0, ""); @@ -329,15 +343,10 @@ ASTPointer Parser::parseContractDefinition() subNodes.push_back(parseStructDefinition()); else if (currentTokenValue == Token::Enum) subNodes.push_back(parseEnumDefinition()); - else if ( - currentTokenValue == Token::Identifier || - currentTokenValue == Token::Mapping || - TokenTraits::isElementaryTypeName(currentTokenValue) || - (currentTokenValue == Token::Function && m_scanner->peekNextToken() == Token::LParen) - ) + else if (variableDeclarationStart()) { VarDeclParserOptions options; - options.isStateVariable = true; + options.kind = VarDeclKind::State; options.allowInitialValue = true; subNodes.push_back(parseVariableDeclaration(options)); expectToken(Token::Semicolon); @@ -381,7 +390,7 @@ ASTPointer Parser::parseInheritanceSpecifier() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - ASTPointer name(parseUserDefinedTypeName()); + ASTPointer name(parseIdentifierPath()); unique_ptr>> arguments; if (m_scanner->currentToken() == Token::LParen) { @@ -425,7 +434,7 @@ ASTPointer Parser::parseOverrideSpecifier() solAssert(m_scanner->currentToken() == Token::Override, ""); ASTNodeFactory nodeFactory(*this); - std::vector> overrides; + std::vector> overrides; nodeFactory.markEndPosition(); m_scanner->next(); @@ -435,7 +444,7 @@ ASTPointer Parser::parseOverrideSpecifier() m_scanner->next(); while (true) { - overrides.push_back(parseUserDefinedTypeName()); + overrides.push_back(parseIdentifierPath()); if (m_scanner->currentToken() == Token::RParen) break; @@ -548,7 +557,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari return result; } -ASTPointer Parser::parseFunctionDefinition() +ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -607,6 +616,7 @@ ASTPointer Parser::parseFunctionDefinition() name, header.visibility, header.stateMutability, + _freeFunction, kind, header.isVirtual, header.overrides, @@ -663,7 +673,7 @@ ASTPointer Parser::parseEnumDefinition() fatalParserError(1612_error, "Expected identifier after ','"); } if (members.empty()) - parserError(3147_error, "enum with no members is not allowed."); + parserError(3147_error, "Enum with no members is not allowed."); nodeFactory.markEndPosition(); expectToken(Token::RBrace); @@ -683,10 +693,10 @@ ASTPointer Parser::parseVariableDeclaration( ASTPointer type = _lookAheadArrayType ? _lookAheadArrayType : parseTypeName(); nodeFactory.setEndPositionFromNode(type); - if (!_options.isStateVariable && documentation != nullptr) - parserError(2837_error, "Only state variables can have a docstring."); + if (_options.kind == VarDeclKind::Other && documentation != nullptr) + parserError(2837_error, "Only state variables or file-level variables can have a docstring."); - if (dynamic_cast(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace) + if (dynamic_cast(type.get()) && _options.kind == VarDeclKind::State && m_scanner->currentToken() == Token::LBrace) fatalParserError( 2915_error, "Expected a state variable declaration. If you intended this as a fallback function " @@ -704,7 +714,7 @@ ASTPointer Parser::parseVariableDeclaration( while (true) { Token token = m_scanner->currentToken(); - if (_options.isStateVariable && TokenTraits::isVariableVisibilitySpecifier(token)) + if (_options.kind == VarDeclKind::State && TokenTraits::isVariableVisibilitySpecifier(token)) { nodeFactory.markEndPosition(); if (visibility != Visibility::Default) @@ -720,7 +730,7 @@ ASTPointer Parser::parseVariableDeclaration( else visibility = parseVisibilitySpecifier(); } - else if (_options.isStateVariable && token == Token::Override) + else if (_options.kind == VarDeclKind::State && token == Token::Override) { if (overrides) parserError(9125_error, "Override already specified."); @@ -796,7 +806,6 @@ ASTPointer Parser::parseVariableDeclaration( value, visibility, documentation, - _options.isStateVariable, isIndexed, mutability, overrides, @@ -892,7 +901,7 @@ ASTPointer Parser::parseUsingDirective() ASTNodeFactory nodeFactory(*this); expectToken(Token::Using); - ASTPointer library(parseUserDefinedTypeName()); + ASTPointer library(parseIdentifierPath()); ASTPointer typeName; expectToken(Token::For); if (m_scanner->currentToken() == Token::Mul) @@ -908,7 +917,7 @@ ASTPointer Parser::parseModifierInvocation() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - ASTPointer name(parseIdentifier()); + ASTPointer name(parseIdentifierPath()); unique_ptr>> arguments; if (m_scanner->currentToken() == Token::LParen) { @@ -931,6 +940,14 @@ ASTPointer Parser::parseIdentifier() } ASTPointer Parser::parseUserDefinedTypeName() +{ + ASTNodeFactory nodeFactory(*this); + ASTPointer identifierPath = parseIdentifierPath(); + nodeFactory.setEndPositionFromNode(identifierPath); + return nodeFactory.createNode(identifierPath); +} + +ASTPointer Parser::parseIdentifierPath() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -942,7 +959,7 @@ ASTPointer Parser::parseUserDefinedTypeName() nodeFactory.markEndPosition(); identifierPath.push_back(*expectIdentifierToken()); } - return nodeFactory.createNode(identifierPath); + return nodeFactory.createNode(identifierPath); } ASTPointer Parser::parseTypeNameSuffix(ASTPointer type, ASTNodeFactory& nodeFactory) @@ -1046,7 +1063,7 @@ ASTPointer Parser::parseMapping() } else fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type"); - expectToken(Token::Arrow); + expectToken(Token::DoubleArrow); ASTPointer valueType = parseTypeName(); nodeFactory.markEndPosition(); expectToken(Token::RParen); @@ -1080,16 +1097,23 @@ ASTPointer Parser::parseParameterList( return nodeFactory.createNode(parameters); } -ASTPointer Parser::parseBlock(ASTPointer const& _docString) +ASTPointer Parser::parseBlock(bool _allowUnchecked, ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); + bool const unchecked = m_scanner->currentToken() == Token::Unchecked; + if (unchecked) + { + if (!_allowUnchecked) + parserError(5296_error, "\"unchecked\" blocks can only be used inside regular blocks."); + m_scanner->next(); + } expectToken(Token::LBrace); vector> statements; try { while (m_scanner->currentToken() != Token::RBrace) - statements.push_back(parseStatement()); + statements.push_back(parseStatement(true)); nodeFactory.markEndPosition(); } catch (FatalError const&) @@ -1102,14 +1126,14 @@ ASTPointer Parser::parseBlock(ASTPointer const& _docString) BOOST_THROW_EXCEPTION(FatalError()); /* Don't try to recover here. */ m_inParserRecovery = true; } - if (m_parserErrorRecovery) + if (m_inParserRecovery) expectTokenOrConsumeUntil(Token::RBrace, "Block"); else expectToken(Token::RBrace); - return nodeFactory.createNode(_docString, statements); + return nodeFactory.createNode(_docString, unchecked, statements); } -ASTPointer Parser::parseStatement() +ASTPointer Parser::parseStatement(bool _allowUnchecked) { RecursionGuard recursionGuard(*this); ASTPointer docString; @@ -1128,9 +1152,9 @@ ASTPointer Parser::parseStatement() return parseDoWhileStatement(docString); case Token::For: return parseForStatement(docString); + case Token::Unchecked: case Token::LBrace: - return parseBlock(docString); - // starting from here, all statements must be terminated by a semicolon + return parseBlock(_allowUnchecked, docString); case Token::Continue: statement = ASTNodeFactory(*this).createNode(docString); m_scanner->next(); @@ -1598,7 +1622,13 @@ ASTPointer Parser::parseBinaryExpression( { Token op = m_scanner->currentToken(); m_scanner->next(); - ASTPointer right = parseBinaryExpression(precedence + 1); + + static_assert(TokenTraits::hasExpHighestPrecedence(), "Exp does not have the highest precedence"); + + // Parse a**b**c as a**(b**c) + ASTPointer right = (op == Token::Exp) ? + parseBinaryExpression(precedence) : + parseBinaryExpression(precedence + 1); nodeFactory.setEndPositionFromNode(right); expression = nodeFactory.createNode(expression, op, right); } @@ -1928,6 +1958,16 @@ pair>, vector>> Parser::pars return ret; } +bool Parser::variableDeclarationStart() +{ + Token currentToken = m_scanner->currentToken(); + return + currentToken == Token::Identifier || + currentToken == Token::Mapping || + TokenTraits::isElementaryTypeName(currentToken) || + (currentToken == Token::Function && m_scanner->peekNextToken() == Token::LParen); +} + optional Parser::findLicenseString(std::vector> const& _nodes) { // We circumvent the scanner here, because it skips non-docstring comments. @@ -2086,7 +2126,7 @@ ASTPointer Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces vector path; for (auto const& el: _iap.path) path.push_back(dynamic_cast(*el).name()); - type = nodeFactory.createNode(path); + type = nodeFactory.createNode(nodeFactory.createNode(path)); } for (auto const& lengthExpression: _iap.indices) { diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index b769545da189..c5c85d76c667 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -52,13 +52,13 @@ class Parser: public langutil::ParserBase private: class ASTNodeFactory; + enum class VarDeclKind { FileLevel, State, Other }; struct VarDeclParserOptions { // This is actually not needed, but due to a defect in the C++ standard, we have to. // https://stackoverflow.com/questions/17430377 VarDeclParserOptions() {} - - bool isStateVariable = false; + VarDeclKind kind = VarDeclKind::Other; bool allowIndexed = false; bool allowEmptyName = false; bool allowInitialValue = false; @@ -92,7 +92,7 @@ class Parser: public langutil::ParserBase ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); - ASTPointer parseFunctionDefinition(); + ASTPointer parseFunctionDefinition(bool _freeFunction = false); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseEnumValue(); @@ -106,6 +106,7 @@ class Parser: public langutil::ParserBase ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); ASTPointer parseUserDefinedTypeName(); + ASTPointer parseIdentifierPath(); ASTPointer parseTypeNameSuffix(ASTPointer type, ASTNodeFactory& nodeFactory); ASTPointer parseTypeName(); ASTPointer parseFunctionType(); @@ -114,8 +115,8 @@ class Parser: public langutil::ParserBase VarDeclParserOptions const& _options = {}, bool _allowEmpty = true ); - ASTPointer parseBlock(ASTPointer const& _docString = {}); - ASTPointer parseStatement(); + ASTPointer parseBlock(bool _allowUncheckedBlock = false, ASTPointer const& _docString = {}); + ASTPointer parseStatement(bool _allowUncheckedBlock = false); ASTPointer parseInlineAssembly(ASTPointer const& _docString = {}); ASTPointer parseIfStatement(ASTPointer const& _docString); ASTPointer parseTryStatement(ASTPointer const& _docString); @@ -155,6 +156,9 @@ class Parser: public langutil::ParserBase ///@{ ///@name Helper functions + /// @return true if we are at the start of a variable declaration. + bool variableDeclarationStart(); + /// Used as return value of @see peekStatementType. enum class LookAheadInfo { diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index 3672657e8edb..a38df7443722 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -2,6 +2,7 @@ set(sources Algorithms.h AnsiColorized.h Assertions.h + Common.cpp Common.h CommonData.cpp CommonData.h @@ -9,7 +10,9 @@ set(sources CommonIO.h Exceptions.cpp Exceptions.h + ErrorCodes.h FixedHash.h + FunctionSelector.h IndentedWriter.cpp IndentedWriter.h InvertibleMap.h @@ -20,8 +23,10 @@ set(sources Keccak256.cpp Keccak256.h LazyInit.h + LEB128.h picosha2.h Result.h + SetOnce.h StringUtils.cpp StringUtils.h SwarmHash.cpp diff --git a/libsolutil/Common.cpp b/libsolutil/Common.cpp new file mode 100644 index 000000000000..3f77e5d255c6 --- /dev/null +++ b/libsolutil/Common.cpp @@ -0,0 +1,40 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +using namespace solidity; + +bool solidity::fitsPrecisionBaseX(bigint const& _mantissa, double _log2OfBase, uint32_t _exp) +{ + if (_mantissa == 0) + return true; + + solAssert(_mantissa > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantMantissaBit = boost::multiprecision::msb(_mantissa); + if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096 + return false; + + bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1; + return bitsNeeded <= bitsMax; +} diff --git a/libsolutil/Common.h b/libsolutil/Common.h index aae2fa8590cd..659ea2e3e6e0 100644 --- a/libsolutil/Common.h +++ b/libsolutil/Common.h @@ -107,6 +107,10 @@ inline u256 exp256(u256 _base, u256 _exponent) return result; } +/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits, +/// where X is given indirectly via _log2OfBase = log2(X). +bool fitsPrecisionBaseX(bigint const& _mantissa, double _log2OfBase, uint32_t _exp); + inline std::ostream& operator<<(std::ostream& os, bytes const& _bytes) { std::ostringstream ss; diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index b61ba82a2c3c..fa5fb80829d5 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -78,6 +78,7 @@ template std::set& operator+=(std::set& _a, U& _a.insert(std::move(x)); return _a; } + /// Concatenate two vectors of elements. template inline std::vector operator+(std::vector const& _a, std::vector const& _b) @@ -86,6 +87,7 @@ inline std::vector operator+(std::vector const& _a, std::vector const& ret += _b; return ret; } + /// Concatenate two vectors of elements, moving them. template inline std::vector operator+(std::vector&& _a, std::vector&& _b) @@ -97,6 +99,7 @@ inline std::vector operator+(std::vector&& _a, std::vector&& _b) ret += std::move(_b); return ret; } + /// Concatenate something to a sets of elements. template inline std::set operator+(std::set const& _a, U&& _b) @@ -105,6 +108,7 @@ inline std::set operator+(std::set const& _a, U&& _b) ret += std::forward(_b); return ret; } + /// Concatenate something to a sets of elements, move variant. template inline std::set operator+(std::set&& _a, U&& _b) @@ -159,6 +163,22 @@ auto applyMap(Container const& _c, Callable&& _op, OutputContainer _oc = OutputC return _oc; } +/// Filter a vector. +/// Returns a copy of the vector after only taking indices `i` such that `_mask[i]` is true. +template +std::vector filter(std::vector const& _vec, std::vector const& _mask) +{ + assert(_vec.size() == _mask.size()); + + std::vector ret; + + for (size_t i = 0; i < _mask.size(); ++i) + if (_mask[i]) + ret.push_back(_vec[i]); + + return ret; +} + /// Functional fold. /// Given a container @param _c, an initial value @param _acc, /// and a binary operator @param _binaryOp(T, U), accumulate @@ -205,6 +225,13 @@ std::map invertMap(std::map const& originalMap) return inverseMap; } +/// Returns a set of keys of a map. +template +std::set keys(std::map const& _map) +{ + return applyMap(_map, [](auto const& _elem) { return _elem.first; }, std::set{}); +} + // String conversion functions, mainly to/from hex/nibble/byte representations. enum class WhenError diff --git a/libsolutil/CommonIO.cpp b/libsolutil/CommonIO.cpp index 43886264b2b2..3cf83a82655b 100644 --- a/libsolutil/CommonIO.cpp +++ b/libsolutil/CommonIO.cpp @@ -47,8 +47,7 @@ inline T readFile(std::string const& _file) T ret; size_t const c_elementSize = sizeof(typename T::value_type); std::ifstream is(_file, std::ifstream::binary); - if (!is) - return ret; + assertThrow(is, FileNotFound, _file); // get length of file: is.seekg(0, is.end); @@ -58,7 +57,7 @@ inline T readFile(std::string const& _file) is.seekg(0, is.beg); ret.resize((static_cast(length) + c_elementSize - 1) / c_elementSize); - is.read(const_cast(reinterpret_cast(ret.data())), length); + is.read(const_cast(reinterpret_cast(ret.data())), static_cast(length)); return ret; } diff --git a/libsolutil/CommonIO.h b/libsolutil/CommonIO.h index 2a811387bdb9..88f7f9b5c96b 100644 --- a/libsolutil/CommonIO.h +++ b/libsolutil/CommonIO.h @@ -32,7 +32,8 @@ namespace solidity::util { /// Retrieve and returns the contents of the given file as a std::string. -/// If the file doesn't exist or isn't readable, returns an empty container / bytes. +/// If the file doesn't exist, it will throw a FileNotFound exception. +/// If the file is empty, returns an empty string. std::string readFileAsString(std::string const& _file); /// Retrieve and returns the contents of standard input (until EOF). diff --git a/libsolutil/ErrorCodes.h b/libsolutil/ErrorCodes.h new file mode 100644 index 000000000000..86129e3a4c6e --- /dev/null +++ b/libsolutil/ErrorCodes.h @@ -0,0 +1,39 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include + +namespace solidity::util +{ + +enum class PanicCode +{ + Generic = 0x00, // generic / unspecified error. + Assert = 0x01, // generic / unspecified error. Used by assert(). + UnderOverflow = 0x11, // arithmetic underflow or overflow + DivisionByZero = 0x12, // division or modulo by zero + EnumConversionError = 0x21, // enum conversion error + StorageEncodingError = 0x22, // invalid encoding in storage + EmptyArrayPop = 0x31, // empty array pop + ArrayOutOfBounds = 0x32, // array out of bounds access + ResourceError = 0x41, // resource error (too large allocation or too large array) + InvalidInternalFunction = 0x51, // calling invalid internal function +}; + +} diff --git a/libsolutil/Exceptions.h b/libsolutil/Exceptions.h index 9c84c8f71e2b..563c7951c700 100644 --- a/libsolutil/Exceptions.h +++ b/libsolutil/Exceptions.h @@ -48,7 +48,7 @@ struct Exception: virtual std::exception, virtual boost::exception DEV_SIMPLE_EXCEPTION(InvalidAddress); DEV_SIMPLE_EXCEPTION(BadHexCharacter); DEV_SIMPLE_EXCEPTION(BadHexCase); -DEV_SIMPLE_EXCEPTION(FileError); +DEV_SIMPLE_EXCEPTION(FileNotFound); DEV_SIMPLE_EXCEPTION(DataTooLong); DEV_SIMPLE_EXCEPTION(StringTooLong); diff --git a/libsolutil/FixedHash.h b/libsolutil/FixedHash.h index 84d6afba3a19..dffeb7a17beb 100644 --- a/libsolutil/FixedHash.h +++ b/libsolutil/FixedHash.h @@ -48,6 +48,7 @@ class FixedHash /// The size of the container. enum { size = N }; + static_assert(N != 0); /// Method to convert from a string. enum ConstructFromStringType { FromHex, FromBinary }; @@ -59,7 +60,13 @@ class FixedHash explicit FixedHash() { m_data.fill(0); } /// Construct from another hash, filling with zeroes or cropping as necessary. - template explicit FixedHash(FixedHash const& _h, ConstructFromHashType _t = AlignLeft) { m_data.fill(0); unsigned c = std::min(M, N); for (unsigned i = 0; i < c; ++i) m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; } + template explicit FixedHash(FixedHash const& _h, ConstructFromHashType _t = AlignLeft) + { + m_data.fill(0); + unsigned c = std::min(M, N); + for (unsigned i = 0; i < c; ++i) + m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i]; + } /// Convert from the corresponding arithmetic type. FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); } @@ -104,8 +111,10 @@ class FixedHash } } - /// Explicitly construct, copying from a string. - explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : solidity::util::asBytes(_s), _ht) {} + /// Explicitly construct, copying from a string. + explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent): + FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : solidity::util::asBytes(_s), _ht) + {} /// Convert to arithmetic type. operator Arith() const { return fromBigEndian(m_data); } @@ -114,7 +123,16 @@ class FixedHash bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; } /// Required to sort objects of this type or use them as map keys. - bool operator<(FixedHash const& _c) const { for (unsigned i = 0; i < N; ++i) if (m_data[i] < _c.m_data[i]) return true; else if (m_data[i] > _c.m_data[i]) return false; return false; } + bool operator<(FixedHash const& _c) const { + for (unsigned i = 0; i < N; ++i) + { + if (m_data[i] < _c.m_data[i]) + return true; + else if (m_data[i] > _c.m_data[i]) + return false; + } + return false; + } /// @returns a particular byte from the hash. uint8_t& operator[](unsigned _i) { return m_data[_i]; } diff --git a/test/libsolidity/SMTCheckerJSONTest.h b/libsolutil/FunctionSelector.h similarity index 50% rename from test/libsolidity/SMTCheckerJSONTest.h rename to libsolutil/FunctionSelector.h index 6de7f36c9573..da6feb4e0a73 100644 --- a/test/libsolidity/SMTCheckerJSONTest.h +++ b/libsolutil/FunctionSelector.h @@ -18,31 +18,25 @@ #pragma once -#include - -#include +#include +#include #include -namespace solidity::frontend::test +namespace solidity::util { -class SMTCheckerJSONTest: public SyntaxTest +/// @returns the ABI selector for a given function signature, as a 32 bit number. +inline uint32_t selectorFromSignature32(std::string const& _signature) { -public: - static std::unique_ptr create(Config const& _config) - { - return std::make_unique(_config.filename, _config.evmVersion); - } - SMTCheckerJSONTest(std::string const& _filename, langutil::EVMVersion _evmVersion); - - TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; + return uint32_t(FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256(_signature)))); +} -private: - std::vector hashesFromJson(Json::Value const& _jsonObj, std::string const& _auxInput, std::string const& _smtlib); - Json::Value buildJson(std::string const& _extra); +/// @returns the ABI selector for a given function signature, as a u256 (left aligned) number. +inline u256 selectorFromSignature(std::string const& _signature) +{ + return u256(selectorFromSignature32(_signature)) << (256 - 32); +} - Json::Value m_smtResponses; -}; } diff --git a/libsolutil/LEB128.h b/libsolutil/LEB128.h new file mode 100644 index 000000000000..a156a09bd588 --- /dev/null +++ b/libsolutil/LEB128.h @@ -0,0 +1,59 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + + +#include + +namespace solidity::util +{ + +inline bytes lebEncode(uint64_t _n) +{ + bytes encoded; + while (_n > 0x7f) + { + encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f))); + _n >>= 7; + } + encoded.emplace_back(_n); + return encoded; +} + +// signed right shift is an arithmetic right shift +static_assert((-1 >> 1) == -1, "Arithmetic shift not supported."); + +inline bytes lebEncodeSigned(int64_t _n) +{ + // Based on https://github.com/llvm/llvm-project/blob/master/llvm/include/llvm/Support/LEB128.h + bytes result; + bool more; + do + { + uint8_t v = _n & 0x7f; + _n >>= 7; + more = !((((_n == 0) && ((v & 0x40) == 0)) || ((_n == -1) && ((v & 0x40) != 0)))); + if (more) + v |= 0x80; // Mark this byte to show that more bytes will follow. + result.emplace_back(v); + } + while (more); + return result; +} + +} diff --git a/libsolutil/SetOnce.h b/libsolutil/SetOnce.h new file mode 100644 index 000000000000..007424ff1ed4 --- /dev/null +++ b/libsolutil/SetOnce.h @@ -0,0 +1,88 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace solidity::util +{ + +DEV_SIMPLE_EXCEPTION(BadSetOnceReassignment); +DEV_SIMPLE_EXCEPTION(BadSetOnceAccess); + +/// A class that stores a value that can only be set once +/// \tparam T the type of the stored value +template +class SetOnce +{ +public: + /// Initializes the class to have no stored value. + SetOnce() = default; + + // Not copiable + SetOnce(SetOnce const&) = delete; + SetOnce(SetOnce&&) = delete; + + // Not movable + SetOnce& operator=(SetOnce const&) = delete; + SetOnce& operator=(SetOnce&&) = delete; + + /// @brief Sets the stored value to \p _newValue + /// @throws BadSetOnceReassignment when the stored value has already been set + /// @return `*this` + constexpr SetOnce& operator=(T _newValue) & + { + assertThrow( + !m_value.has_value(), + BadSetOnceReassignment, + "Attempt to reassign to a SetOnce that already has a value." + ); + + m_value.emplace(std::move(_newValue)); + return *this; + } + + /// @return A reference to the stored value. The returned reference has the same lifetime as `*this`. + /// @throws BadSetOnceAccess when the stored value has not yet been set + T const& operator*() const + { + assertThrow( + m_value.has_value(), + BadSetOnceAccess, + "Attempt to access the value of a SetOnce that does not have a value." + ); + + return m_value.value(); + } + + /// @return A reference to the stored value. The referent of the returned pointer has the same lifetime as `*this`. + /// @throws BadSetOnceAccess when the stored value has not yet been set + T const* operator->() const { return std::addressof(**this); } + + /// @return true if a value was assigned + bool set() const { return m_value.has_value(); } +private: + std::optional m_value = std::nullopt; +}; + +} diff --git a/libsolutil/UTF8.cpp b/libsolutil/UTF8.cpp index 4acf7a8ea9f2..baeda14b08ea 100644 --- a/libsolutil/UTF8.cpp +++ b/libsolutil/UTF8.cpp @@ -76,8 +76,6 @@ bool isWellFormed(unsigned char byte1, unsigned char byte2) return false; } -} - bool validateUTF8(unsigned char const* _input, size_t _length, size_t& _invalidPosition) { bool valid = true; @@ -134,6 +132,8 @@ bool validateUTF8(unsigned char const* _input, size_t _length, size_t& _invalidP return false; } +} + bool validateUTF8(std::string const& _input, size_t& _invalidPosition) { return validateUTF8(reinterpret_cast(_input.c_str()), _input.length(), _invalidPosition); diff --git a/libyul/AsmData.h b/libyul/AST.h similarity index 99% rename from libyul/AsmData.h rename to libyul/AST.h index c4040f320c7e..09d39832ff84 100644 --- a/libyul/AsmData.h +++ b/libyul/AST.h @@ -23,7 +23,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/AsmDataForward.h b/libyul/ASTForward.h similarity index 98% rename from libyul/AsmDataForward.h rename to libyul/ASTForward.h index 02216fd0140b..a4dc5be3f8eb 100644 --- a/libyul/AsmDataForward.h +++ b/libyul/ASTForward.h @@ -28,6 +28,7 @@ namespace solidity::yul { +enum class LiteralKind; struct Literal; struct Label; struct Identifier; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index b8f0dd4e7a1e..eadcdbbaf5b4 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include #include #include @@ -44,6 +44,20 @@ using namespace solidity::yul; using namespace solidity::util; using namespace solidity::langutil; +namespace +{ +inline string to_string(LiteralKind _kind) +{ + switch (_kind) + { + case LiteralKind::Number: return "number"; + case LiteralKind::Boolean: return "boolean"; + case LiteralKind::String: return "string"; + default: yulAssert(false, ""); + } +} +} + bool AsmAnalyzer::analyze(Block const& _block) { auto watcher = m_errorReporter.errorWatcher(); @@ -272,14 +286,14 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) auto watcher = m_errorReporter.errorWatcher(); vector const* parameterTypes = nullptr; vector const* returnTypes = nullptr; - vector const* needsLiteralArguments = nullptr; + vector> const* literalArguments = nullptr; if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name)) { parameterTypes = &f->parameters; returnTypes = &f->returns; - if (f->literalArguments) - needsLiteralArguments = &f->literalArguments.value(); + if (!f->literalArguments.empty()) + literalArguments = &f->literalArguments; validateInstructions(_funCall); } @@ -318,15 +332,11 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) for (size_t i = _funCall.arguments.size(); i > 0; i--) { Expression const& arg = _funCall.arguments[i - 1]; - bool isLiteralArgument = needsLiteralArguments && (*needsLiteralArguments)[i - 1]; - bool isStringLiteral = holds_alternative(arg) && get(arg).kind == LiteralKind::String; - - if (isLiteralArgument && isStringLiteral) - argTypes.emplace_back(expectUnlimitedStringLiteral(get(arg))); - else - argTypes.emplace_back(expectExpression(arg)); - - if (isLiteralArgument) + if ( + auto literalArgumentKind = (literalArguments && i <= literalArguments->size()) ? + literalArguments->at(i - 1) : + std::nullopt + ) { if (!holds_alternative(arg)) m_errorReporter.typeError( @@ -334,17 +344,29 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) _funCall.functionName.location, "Function expects direct literals as arguments." ); - else if ( - _funCall.functionName.name.str() == "datasize" || - _funCall.functionName.name.str() == "dataoffset" - ) - if (!m_dataNames.count(std::get(arg).value)) - m_errorReporter.typeError( - 3517_error, - _funCall.functionName.location, - "Unknown data object \"" + std::get(arg).value.str() + "\"." - ); + else if (*literalArgumentKind != get(arg).kind) + m_errorReporter.typeError( + 5859_error, + get(arg).location, + "Function expects " + to_string(*literalArgumentKind) + " literal." + ); + else if (*literalArgumentKind == LiteralKind::String) + { + if ( + _funCall.functionName.name.str() == "datasize" || + _funCall.functionName.name.str() == "dataoffset" + ) + if (!m_dataNames.count(get(arg).value)) + m_errorReporter.typeError( + 3517_error, + get(arg).location, + "Unknown data object \"" + std::get(arg).value.str() + "\"." + ); + argTypes.emplace_back(expectUnlimitedStringLiteral(get(arg))); + continue; + } } + argTypes.emplace_back(expectExpression(arg)); } std::reverse(argTypes.begin(), argTypes.end()); @@ -552,6 +574,12 @@ void AsmAnalyzer::expectValidIdentifier(YulString _identifier, SourceLocation co "\"" + _identifier.str() + "\" is not a valid identifier (contains consecutive dots)." ); + if (m_dialect.reservedIdentifier(_identifier)) + m_errorReporter.declarationError( + 5017_error, + _location, + "The identifier \"" + _identifier.str() + "\" is reserved and can not be used." + ); } void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location) @@ -617,19 +645,18 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio ); }; - if (( - _instr == evmasm::Instruction::RETURNDATACOPY || - _instr == evmasm::Instruction::RETURNDATASIZE - ) && !m_evmVersion.supportsReturndata()) + if (_instr == evmasm::Instruction::RETURNDATACOPY && !m_evmVersion.supportsReturndata()) errorForVM(7756_error, "only available for Byzantium-compatible"); + else if (_instr == evmasm::Instruction::RETURNDATASIZE && !m_evmVersion.supportsReturndata()) + errorForVM(4778_error, "only available for Byzantium-compatible"); else if (_instr == evmasm::Instruction::STATICCALL && !m_evmVersion.hasStaticCall()) errorForVM(1503_error, "only available for Byzantium-compatible"); - else if (( - _instr == evmasm::Instruction::SHL || - _instr == evmasm::Instruction::SHR || - _instr == evmasm::Instruction::SAR - ) && !m_evmVersion.hasBitwiseShifting()) + else if (_instr == evmasm::Instruction::SHL && !m_evmVersion.hasBitwiseShifting()) errorForVM(6612_error, "only available for Constantinople-compatible"); + else if (_instr == evmasm::Instruction::SHR && !m_evmVersion.hasBitwiseShifting()) + errorForVM(7458_error, "only available for Constantinople-compatible"); + else if (_instr == evmasm::Instruction::SAR && !m_evmVersion.hasBitwiseShifting()) + errorForVM(2054_error, "only available for Constantinople-compatible"); else if (_instr == evmasm::Instruction::CREATE2 && !m_evmVersion.hasCreate2()) errorForVM(6166_error, "only available for Constantinople-compatible"); else if (_instr == evmasm::Instruction::EXTCODEHASH && !m_evmVersion.hasExtCodeHash()) @@ -651,3 +678,8 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio return true; } + +bool AsmAnalyzer::validateInstructions(FunctionCall const& _functionCall) +{ + return validateInstructions(_functionCall.functionName.name.str(), _functionCall.functionName.location); +} diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index 9906c66e2554..d362c963a7e3 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -114,10 +114,7 @@ class AsmAnalyzer bool validateInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location); bool validateInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location); - bool validateInstructions(FunctionCall const& _functionCall) - { - return validateInstructions(_functionCall.functionName.name.str(), _functionCall.functionName.location); - } + bool validateInstructions(FunctionCall const& _functionCall); yul::ExternalIdentifierAccess::Resolver m_resolver; Scope* m_currentScope = nullptr; diff --git a/libyul/AsmAnalysisInfo.h b/libyul/AsmAnalysisInfo.h index 9ac179ad8508..120228d2e763 100644 --- a/libyul/AsmAnalysisInfo.h +++ b/libyul/AsmAnalysisInfo.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/AsmJsonConverter.cpp b/libyul/AsmJsonConverter.cpp index 58b11338d49a..019a5c4ce725 100644 --- a/libyul/AsmJsonConverter.cpp +++ b/libyul/AsmJsonConverter.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include diff --git a/libyul/AsmJsonConverter.h b/libyul/AsmJsonConverter.h index 3436523b7977..fe0c4685c997 100644 --- a/libyul/AsmJsonConverter.h +++ b/libyul/AsmJsonConverter.h @@ -23,7 +23,7 @@ #pragma once -#include +#include #include #include #include diff --git a/libsolidity/ast/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp similarity index 59% rename from libsolidity/ast/AsmJsonImporter.cpp rename to libyul/AsmJsonImporter.cpp index ec5a50e1c59e..bbcd1b9a7a33 100644 --- a/libsolidity/ast/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -22,30 +22,28 @@ */ -#include -#include -#include -#include -#include -#include +#include +#include +#include + #include -#include #include #include +#include using namespace std; -using namespace solidity::yul; +using namespace solidity::langutil; -namespace solidity::frontend +namespace solidity::yul { using SourceLocation = langutil::SourceLocation; SourceLocation const AsmJsonImporter::createSourceLocation(Json::Value const& _node) { - astAssert(member(_node, "src").isString(), "'src' must be a string"); + yulAssert(member(_node, "src").isString(), "'src' must be a string"); return solidity::langutil::parseSourceLocation(_node["src"].asString(), m_sourceName); } @@ -55,7 +53,7 @@ T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; r.location = createSourceLocation(_node); - astAssert( + yulAssert( r.location.source && 0 <= r.location.start && r.location.start <= r.location.end, "Invalid source location in Asm AST" ); @@ -69,21 +67,21 @@ Json::Value AsmJsonImporter::member(Json::Value const& _node, string const& _nam return _node[_name]; } -yul::TypedName AsmJsonImporter::createTypedName(Json::Value const& _node) +TypedName AsmJsonImporter::createTypedName(Json::Value const& _node) { - auto typedName = createAsmNode(_node); + auto typedName = createAsmNode(_node); typedName.type = YulString{member(_node, "type").asString()}; typedName.name = YulString{member(_node, "name").asString()}; return typedName; } -yul::Statement AsmJsonImporter::createStatement(Json::Value const& _node) +Statement AsmJsonImporter::createStatement(Json::Value const& _node) { Json::Value jsonNodeType = member(_node, "nodeType"); - astAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); + yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); string nodeType = jsonNodeType.asString(); - astAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); + yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); nodeType = nodeType.substr(3); if (nodeType == "ExpressionStatement") @@ -109,16 +107,16 @@ yul::Statement AsmJsonImporter::createStatement(Json::Value const& _node) else if (nodeType == "Block") return createBlock(_node); else - astAssert(false, "Invalid nodeType as statement"); + yulAssert(false, "Invalid nodeType as statement"); } -yul::Expression AsmJsonImporter::createExpression(Json::Value const& _node) +Expression AsmJsonImporter::createExpression(Json::Value const& _node) { Json::Value jsonNodeType = member(_node, "nodeType"); - astAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); + yulAssert(jsonNodeType.isString(), "Expected \"nodeType\" to be of type string!"); string nodeType = jsonNodeType.asString(); - astAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); + yulAssert(nodeType.substr(0, 3) == "Yul", "Invalid nodeType prefix"); nodeType = nodeType.substr(3); if (nodeType == "FunctionCall") @@ -128,35 +126,35 @@ yul::Expression AsmJsonImporter::createExpression(Json::Value const& _node) else if (nodeType == "Literal") return createLiteral(_node); else - astAssert(false, "Invalid nodeType as expression"); + yulAssert(false, "Invalid nodeType as expression"); } -vector AsmJsonImporter::createExpressionVector(Json::Value const& _array) +vector AsmJsonImporter::createExpressionVector(Json::Value const& _array) { - vector ret; + vector ret; for (auto& var: _array) ret.emplace_back(createExpression(var)); return ret; } -vector AsmJsonImporter::createStatementVector(Json::Value const& _array) +vector AsmJsonImporter::createStatementVector(Json::Value const& _array) { - vector ret; + vector ret; for (auto& var: _array) ret.emplace_back(createStatement(var)); return ret; } -yul::Block AsmJsonImporter::createBlock(Json::Value const& _node) +Block AsmJsonImporter::createBlock(Json::Value const& _node) { - auto block = createAsmNode(_node); + auto block = createAsmNode(_node); block.statements = createStatementVector(_node["statements"]); return block; } -yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node) +Literal AsmJsonImporter::createLiteral(Json::Value const& _node) { - auto lit = createAsmNode(_node); + auto lit = createAsmNode(_node); string kind = member(_node, "kind").asString(); lit.value = YulString{member(_node, "value").asString()}; @@ -165,8 +163,8 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node) if (kind == "number") { langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; - lit.kind = yul::LiteralKind::Number; - astAssert( + lit.kind = LiteralKind::Number; + yulAssert( scanner.currentToken() == Token::Number, "Expected number but got " + langutil::TokenTraits::friendlyName(scanner.currentToken()) + string(" while scanning ") + lit.value.str() ); @@ -174,8 +172,8 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node) else if (kind == "bool") { langutil::Scanner scanner{langutil::CharStream(lit.value.str(), "")}; - lit.kind = yul::LiteralKind::Boolean; - astAssert( + lit.kind = LiteralKind::Boolean; + yulAssert( scanner.currentToken() == Token::TrueLiteral || scanner.currentToken() == Token::FalseLiteral, "Expected true/false literal!" @@ -183,45 +181,45 @@ yul::Literal AsmJsonImporter::createLiteral(Json::Value const& _node) } else if (kind == "string") { - lit.kind = yul::LiteralKind::String; - astAssert( + lit.kind = LiteralKind::String; + yulAssert( lit.value.str().size() <= 32, "String literal too long (" + to_string(lit.value.str().size()) + " > 32)" ); } else - solAssert(false, "unknown type of literal"); + yulAssert(false, "unknown type of literal"); return lit; } -yul::Leave AsmJsonImporter::createLeave(Json::Value const& _node) +Leave AsmJsonImporter::createLeave(Json::Value const& _node) { - return createAsmNode(_node); + return createAsmNode(_node); } -yul::Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node) +Identifier AsmJsonImporter::createIdentifier(Json::Value const& _node) { - auto identifier = createAsmNode(_node); + auto identifier = createAsmNode(_node); identifier.name = YulString(member(_node, "name").asString()); return identifier; } -yul::Assignment AsmJsonImporter::createAssignment(Json::Value const& _node) +Assignment AsmJsonImporter::createAssignment(Json::Value const& _node) { - auto assignment = createAsmNode(_node); + auto assignment = createAsmNode(_node); if (_node.isMember("variableNames")) for (auto const& var: member(_node, "variableNames")) assignment.variableNames.emplace_back(createIdentifier(var)); - assignment.value = make_unique(createExpression(member(_node, "value"))); + assignment.value = make_unique(createExpression(member(_node, "value"))); return assignment; } -yul::FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node) +FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node) { - auto functionCall = createAsmNode(_node); + auto functionCall = createAsmNode(_node); for (auto const& var: member(_node, "arguments")) functionCall.arguments.emplace_back(createExpression(var)); @@ -231,25 +229,25 @@ yul::FunctionCall AsmJsonImporter::createFunctionCall(Json::Value const& _node) return functionCall; } -yul::ExpressionStatement AsmJsonImporter::createExpressionStatement(Json::Value const& _node) +ExpressionStatement AsmJsonImporter::createExpressionStatement(Json::Value const& _node) { - auto statement = createAsmNode(_node); + auto statement = createAsmNode(_node); statement.expression = createExpression(member(_node, "expression")); return statement; } -yul::VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node) +VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const& _node) { - auto varDec = createAsmNode(_node); + auto varDec = createAsmNode(_node); for (auto const& var: member(_node, "variables")) varDec.variables.emplace_back(createTypedName(var)); - varDec.value = make_unique(createExpression(member(_node, "value"))); + varDec.value = make_unique(createExpression(member(_node, "value"))); return varDec; } -yul::FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node) +FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value const& _node) { - auto funcDef = createAsmNode(_node); + auto funcDef = createAsmNode(_node); funcDef.name = YulString{member(_node, "name").asString()}; if (_node.isMember("parameters")) @@ -264,53 +262,53 @@ yul::FunctionDefinition AsmJsonImporter::createFunctionDefinition(Json::Value co return funcDef; } -yul::If AsmJsonImporter::createIf(Json::Value const& _node) +If AsmJsonImporter::createIf(Json::Value const& _node) { - auto ifStatement = createAsmNode(_node); - ifStatement.condition = make_unique(createExpression(member(_node, "condition"))); + auto ifStatement = createAsmNode(_node); + ifStatement.condition = make_unique(createExpression(member(_node, "condition"))); ifStatement.body = createBlock(member(_node, "body")); return ifStatement; } -yul::Case AsmJsonImporter::createCase(Json::Value const& _node) +Case AsmJsonImporter::createCase(Json::Value const& _node) { - auto caseStatement = createAsmNode(_node); + auto caseStatement = createAsmNode(_node); auto const& value = member(_node, "value"); if (value.isString()) - astAssert(value.asString() == "default", "Expected default case"); + yulAssert(value.asString() == "default", "Expected default case"); else - caseStatement.value = make_unique(createLiteral(value)); + caseStatement.value = make_unique(createLiteral(value)); caseStatement.body = createBlock(member(_node, "body")); return caseStatement; } -yul::Switch AsmJsonImporter::createSwitch(Json::Value const& _node) +Switch AsmJsonImporter::createSwitch(Json::Value const& _node) { - auto switchStatement = createAsmNode(_node); - switchStatement.expression = make_unique(createExpression(member(_node, "expression"))); + auto switchStatement = createAsmNode(_node); + switchStatement.expression = make_unique(createExpression(member(_node, "expression"))); for (auto const& var: member(_node, "cases")) switchStatement.cases.emplace_back(createCase(var)); return switchStatement; } -yul::ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node) +ForLoop AsmJsonImporter::createForLoop(Json::Value const& _node) { - auto forLoop = createAsmNode(_node); + auto forLoop = createAsmNode(_node); forLoop.pre = createBlock(member(_node, "pre")); - forLoop.condition = make_unique(createExpression(member(_node, "condition"))); + forLoop.condition = make_unique(createExpression(member(_node, "condition"))); forLoop.post = createBlock(member(_node, "post")); forLoop.body = createBlock(member(_node, "body")); return forLoop; } -yul::Break AsmJsonImporter::createBreak(Json::Value const& _node) +Break AsmJsonImporter::createBreak(Json::Value const& _node) { - return createAsmNode(_node); + return createAsmNode(_node); } -yul::Continue AsmJsonImporter::createContinue(Json::Value const& _node) +Continue AsmJsonImporter::createContinue(Json::Value const& _node) { - return createAsmNode(_node); + return createAsmNode(_node); } } diff --git a/libsolidity/ast/AsmJsonImporter.h b/libyul/AsmJsonImporter.h similarity index 97% rename from libsolidity/ast/AsmJsonImporter.h rename to libyul/AsmJsonImporter.h index b4e8cfe7b7f5..e491430b63f1 100644 --- a/libsolidity/ast/AsmJsonImporter.h +++ b/libyul/AsmJsonImporter.h @@ -25,11 +25,11 @@ #include #include -#include +#include #include -namespace solidity::frontend +namespace solidity::yul { /** diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 6c5d97e36d3f..25b94157cb49 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -22,10 +22,12 @@ */ #include +#include #include #include #include #include +#include #include @@ -42,8 +44,8 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ { m_recursionDepth = 0; - _scanner->supportPeriodInIdentifier(true); - ScopeGuard resetScanner([&]{ _scanner->supportPeriodInIdentifier(false); }); + _scanner->setScannerMode(ScannerKind::Yul); + ScopeGuard resetScanner([&]{ _scanner->setScannerMode(ScannerKind::Solidity); }); try { @@ -61,27 +63,6 @@ unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _ return nullptr; } -std::map const& Parser::instructions() -{ - // Allowed instructions, lowercase names. - static map s_instructions; - if (s_instructions.empty()) - { - for (auto const& instruction: evmasm::c_instructions) - { - if ( - instruction.second == evmasm::Instruction::JUMPDEST || - evmasm::isPushInstruction(instruction.second) - ) - continue; - string name = instruction.first; - transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); - s_instructions[name] = instruction.second; - } - } - return s_instructions; -} - Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); @@ -137,41 +118,39 @@ Statement Parser::parseStatement() { Statement stmt{createWithLocation()}; checkBreakContinuePosition("break"); - m_scanner->next(); + advance(); return stmt; } case Token::Continue: { Statement stmt{createWithLocation()}; checkBreakContinuePosition("continue"); - m_scanner->next(); + advance(); + return stmt; + } + case Token::Leave: + { + Statement stmt{createWithLocation()}; + if (!m_insideFunction) + m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); + advance(); return stmt; } - case Token::Identifier: - if (currentLiteral() == "leave") - { - Statement stmt{createWithLocation()}; - if (!m_insideFunction) - m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); - m_scanner->next(); - return stmt; - } - break; default: break; } + // Options left: - // Simple instruction (might turn into functional), - // literal, - // identifier (might turn into label or functional assignment) - ElementaryOperation elementary(parseElementaryOperation()); + // Expression/FunctionCall + // Assignment + variant elementary(parseLiteralOrIdentifier()); switch (currentToken()) { case Token::LParen: { Expression expr = parseCall(std::move(elementary)); - return ExpressionStatement{locationOf(expr), expr}; + return ExpressionStatement{locationOf(expr), move(expr)}; } case Token::Comma: case Token::AssemblyAssign: @@ -206,7 +185,7 @@ Statement Parser::parseStatement() expectToken(Token::Comma); - elementary = parseElementaryOperation(); + elementary = parseLiteralOrIdentifier(); } expectToken(Token::AssemblyAssign); @@ -214,28 +193,15 @@ Statement Parser::parseStatement() assignment.value = make_unique(parseExpression()); assignment.location.end = locationOf(*assignment.value).end; - return Statement{std::move(assignment)}; + return Statement{move(assignment)}; } default: fatalParserError(6913_error, "Call or assignment expected."); break; } - if (holds_alternative(elementary)) - { - Identifier& identifier = std::get(elementary); - return ExpressionStatement{identifier.location, { move(identifier) }}; - } - else if (holds_alternative(elementary)) - { - Expression expr = std::get(elementary); - return ExpressionStatement{locationOf(expr), expr}; - } - else - { - yulAssert(false, "Invalid elementary operation."); - return {}; - } + yulAssert(false, ""); + return {}; } Case Parser::parseCase() @@ -247,7 +213,7 @@ Case Parser::parseCase() else if (currentToken() == Token::Case) { advance(); - ElementaryOperation literal = parseElementaryOperation(); + variant literal = parseLiteralOrIdentifier(); if (!holds_alternative(literal)) fatalParserError(4805_error, "Literal expected."); _case.value = make_unique(std::get(std::move(literal))); @@ -286,44 +252,37 @@ Expression Parser::parseExpression() { RecursionGuard recursionGuard(*this); - ElementaryOperation operation = parseElementaryOperation(); - if (holds_alternative(operation) || currentToken() == Token::LParen) - return parseCall(std::move(operation)); - else if (holds_alternative(operation)) - return std::get(operation); - else - { - yulAssert(holds_alternative(operation), ""); - return std::get(operation); - } + variant operation = parseLiteralOrIdentifier(); + return visit(GenericVisitor{ + [&](Identifier& _identifier) -> Expression + { + if (currentToken() == Token::LParen) + return parseCall(std::move(operation)); + if (m_dialect.builtin(_identifier.name)) + fatalParserError( + 7104_error, + _identifier.location, + "Builtin function \"" + _identifier.name.str() + "\" must be called." + ); + return move(_identifier); + }, + [&](Literal& _literal) -> Expression + { + return move(_literal); + } + }, operation); } -Parser::ElementaryOperation Parser::parseElementaryOperation() +variant Parser::parseLiteralOrIdentifier() { RecursionGuard recursionGuard(*this); - ElementaryOperation ret; switch (currentToken()) { case Token::Identifier: - case Token::Return: - case Token::Byte: - case Token::Bool: - case Token::Address: - case Token::Var: - case Token::In: { - YulString literal{currentLiteral()}; - if (m_dialect.builtin(literal)) - { - Identifier identifier{currentLocation(), literal}; - advance(); - expectToken(Token::LParen, false); - return FunctionCall{identifier.location, identifier, {}}; - } - else - ret = Identifier{currentLocation(), literal}; + Identifier identifier{currentLocation(), YulString{currentLiteral()}}; advance(); - break; + return identifier; } case Token::StringLiteral: case Token::Number: @@ -363,13 +322,18 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() literal.type = expectAsmIdentifier(); } - ret = std::move(literal); - break; + return literal; } + case Token::HexStringLiteral: + fatalParserError(3772_error, "Hex literals are not valid in this context."); + break; + case Token::Illegal: + fatalParserError(1465_error, "Illegal token: " + to_string(m_scanner->currentError())); + break; default: fatalParserError(1856_error, "Literal or identifier expected."); } - return ret; + return {}; } VariableDeclaration Parser::parseVariableDeclaration() @@ -422,10 +386,9 @@ FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::Comma); } expectToken(Token::RParen); - if (currentToken() == Token::Sub) + if (currentToken() == Token::RightArrow) { - expectToken(Token::Sub); - expectToken(Token::GreaterThan); + expectToken(Token::RightArrow); while (true) { funDef.returnVariables.emplace_back(parseTypedName()); @@ -444,21 +407,17 @@ FunctionDefinition Parser::parseFunctionDefinition() return funDef; } -Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) +FunctionCall Parser::parseCall(variant&& _initialOp) { RecursionGuard recursionGuard(*this); - FunctionCall ret; - if (holds_alternative(_initialOp)) - { - ret.functionName = std::move(std::get(_initialOp)); - ret.location = ret.functionName.location; - } - else if (holds_alternative(_initialOp)) - ret = std::move(std::get(_initialOp)); - else + if (!holds_alternative(_initialOp)) fatalParserError(9980_error, "Function name expected."); + FunctionCall ret; + ret.functionName = std::move(std::get(_initialOp)); + ret.location = ret.functionName.location; + expectToken(Token::LParen); if (currentToken() != Token::RParen) { @@ -494,24 +453,10 @@ TypedName Parser::parseTypedName() YulString Parser::expectAsmIdentifier() { YulString name{currentLiteral()}; - switch (currentToken()) - { - case Token::Return: - case Token::Byte: - case Token::Address: - case Token::Bool: - case Token::Identifier: - case Token::Var: - case Token::In: - break; - default: - expectToken(Token::Identifier); - break; - } - - if (m_dialect.builtin(name)) + if (currentToken() == Token::Identifier && m_dialect.builtin(name)) fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name."); - advance(); + // NOTE: We keep the expectation here to ensure the correct source location for the error above. + expectToken(Token::Identifier); return name; } diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index ecd62228afa5..d1294b111e19 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -23,15 +23,13 @@ #pragma once -#include +#include #include #include #include #include -#include - #include #include #include @@ -62,12 +60,7 @@ class Parser: public langutil::ParserBase /// @returns an empty shared pointer on error. std::unique_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); - /// @returns a map of all EVM instructions available to assembly. - static std::map const& instructions(); - protected: - using ElementaryOperation = std::variant; - langutil::SourceLocation currentLocation() const override { return m_locationOverride ? *m_locationOverride : ParserBase::currentLocation(); @@ -89,10 +82,10 @@ class Parser: public langutil::ParserBase Expression parseExpression(); /// Parses an elementary operation, i.e. a literal, identifier, instruction or /// builtin functian call (only the name). - ElementaryOperation parseElementaryOperation(); + std::variant parseLiteralOrIdentifier(); VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); - Expression parseCall(ElementaryOperation&& _initialOp); + FunctionCall parseCall(std::variant&& _initialOp); TypedName parseTypedName(); YulString expectAsmIdentifier(); diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index c5982afc9b9d..61e01bf0f5ae 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -22,7 +22,7 @@ */ #include -#include +#include #include #include diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 648181cbf576..63b1bd3a2f1e 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -23,7 +23,7 @@ #pragma once -#include +#include #include diff --git a/libyul/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp index 0e06954106dd..287c0caeaea9 100644 --- a/libyul/AsmScopeFiller.cpp +++ b/libyul/AsmScopeFiller.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include #include #include diff --git a/libyul/AsmScopeFiller.h b/libyul/AsmScopeFiller.h index f2c35118b7db..b43910874337 100644 --- a/libyul/AsmScopeFiller.h +++ b/libyul/AsmScopeFiller.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 65269e9e0b28..8d537bd6da47 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -1,11 +1,31 @@ +# This will re-generate the polyfill headers, if any file within libyul/backends/wasm/polyfill/ was modified. +set_directory_properties(PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/libyul/backends/wasm/polyfill/) + +set(POLYFILLS Arithmetic Bitwise Comparison Conversion Interface Keccak Logical Memory) +set(GENERATED_POLYFILL_HEADERS) +foreach(polyfill IN LISTS POLYFILLS) + set(POLYFILL_FILE ${CMAKE_SOURCE_DIR}/libyul/backends/wasm/polyfill/${polyfill}.yul) + file(READ ${POLYFILL_FILE} EWASM_POLYFILL_CONTENT HEX) + string(REGEX MATCHALL ".." EWASM_POLYFILL_CONTENT "${EWASM_POLYFILL_CONTENT}") + string(REGEX REPLACE ";" ",\n\t0x" EWASM_POLYFILL_CONTENT "${EWASM_POLYFILL_CONTENT}") + set(EWASM_POLYFILL_CONTENT "0x${EWASM_POLYFILL_CONTENT}") + set(EWASM_POLYFILL_NAME ${polyfill}) + configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/ewasm_polyfill.in" ${CMAKE_BINARY_DIR}/include/ewasmPolyfills/${polyfill}.h @ONLY) + list(APPEND GENERATED_POLYFILL_HEADERS ${CMAKE_BINARY_DIR}/include/ewasmPolyfills/${polyfill}.h) +endforeach() + add_library(yul + ${GENERATED_POLYFILL_HEADERS} + AsmAnalysis.cpp AsmAnalysis.h AsmAnalysisInfo.h - AsmData.h - AsmDataForward.h + AST.h + ASTForward.h AsmJsonConverter.h AsmJsonConverter.cpp + AsmJsonImporter.h + AsmJsonImporter.cpp AsmParser.cpp AsmParser.h AsmPrinter.cpp @@ -105,6 +125,8 @@ add_library(yul optimiser/ForLoopInitRewriter.h optimiser/FullInliner.cpp optimiser/FullInliner.h + optimiser/FunctionCallFinder.cpp + optimiser/FunctionCallFinder.h optimiser/FunctionGrouper.cpp optimiser/FunctionGrouper.h optimiser/FunctionHoister.cpp @@ -127,9 +149,13 @@ add_library(yul optimiser/NameDispenser.h optimiser/NameDisplacer.cpp optimiser/NameDisplacer.h + optimiser/NameSimplifier.cpp + optimiser/NameSimplifier.h optimiser/OptimiserStep.h optimiser/OptimizerUtilities.cpp optimiser/OptimizerUtilities.h + optimiser/ReasoningBasedSimplifier.cpp + optimiser/ReasoningBasedSimplifier.h optimiser/RedundantAssignEliminator.cpp optimiser/RedundantAssignEliminator.h optimiser/Rematerialiser.cpp @@ -146,6 +172,10 @@ add_library(yul optimiser/SimplificationRules.h optimiser/StackCompressor.cpp optimiser/StackCompressor.h + optimiser/StackLimitEvader.cpp + optimiser/StackLimitEvader.h + optimiser/StackToMemoryMover.cpp + optimiser/StackToMemoryMover.h optimiser/StructuralSimplifier.cpp optimiser/StructuralSimplifier.h optimiser/Substitution.cpp @@ -156,6 +186,10 @@ add_library(yul optimiser/SyntacticalEquality.h optimiser/TypeInfo.cpp optimiser/TypeInfo.h + optimiser/UnusedFunctionParameterPruner.cpp + optimiser/UnusedFunctionParameterPruner.h + optimiser/UnusedFunctionsCommon.h + optimiser/UnusedFunctionsCommon.cpp optimiser/UnusedPruner.cpp optimiser/UnusedPruner.h optimiser/VarDeclInitializer.cpp @@ -163,4 +197,5 @@ add_library(yul optimiser/VarNameCleaner.cpp optimiser/VarNameCleaner.h ) -target_link_libraries(yul PUBLIC evmasm solutil langutil) + +target_link_libraries(yul PUBLIC evmasm solutil langutil smtutil) diff --git a/libyul/CompilabilityChecker.cpp b/libyul/CompilabilityChecker.cpp index 2a99e3c7934e..4b1a99a74018 100644 --- a/libyul/CompilabilityChecker.cpp +++ b/libyul/CompilabilityChecker.cpp @@ -33,7 +33,7 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::util; -map CompilabilityChecker::run( +CompilabilityChecker::CompilabilityChecker( Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation @@ -63,12 +63,11 @@ map CompilabilityChecker::run( ); transform(*_object.code); - std::map functions; for (StackTooDeepError const& error: transform.stackErrors()) - functions[error.functionName] = max(error.depth, functions[error.functionName]); - - return functions; + { + unreachableVariables[error.functionName].emplace(error.variable); + int& deficit = stackDeficit[error.functionName]; + deficit = std::max(error.depth, deficit); + } } - else - return {}; } diff --git a/libyul/CompilabilityChecker.h b/libyul/CompilabilityChecker.h index 1267640bf16f..307d719e4b0e 100644 --- a/libyul/CompilabilityChecker.h +++ b/libyul/CompilabilityChecker.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include @@ -33,22 +33,20 @@ namespace solidity::yul /** * Component that checks whether all variables are reachable on the stack and - * returns a mapping from function name to the largest stack difference found - * in that function (no entry present if that function is compilable). + * provides a mapping from function name to the largest stack difference found + * in that function (no entry present if that function is compilable), as well + * as the set of unreachable variables for each function. * * This only works properly if the outermost block is compilable and * functions are not nested. Otherwise, it might miss reporting some functions. * * Only checks the code of the object itself, does not descend into sub-objects. */ -class CompilabilityChecker +struct CompilabilityChecker { -public: - static std::map run( - Dialect const& _dialect, - Object const& _object, - bool _optimizeStackAllocation - ); + CompilabilityChecker(Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation); + std::map> unreachableVariables; + std::map stackDeficit; }; } diff --git a/libyul/Dialect.cpp b/libyul/Dialect.cpp index 1003c8e44a40..847c9f1ac053 100644 --- a/libyul/Dialect.cpp +++ b/libyul/Dialect.cpp @@ -20,7 +20,7 @@ */ #include -#include +#include using namespace solidity::yul; using namespace std; @@ -33,6 +33,15 @@ Literal Dialect::zeroLiteralForType(solidity::yul::YulString _type) const return {SourceLocation{}, LiteralKind::Number, "0"_yulstring, _type}; } + +Literal Dialect::trueLiteral() const +{ + if (boolType != defaultType) + return {SourceLocation{}, LiteralKind::Boolean, "true"_yulstring, boolType}; + else + return {SourceLocation{}, LiteralKind::Number, "1"_yulstring, defaultType}; +} + bool Dialect::validTypeForLiteral(LiteralKind _kind, YulString, YulString _type) const { if (_kind == LiteralKind::Boolean) diff --git a/libyul/Dialect.h b/libyul/Dialect.h index 553f9049abdc..ef8c5690938f 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -48,8 +48,13 @@ struct BuiltinFunction ControlFlowSideEffects controlFlowSideEffects; /// If true, this is the msize instruction. bool isMSize = false; - /// If set, same length as the arguments, if true at index i, the i'th argument has to be a literal which means it can't be moved to variables. - std::optional> literalArguments; + /// Must be empty or the same length as the arguments. + /// If set at index i, the i'th argument has to be a literal which means it can't be moved to variables. + std::vector> literalArguments{}; + std::optional literalArgument(size_t i) const + { + return literalArguments.empty() ? std::nullopt : literalArguments.at(i); + } }; struct Dialect: boost::noncopyable @@ -63,15 +68,24 @@ struct Dialect: boost::noncopyable /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; } + /// @returns true if the identifier is reserved. This includes the builtins too. + virtual bool reservedIdentifier(YulString _name) const { return builtin(_name) != nullptr; } + virtual BuiltinFunction const* discardFunction(YulString /* _type */) const { return nullptr; } virtual BuiltinFunction const* equalityFunction(YulString /* _type */) const { return nullptr; } virtual BuiltinFunction const* booleanNegationFunction() const { return nullptr; } + virtual BuiltinFunction const* memoryStoreFunction(YulString /* _type */) const { return nullptr; } + virtual BuiltinFunction const* memoryLoadFunction(YulString /* _type */) const { return nullptr; } + virtual BuiltinFunction const* storageStoreFunction(YulString /* _type */) const { return nullptr; } + virtual BuiltinFunction const* storageLoadFunction(YulString /* _type */) const { return nullptr; } + /// Check whether the given type is legal for the given literal value. /// Should only be called if the type exists in the dialect at all. virtual bool validTypeForLiteral(LiteralKind _kind, YulString _value, YulString _type) const; virtual Literal zeroLiteralForType(YulString _type) const; + virtual Literal trueLiteral() const; virtual std::set fixedFunctionNames() const { return {}; } diff --git a/libyul/Object.h b/libyul/Object.h index deb52eb25f04..aa4bde62944e 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index f1d38ad730be..4385c2b3bbc3 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include diff --git a/libyul/SideEffects.h b/libyul/SideEffects.h index e9c12d9c8720..7433e40d2f18 100644 --- a/libyul/SideEffects.h +++ b/libyul/SideEffects.h @@ -18,6 +18,7 @@ #pragma once +#include #include namespace solidity::yul @@ -30,6 +31,20 @@ namespace solidity::yul */ struct SideEffects { + /// Corresponds to the effect that a Yul-builtin has on a generic data location (storage, memory + /// and other blockchain state). + enum Effect + { + None, + Read, + Write + }; + + friend Effect operator+(Effect const& _a, Effect const& _b) + { + return static_cast(std::max(static_cast(_a), static_cast(_b))); + } + /// If true, expressions in this code can be freely moved and copied without altering the /// semantics. /// At statement level, it means that functions containing this code can be @@ -38,22 +53,34 @@ struct SideEffects /// This means it cannot depend on storage or memory, cannot have any side-effects, /// but it can depend on state that is constant across an EVM-call. bool movable = true; + /// If true, the expressions in this code can be moved or copied (together with their arguments) + /// across control flow branches and instructions as long as these instructions' 'effects' do + /// not influence the 'effects' of the aforementioned expressions. + bool movableApartFromEffects = true; /// If true, the code can be removed without changing the semantics. - bool sideEffectFree = true; + bool canBeRemoved = true; /// If true, the code can be removed without changing the semantics as long as /// the whole program does not contain the msize instruction. - bool sideEffectFreeIfNoMSize = true; - /// If false, storage is guaranteed to be unchanged by the code under all - /// circumstances. - bool invalidatesStorage = false; - /// If false, memory is guaranteed to be unchanged by the code under all - /// circumstances. - bool invalidatesMemory = false; + bool canBeRemovedIfNoMSize = true; + /// If false, the code calls a for-loop or a recursive function, and therefore potentially loops + /// infinitely. All builtins are set to true by default, even `invalid()`. + bool cannotLoop = true; + /// Can write, read or have no effect on the blockchain state, when the value of `otherState` is + /// `Write`, `Read` or `None` respectively. + Effect otherState = None; + /// Can write, read or have no effect on storage, when the value of `storage` is `Write`, `Read` + /// or `None` respectively. When the value is `Write`, the expression can invalidate storage, + /// potentially indirectly through external calls. + Effect storage = None; + /// Can write, read or have no effect on memory, when the value of `memory` is `Write`, `Read` + /// or `None` respectively. Note that, when the value is `Read`, the expression can have an + /// effect on `msize()`. + Effect memory = None; /// @returns the worst-case side effects. static SideEffects worst() { - return SideEffects{false, false, false, true, true}; + return SideEffects{false, false, false, false, false, Write, Write, Write}; } /// @returns the combined side effects of two pieces of code. @@ -61,10 +88,13 @@ struct SideEffects { return SideEffects{ movable && _other.movable, - sideEffectFree && _other.sideEffectFree, - sideEffectFreeIfNoMSize && _other.sideEffectFreeIfNoMSize, - invalidatesStorage || _other.invalidatesStorage, - invalidatesMemory || _other.invalidatesMemory + movableApartFromEffects && _other.movableApartFromEffects, + canBeRemoved && _other.canBeRemoved, + canBeRemovedIfNoMSize && _other.canBeRemovedIfNoMSize, + cannotLoop && _other.cannotLoop, + otherState + _other.otherState, + storage + _other.storage, + memory + _other.memory }; } @@ -79,10 +109,13 @@ struct SideEffects { return movable == _other.movable && - sideEffectFree == _other.sideEffectFree && - sideEffectFreeIfNoMSize == _other.sideEffectFreeIfNoMSize && - invalidatesStorage == _other.invalidatesStorage && - invalidatesMemory == _other.invalidatesMemory; + movableApartFromEffects == _other.movableApartFromEffects && + canBeRemoved == _other.canBeRemoved && + canBeRemovedIfNoMSize == _other.canBeRemovedIfNoMSize && + cannotLoop == _other.cannotLoop && + otherState == _other.otherState && + storage == _other.storage && + memory == _other.memory; } }; diff --git a/libyul/Utilities.cpp b/libyul/Utilities.cpp index af72888fa42d..9c16252ee699 100644 --- a/libyul/Utilities.cpp +++ b/libyul/Utilities.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include #include @@ -49,7 +49,7 @@ string solidity::yul::reindent(string const& _code) auto const e = i == _s.npos ? end(_s) : next(begin(_s), static_cast(i)); auto const opening = count_if(begin(_s), e, [](auto ch) { return ch == '{' || ch == '('; }); auto const closing = count_if(begin(_s), e, [](auto ch) { return ch == '}' || ch == ')'; }); - return opening - closing; + return int(opening - closing); }; vector lines; diff --git a/libyul/Utilities.h b/libyul/Utilities.h index 53913e6fd477..b9077eeaee18 100644 --- a/libyul/Utilities.h +++ b/libyul/Utilities.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include namespace solidity::yul { diff --git a/libyul/backends/evm/AsmCodeGen.cpp b/libyul/backends/evm/AsmCodeGen.cpp index 15d646755306..49665e347cdb 100644 --- a/libyul/backends/evm/AsmCodeGen.cpp +++ b/libyul/backends/evm/AsmCodeGen.cpp @@ -21,8 +21,8 @@ #include -#include #include +#include #include #include diff --git a/libyul/backends/evm/ConstantOptimiser.cpp b/libyul/backends/evm/ConstantOptimiser.cpp index 9148b0793677..d37ddade1967 100644 --- a/libyul/backends/evm/ConstantOptimiser.cpp +++ b/libyul/backends/evm/ConstantOptimiser.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include diff --git a/libyul/backends/evm/ConstantOptimiser.h b/libyul/backends/evm/ConstantOptimiser.h index d6844bbe6dcb..52d78be86bc2 100644 --- a/libyul/backends/evm/ConstantOptimiser.h +++ b/libyul/backends/evm/ConstantOptimiser.h @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index 99945383e528..ade8b7e05fff 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -53,7 +53,7 @@ void EVMAssembly::appendInstruction(evmasm::Instruction _instr) void EVMAssembly::appendConstant(u256 const& _constant) { bytes data = toCompactBigEndian(_constant, 1); - appendInstruction(evmasm::pushInstruction(data.size())); + appendInstruction(evmasm::pushInstruction(static_cast(data.size()))); m_bytecode += data; } diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 95f787400f65..7877e8831593 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -222,7 +222,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) m_unusedStackSlots.erase(m_unusedStackSlots.begin()); m_context->variableStackHeights[&var] = slot; if (size_t heightDiff = variableHeightDiff(var, varName, true)) - m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(evmasm::swapInstruction(static_cast(heightDiff - 1))); m_assembly.appendInstruction(evmasm::Instruction::POP); } } @@ -314,7 +314,7 @@ void CodeTransform::operator()(Identifier const& _identifier) // TODO: opportunity for optimization: Do not DUP if this is the last reference // to the top most element of the stack if (size_t heightDiff = variableHeightDiff(_var, _identifier.name, false)) - m_assembly.appendInstruction(evmasm::dupInstruction(heightDiff)); + m_assembly.appendInstruction(evmasm::dupInstruction(static_cast(heightDiff))); else // Store something to balance the stack m_assembly.appendConstant(u256(0)); @@ -694,7 +694,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) { Scope::Variable const& _var = std::get(*var); if (size_t heightDiff = variableHeightDiff(_var, _variableName.name, true)) - m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(evmasm::swapInstruction(static_cast(heightDiff - 1))); m_assembly.appendInstruction(evmasm::Instruction::POP); decreaseReference(_variableName.name, _var); } diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index 40cfe375f6c1..45443ae7d93e 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -193,7 +193,7 @@ class CodeTransform void visitStatements(std::vector const& _statements); /// Pops all variables declared in the block and checks that the stack height is equal - /// to @a _blackStartStackHeight. + /// to @a _blockStartStackHeight. void finalizeBlock(Block const& _block, int _blockStartStackHeight); void generateMultiAssignment(std::vector const& _variableNames); diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index a54b267edbe7..6f58da364725 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -70,7 +70,7 @@ pair createEVMFunction( f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction); f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction); f.isMSize = _instruction == evmasm::Instruction::MSIZE; - f.literalArguments.reset(); + f.literalArguments.clear(); f.instruction = _instruction; f.generateCode = [_instruction]( FunctionCall const& _call, @@ -90,7 +90,7 @@ pair createFunction( size_t _params, size_t _returns, SideEffects _sideEffects, - vector _literalArguments, + vector> _literalArguments, std::function)> _generateCode ) { @@ -102,35 +102,57 @@ pair createFunction( f.parameters.resize(_params); f.returns.resize(_returns); f.sideEffects = std::move(_sideEffects); - if (!_literalArguments.empty()) - f.literalArguments = std::move(_literalArguments); - else - f.literalArguments.reset(); + f.literalArguments = std::move(_literalArguments); f.isMSize = false; f.instruction = {}; f.generateCode = std::move(_generateCode); return {name, f}; } +set createReservedIdentifiers() +{ + set reserved; + for (auto const& instr: evmasm::c_instructions) + { + string name = instr.first; + transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); + reserved.emplace(name); + } + reserved += vector{ + "linkersymbol"_yulstring, + "datasize"_yulstring, + "dataoffset"_yulstring, + "datacopy"_yulstring, + "setimmutable"_yulstring, + "loadimmutable"_yulstring, + }; + return reserved; +} + map createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess) { map builtins; - // NOTE: Parser::instructions() will filter JUMPDEST and PUSHnn too - for (auto const& instr: Parser::instructions()) + for (auto const& instr: evmasm::c_instructions) + { + string name = instr.first; + transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); + auto const opcode = instr.second; + if ( - !evmasm::isDupInstruction(instr.second) && - !evmasm::isSwapInstruction(instr.second) && - !evmasm::isPushInstruction(instr.second) && - instr.second != evmasm::Instruction::JUMP && - instr.second != evmasm::Instruction::JUMPI && - instr.second != evmasm::Instruction::JUMPDEST && - _evmVersion.hasOpcode(instr.second) + !evmasm::isDupInstruction(opcode) && + !evmasm::isSwapInstruction(opcode) && + !evmasm::isPushInstruction(opcode) && + opcode != evmasm::Instruction::JUMP && + opcode != evmasm::Instruction::JUMPI && + opcode != evmasm::Instruction::JUMPDEST && + _evmVersion.hasOpcode(opcode) ) - builtins.emplace(createEVMFunction(instr.first, instr.second)); + builtins.emplace(createEVMFunction(name, opcode)); + } if (_objectAccess) { - builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {true}, []( + builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&, @@ -140,7 +162,24 @@ map createBuiltins(langutil::EVMVersion _evmVe Expression const& arg = _call.arguments.front(); _assembly.appendLinkerSymbol(std::get(arg).value.str()); })); - builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {true}, []( + + builtins.emplace(createFunction( + "memoryguard", + 1, + 1, + SideEffects{}, + {LiteralKind::Number}, + []( + FunctionCall const& _call, + AbstractAssembly& _assembly, + BuiltinContext&, + function _visitExpression + ) { + visitArguments(_assembly, _call, _visitExpression); + }) + ); + + builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext& _context, @@ -162,7 +201,7 @@ map createBuiltins(langutil::EVMVersion _evmVe _assembly.appendDataSize(subIdPath); } })); - builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {true}, []( + builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext& _context, @@ -188,7 +227,7 @@ map createBuiltins(langutil::EVMVersion _evmVe "datacopy", 3, 0, - SideEffects{false, false, false, false, true}, + SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, {}, []( FunctionCall const& _call, @@ -202,21 +241,22 @@ map createBuiltins(langutil::EVMVersion _evmVe )); builtins.emplace(createFunction( "setimmutable", - 2, + 3, 0, - SideEffects{false, false, false, false, true}, - {true, false}, + SideEffects{false, false, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, + {std::nullopt, LiteralKind::String, std::nullopt}, []( FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&, std::function _visitExpression ) { - yulAssert(_call.arguments.size() == 2, ""); + yulAssert(_call.arguments.size() == 3, ""); - _visitExpression(_call.arguments[1]); + _visitExpression(_call.arguments[2]); + YulString identifier = std::get(_call.arguments[1]).value; + _visitExpression(_call.arguments[0]); _assembly.setSourceLocation(_call.location); - YulString identifier = std::get(_call.arguments.front()).value; _assembly.appendImmutableAssignment(identifier.str()); } )); @@ -225,7 +265,7 @@ map createBuiltins(langutil::EVMVersion _evmVe 1, 1, SideEffects{}, - {true}, + {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, @@ -245,7 +285,8 @@ map createBuiltins(langutil::EVMVersion _evmVe EVMDialect::EVMDialect(langutil::EVMVersion _evmVersion, bool _objectAccess): m_objectAccess(_objectAccess), m_evmVersion(_evmVersion), - m_functions(createBuiltins(_evmVersion, _objectAccess)) + m_functions(createBuiltins(_evmVersion, _objectAccess)), + m_reserved(createReservedIdentifiers()) { } @@ -258,6 +299,11 @@ BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const return nullptr; } +bool EVMDialect::reservedIdentifier(YulString _name) const +{ + return m_reserved.count(_name) != 0; +} + EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version) { static map> dialects; @@ -278,12 +324,20 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instruction) { + auto translate = [](evmasm::SemanticInformation::Effect _e) -> SideEffects::Effect + { + return static_cast(_e); + }; + return SideEffects{ evmasm::SemanticInformation::movable(_instruction), - evmasm::SemanticInformation::sideEffectFree(_instruction), - evmasm::SemanticInformation::sideEffectFreeIfNoMSize(_instruction), - evmasm::SemanticInformation::invalidatesStorage(_instruction), - evmasm::SemanticInformation::invalidatesMemory(_instruction) + evmasm::SemanticInformation::movableApartFromEffects(_instruction), + evmasm::SemanticInformation::canBeRemoved(_instruction), + evmasm::SemanticInformation::canBeRemovedIfNoMSize(_instruction), + true, // cannotLoop + translate(evmasm::SemanticInformation::otherState(_instruction)), + translate(evmasm::SemanticInformation::storage(_instruction)), + translate(evmasm::SemanticInformation::memory(_instruction)), }; } @@ -349,6 +403,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA BuiltinContext&, std::function _visitExpression ) { + // TODO this should use a Panic. // A value larger than 1 causes an invalid instruction. visitArguments(_assembly, _call, _visitExpression); _assembly.appendConstant(2); diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index fe1c67a4a5a2..1672c623c12b 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -24,10 +24,11 @@ #include #include -#include +#include #include #include +#include namespace solidity::yul { @@ -70,9 +71,16 @@ struct EVMDialect: public Dialect /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. BuiltinFunctionForEVM const* builtin(YulString _name) const override; + /// @returns true if the identifier is reserved. This includes the builtins too. + bool reservedIdentifier(YulString _name) const override; + BuiltinFunctionForEVM const* discardFunction(YulString /*_type*/) const override { return builtin("pop"_yulstring); } BuiltinFunctionForEVM const* equalityFunction(YulString /*_type*/) const override { return builtin("eq"_yulstring); } BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); } + BuiltinFunctionForEVM const* memoryStoreFunction(YulString /*_type*/) const override { return builtin("mstore"_yulstring); } + BuiltinFunctionForEVM const* memoryLoadFunction(YulString /*_type*/) const override { return builtin("mload"_yulstring); } + BuiltinFunctionForEVM const* storageStoreFunction(YulString /*_type*/) const override { return builtin("sstore"_yulstring); } + BuiltinFunctionForEVM const* storageLoadFunction(YulString /*_type*/) const override { return builtin("sload"_yulstring); } static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version); static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version); @@ -87,6 +95,7 @@ struct EVMDialect: public Dialect bool const m_objectAccess; langutil::EVMVersion const m_evmVersion; std::map m_functions; + std::set m_reserved; }; /** diff --git a/libyul/backends/evm/EVMMetrics.cpp b/libyul/backends/evm/EVMMetrics.cpp index 5fb6d863a0fb..ce8d8f31e916 100644 --- a/libyul/backends/evm/EVMMetrics.cpp +++ b/libyul/backends/evm/EVMMetrics.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include #include #include diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index ced32af6a0ae..74caf8073e4d 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -21,6 +21,7 @@ #include +#include #include #include @@ -170,7 +171,7 @@ NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): for (size_t j = 0; j < _call.arguments.size(); j++) { size_t const i = _call.arguments.size() - j - 1; - if (!(fun.second.literalArguments && (*fun.second.literalArguments)[i])) + if (!fun.second.literalArgument(i)) { _visitExpression(_call.arguments[i]); visited++; diff --git a/libyul/backends/wasm/BinaryTransform.cpp b/libyul/backends/wasm/BinaryTransform.cpp index 5746dfaafe86..c395864ec7bf 100644 --- a/libyul/backends/wasm/BinaryTransform.cpp +++ b/libyul/backends/wasm/BinaryTransform.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -106,33 +107,24 @@ bytes toBytes(Export _export) return toBytes(uint8_t(_export)); } +// NOTE: This is a subset of WebAssembly opcodes. +// Those available as a builtin are listed further down. enum class Opcode: uint8_t { - Unreachable = 0x00, - Nop = 0x01, Block = 0x02, Loop = 0x03, If = 0x04, Else = 0x05, - Try = 0x06, - Catch = 0x07, - Throw = 0x08, - Rethrow = 0x09, - BrOnExn = 0x0a, End = 0x0b, Br = 0x0c, BrIf = 0x0d, - BrTable = 0x0e, + BrTable = 0x0e, // Not used yet. Return = 0x0f, Call = 0x10, - CallIndirect = 0x11, - ReturnCall = 0x12, - ReturnCallIndirect = 0x13, - Drop = 0x1a, - Select = 0x1b, + CallIndirect = 0x11, // Not used yet. LocalGet = 0x20, LocalSet = 0x21, - LocalTee = 0x22, + LocalTee = 0x22, // Not used yet. GlobalGet = 0x23, GlobalSet = 0x24, I32Const = 0x41, @@ -155,6 +147,12 @@ Opcode constOpcodeFor(ValueType _type) } static map const builtins = { + {"unreachable", 0x00}, + {"nop", 0x01}, + {"i32.drop", 0x1a}, + {"i64.drop", 0x1a}, + {"i32.select", 0x1b}, + {"i64.select", 0x1b}, {"i32.load", 0x28}, {"i64.load", 0x29}, {"i32.load8_s", 0x2c}, @@ -239,28 +237,6 @@ static map const builtins = { {"i64.extend_i32_u", 0xad}, }; -bytes lebEncode(uint64_t _n) -{ - bytes encoded; - while (_n > 0x7f) - { - encoded.emplace_back(uint8_t(0x80 | (_n & 0x7f))); - _n >>= 7; - } - encoded.emplace_back(_n); - return encoded; -} - -bytes lebEncodeSigned(int64_t _n) -{ - if (_n >= 0 && _n < 0x40) - return toBytes(uint8_t(uint64_t(_n) & 0xff)); - else if (-_n > 0 && -_n < 0x40) - return toBytes(uint8_t(uint64_t(_n + 0x80) & 0xff)); - else - return toBytes(uint8_t(0x80 | uint8_t(_n & 0x7f))) + lebEncodeSigned(_n / 0x80); -} - bytes prefixSize(bytes _data) { size_t size = _data.size(); @@ -326,13 +302,23 @@ bytes BinaryTransform::run(Module const& _module) ret += exportSection(functionIDs); map> subModulePosAndSize; - for (auto const& sub: _module.subModules) + for (auto const& [name, module]: _module.subModules) { // TODO should we prefix and / or shorten the name? - bytes data = BinaryTransform::run(sub.second); - size_t length = data.size(); - ret += customSection(sub.first, move(data)); - subModulePosAndSize[sub.first] = {ret.size() - length, length}; + bytes data = BinaryTransform::run(module); + size_t const length = data.size(); + ret += customSection(name, move(data)); + // Skip all the previous sections and the size field of this current custom section. + size_t const offset = ret.size() - length; + subModulePosAndSize[name] = {offset, length}; + } + for (auto const& [name, data]: _module.customSections) + { + size_t const length = data.size(); + ret += customSection(name, data); + // Skip all the previous sections and the size field of this current custom section. + size_t const offset = ret.size() - length; + subModulePosAndSize[name] = {offset, length}; } BinaryTransform bt( @@ -356,8 +342,9 @@ bytes BinaryTransform::operator()(Literal const& _literal) bytes BinaryTransform::operator()(StringLiteral const&) { - // TODO is this used? - yulAssert(false, "String literals not yet implemented"); + // StringLiteral is a special AST element used for certain builtins. + // It is not mapped to actual WebAssembly, and should be processed in visit(BuiltinCall). + yulAssert(false, ""); } bytes BinaryTransform::operator()(LocalVariable const& _variable) @@ -377,38 +364,33 @@ bytes BinaryTransform::operator()(BuiltinCall const& _call) if (_call.functionName == "dataoffset") { string name = get(_call.arguments.at(0)).value; + // TODO: support the case where name refers to the current object + yulAssert(m_subModulePosAndSize.count(name), ""); return toBytes(Opcode::I64Const) + lebEncodeSigned(static_cast(m_subModulePosAndSize.at(name).first)); } else if (_call.functionName == "datasize") { string name = get(_call.arguments.at(0)).value; + // TODO: support the case where name refers to the current object + yulAssert(m_subModulePosAndSize.count(name), ""); return toBytes(Opcode::I64Const) + lebEncodeSigned(static_cast(m_subModulePosAndSize.at(name).second)); } + yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found"); + // NOTE: the dialect ensures we have the right amount of arguments bytes args = visit(_call.arguments); + bytes ret = move(args) + toBytes(builtins.at(_call.functionName)); + if ( + _call.functionName.find(".load") != string::npos || + _call.functionName.find(".store") != string::npos + ) + // Alignment hint and offset. Interpreters ignore the alignment. JITs/AOTs can take it + // into account to generate more efficient code but if the hint is invalid it could + // actually be more expensive. It's best to hint at 1-byte alignment if we don't plan + // to control the memory layout accordingly. + ret += bytes{{0, 0}}; // 2^0 == 1-byte alignment - if (_call.functionName == "unreachable") - return toBytes(Opcode::Unreachable); - else if (_call.functionName == "nop") - return toBytes(Opcode::Nop); - else if (_call.functionName == "i32.drop" || _call.functionName == "i64.drop") - return toBytes(Opcode::Drop); - else - { - yulAssert(builtins.count(_call.functionName), "Builtin " + _call.functionName + " not found"); - bytes ret = move(args) + toBytes(builtins.at(_call.functionName)); - if ( - _call.functionName.find(".load") != string::npos || - _call.functionName.find(".store") != string::npos - ) - // Alignment hint and offset. Interpreters ignore the alignment. JITs/AOTs can take it - // into account to generate more efficient code but if the hint is invalid it could - // actually be more expensive. It's best to hint at 1-byte alignment if we don't plan - // to control the memory layout accordingly. - ret += bytes{{0, 0}}; // 2^0 == 1-byte alignment - - return ret; - } + return ret; } bytes BinaryTransform::operator()(FunctionCall const& _call) @@ -680,9 +662,11 @@ bytes BinaryTransform::globalSection(vector con bytes BinaryTransform::exportSection(map const& _functionIDs) { - bytes result = lebEncode(2); + bool hasMain = _functionIDs.count("main"); + bytes result = lebEncode(hasMain ? 2 : 1); result += encodeName("memory") + toBytes(Export::Memory) + lebEncode(0); - result += encodeName("main") + toBytes(Export::Function) + lebEncode(_functionIDs.at("main")); + if (hasMain) + result += encodeName("main") + toBytes(Export::Function) + lebEncode(_functionIDs.at("main")); return makeSection(Section::EXPORT, move(result)); } diff --git a/libyul/backends/wasm/BinaryTransform.h b/libyul/backends/wasm/BinaryTransform.h index 348250546785..d164c11bf694 100644 --- a/libyul/backends/wasm/BinaryTransform.h +++ b/libyul/backends/wasm/BinaryTransform.h @@ -112,6 +112,8 @@ class BinaryTransform std::map const m_globalIDs; std::map const m_functionIDs; std::map const m_functionTypes; + /// The map of submodules, where the pair refers to the [offset, length]. The offset is + /// an absolute offset within the resulting assembled bytecode. std::map> const m_subModulePosAndSize; std::map m_locals; diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index d7cbb6ce9d53..3ba58e73e321 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -35,760 +35,31 @@ #include #include #include +#include #include #include #include #include +// The following headers are generated from the +// yul files placed in libyul/backends/wasm/polyfill. + +#include +#include +#include +#include +#include +#include +#include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; using namespace solidity::util; using namespace solidity::langutil; -namespace -{ -static string const polyfill{R"({ -function or_bool(a, b, c, d) -> r { - r := i64.or(i64.or(a, b), i64.or(c, d)) -} -// returns a + y + c plus carry value on 64 bit values. -// c should be at most 1 -function add_carry(x, y, c) -> r, r_c { - let t := i64.add(x, y) - r := i64.add(t, c) - r_c := i64.or( - i64.lt_u(t, x), - i64.lt_u(r, t) - ) -} -function add(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - let carry - r4, carry := add_carry(x4, y4, 0) - r3, carry := add_carry(x3, y3, carry) - r2, carry := add_carry(x2, y2, carry) - r1, carry := add_carry(x1, y1, carry) -} -function bit_negate(x) -> y { - y := i64.xor(x, 0xffffffffffffffff) -} -function sub(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // x - y = x + (~y + 1) - let carry - r4, carry := add_carry(x4, bit_negate(y4), 1) - r3, carry := add_carry(x3, bit_negate(y3), carry) - r2, carry := add_carry(x2, bit_negate(y2), carry) - r1, carry := add_carry(x1, bit_negate(y1), carry) -} -function split(x) -> hi, lo { - hi := i64.shr_u(x, 32) - lo := i64.and(x, 0xffffffff) -} -// Multiplies two 64 bit values resulting in a 128 bit -// value split into two 64 bit values. -function mul_64x64_128(x, y) -> hi, lo { - let xh, xl := split(x) - let yh, yl := split(y) - - let t0 := i64.mul(xl, yl) - let t1 := i64.mul(xh, yl) - let t2 := i64.mul(xl, yh) - let t3 := i64.mul(xh, yh) - - let t0h, t0l := split(t0) - let u1 := i64.add(t1, t0h) - let u1h, u1l := split(u1) - let u2 := i64.add(t2, u1l) - - lo := i64.or(i64.shl(u2, 32), t0l) - hi := i64.add(t3, i64.add(i64.shr_u(u2, 32), u1h)) -} -// Multiplies two 128 bit values resulting in a 256 bit -// value split into four 64 bit values. -function mul_128x128_256(x1, x2, y1, y2) -> r1, r2, r3, r4 { - let ah, al := mul_64x64_128(x1, y1) - let bh, bl := mul_64x64_128(x1, y2) - let ch, cl := mul_64x64_128(x2, y1) - let dh, dl := mul_64x64_128(x2, y2) - - r4 := dl - - let carry1, carry2 - let t1, t2 - - r3, carry1 := add_carry(bl, cl, 0) - r3, carry2 := add_carry(r3, dh, 0) - - t1, carry1 := add_carry(bh, ch, carry1) - r2, carry2 := add_carry(t1, al, carry2) - - r1 := i64.add(i64.add(ah, carry1), carry2) -} -function mul(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO it would actually suffice to have mul_128x128_128 for the first two. - let b1, b2, b3, b4 := mul_128x128_256(x3, x4, y1, y2) - let c1, c2, c3, c4 := mul_128x128_256(x1, x2, y3, y4) - let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) - r4 := d4 - r3 := d3 - let t1, t2 - t1, t2, r1, r2 := add(0, 0, b3, b4, 0, 0, c3, c4) - t1, t2, r1, r2 := add(0, 0, r1, r2, 0, 0, d1, d2) -} -function div(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - r4 := i64.div_u(x4, y4) -} -function sdiv(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - unreachable() -} -function mod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - r4 := i64.rem_u(x4, y4) -} -function smod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - r4 := i64.rem_u(x4, y4) -} -function exp(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - // TODO implement properly - unreachable() -} - -function byte(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - if i64.eqz(i64.or(i64.or(x1, x2), x3)) { - let component - switch i64.div_u(x4, 8) - case 0 { component := y1 } - case 1 { component := y2 } - case 2 { component := y3 } - case 3 { component := y4 } - x4 := i64.mul(i64.rem_u(x4, 8), 8) - r4 := i64.shr_u(component, i64.sub(56, x4)) - r4 := i64.and(0xff, r4) - } -} -function xor(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - r1 := i64.xor(x1, y1) - r2 := i64.xor(x2, y2) - r3 := i64.xor(x3, y3) - r4 := i64.xor(x4, y4) -} -function or(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - r1 := i64.or(x1, y1) - r2 := i64.or(x2, y2) - r3 := i64.or(x3, y3) - r4 := i64.or(x4, y4) -} -function and(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - r1 := i64.and(x1, y1) - r2 := i64.and(x2, y2) - r3 := i64.and(x3, y3) - r4 := i64.and(x4, y4) -} -function not(x1, x2, x3, x4) -> r1, r2, r3, r4 { - let mask := 0xffffffffffffffff - r1, r2, r3, r4 := xor(x1, x2, x3, x4, mask, mask, mask, mask) -} -function iszero(x1, x2, x3, x4) -> r1, r2, r3, r4 { - r4 := i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))) -} -function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - if i64.eq(x1, y1) { - if i64.eq(x2, y2) { - if i64.eq(x3, y3) { - if i64.eq(x4, y4) { - r4 := 1 - } - } - } - } -} - -// returns 0 if a == b, -1 if a < b and 1 if a > b -function cmp(a, b) -> r { - switch i64.lt_u(a, b) - case 1 { r := 0xffffffffffffffff } - default { - r := i64.ne(a, b) - } -} -)" -// Split long string to make it compilable on msvc -// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/compiler-error-c2026?view=vs-2019 -R"( -function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - switch cmp(x1, y1) - case 0 { - switch cmp(x2, y2) - case 0 { - switch cmp(x3, y3) - case 0 { - z4 := i64.lt_u(x4, y4) - } - case 1 { z4 := 0 } - default { z4 := 1 } - } - case 1 { z4 := 0 } - default { z4 := 1 } - } - case 1 { z4 := 0 } - default { z4 := 1 } -} -function gt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - z1, z2, z3, z4 := lt(y1, y2, y3, y4, x1, x2, x3, x4) -} -function slt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO correct? - x1 := i64.add(x1, 0x8000000000000000) - y1 := i64.add(y1, 0x8000000000000000) - z1, z2, z3, z4 := lt(x1, x2, x3, x4, y1, y2, y3, y4) -} -function sgt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - z1, z2, z3, z4 := slt(y1, y2, y3, y4, x1, x2, x3, x4) -} - -function shl_single(a, amount) -> x, y { - // amount < 64 - x := i64.shr_u(a, i64.sub(64, amount)) - y := i64.shl(a, amount) -} - -function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i64.and(i64.eqz(x1), i64.eqz(x2)) { - if i64.eqz(x3) { - if i64.lt_u(x4, 256) { - if i64.ge_u(x4, 128) { - y1 := y3 - y2 := y4 - y3 := 0 - y4 := 0 - x4 := i64.sub(x4, 128) - } - if i64.ge_u(x4, 64) { - y1 := y2 - y2 := y3 - y3 := y4 - y4 := 0 - x4 := i64.sub(x4, 64) - } - let t, r - t, z4 := shl_single(y4, x4) - r, z3 := shl_single(y3, x4) - z3 := i64.or(z3, t) - t, z2 := shl_single(y2, x4) - z2 := i64.or(z2, r) - r, z1 := shl_single(y1, x4) - z1 := i64.or(z1, t) - } - } - } -} - -function shr_single(a, amount) -> x, y { - // amount < 64 - y := i64.shl(a, i64.sub(64, amount)) - x := i64.shr_u(a, amount) -} - -function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - if i64.and(i64.eqz(x1), i64.eqz(x2)) { - if i64.eqz(x3) { - if i64.lt_u(x4, 256) { - if i64.ge_u(x4, 128) { - y4 := y2 - y3 := y1 - y2 := 0 - y1 := 0 - x4 := i64.sub(x4, 128) - } - if i64.ge_u(x4, 64) { - y4 := y3 - y3 := y2 - y2 := y1 - y1 := 0 - x4 := i64.sub(x4, 64) - } - let t - z4, t := shr_single(y4, x4) - z3, t := shr_single(y3, x4) - z4 := i64.or(z4, t) - z2, t := shr_single(y2, x4) - z3 := i64.or(z3, t) - z1, t := shr_single(y1, x4) - z2 := i64.or(z2, t) - } - } - } -} -function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} -function addmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} -function mulmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} -function signextend(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} -function u256_to_u128(x1, x2, x3, x4) -> v1, v2 { - if i64.ne(0, i64.or(x1, x2)) { invalid() } - v2 := x4 - v1 := x3 -} - -function u256_to_i64(x1, x2, x3, x4) -> v { - if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } - v := x4 -} - -function u256_to_i32(x1, x2, x3, x4) -> v { - if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } - if i64.ne(0, i64.shr_u(x4, 32)) { invalid() } - v := x4 -} - -function u256_to_byte(x1, x2, x3, x4) -> v { - if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } - if i64.gt_u(x4, 255) { invalid() } - v := x4 -} - -function u256_to_i32ptr(x1, x2, x3, x4) -> v { - v := u256_to_i32(x1, x2, x3, x4) -} - -function to_internal_i32ptr(x1, x2, x3, x4) -> r { - let p := u256_to_i32ptr(x1, x2, x3, x4) - r := i64.add(p, 64) - if i64.lt_u(r, p) { invalid() } -} - -function u256_to_address(x1, x2, x3, x4) -> r1, r2, r3 { - if i64.ne(0, x1) { invalid() } - if i64.ne(0, i64.shr_u(x2, 32)) { invalid() } - r1 := x2 - r2 := x3 - r3 := x4 -} - -function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} - -function address() -> z1, z2, z3, z4 { - eth.getAddress(0) - z1, z2, z3, z4 := mload_internal(0) -} -function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 { - mstore_address(0, x1, x2, x3, x4) - eth.getExternalBalance(12, 48) - z1, z2, z3, z4 := mload_internal(32) -} -function selfbalance() -> z1, z2, z3, z4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function chainid() -> z1, z2, z3, z4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function origin() -> z1, z2, z3, z4 { - eth.getTxOrigin(0) - z1, z2, z3, z4 := mload_internal(0) -} -function caller() -> z1, z2, z3, z4 { - eth.getCaller(0) - z1, z2, z3, z4 := mload_internal(0) -} -function callvalue() -> z1, z2, z3, z4 { - eth.getCallValue(0) - z1, z2, z3, z4 := mload_internal(0) -} -function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - eth.callDataCopy(0, u256_to_i32(x1, x2, x3, x4), 32) - z1, z2, z3, z4 := mload_internal(0) -} -function calldatasize() -> z1, z2, z3, z4 { - z4 := eth.getCallDataSize() -} -function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - eth.callDataCopy( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4), - u256_to_i32(z1, z2, z3, z4) - ) -} - -// Needed? -function codesize() -> z1, z2, z3, z4 { - z4 := eth.getCodeSize() -} -function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - eth.codeCopy( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4), - u256_to_i32(z1, z2, z3, z4) - ) -} -function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - // TODO correct? - codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) -} - -function gasprice() -> z1, z2, z3, z4 { - eth.getTxGasPrice(0) - z1, z2, z3, z4 := mload_internal(0) -} -function extcodesize_internal(x1, x2, x3, x4) -> r { - mstore_address(0, x1, x2, x3, x4) - r := eth.getExternalCodeSize(12) -} -function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 { - z4 := extcodesize_internal(x1, x2, x3, x4) -} -function extcodehash(x1, x2, x3, x4) -> z1, z2, z3, z4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function extcodecopy(a1, a2, a3, a4, p1, p2, p3, p4, o1, o2, o3, o4, l1, l2, l3, l4) { - mstore_address(0, a1, a2, a3, a4) - let codeOffset := u256_to_i32(o1, o2, o3, o4) - let codeLength := u256_to_i32(l1, l2, l3, l4) - eth.externalCodeCopy(12, to_internal_i32ptr(p1, p2, p3, p4), codeOffset, codeLength) -} - -function returndatasize() -> z1, z2, z3, z4 { - z4 := eth.getReturnDataSize() -} -function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { - eth.returnDataCopy( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4), - u256_to_i32(z1, z2, z3, z4) - ) -} - -function blockhash(x1, x2, x3, x4) -> z1, z2, z3, z4 { - let r := eth.getBlockHash(u256_to_i64(x1, x2, x3, x4), 0) - if i64.eqz(r) { - z1, z2, z3, z4 := mload_internal(0) - } -} -function coinbase() -> z1, z2, z3, z4 { - eth.getBlockCoinbase(0) - z1, z2, z3, z4 := mload_internal(0) -} -function timestamp() -> z1, z2, z3, z4 { - z4 := eth.getBlockTimestamp() -} -function number() -> z1, z2, z3, z4 { - z4 := eth.getBlockNumber() -} -function difficulty() -> z1, z2, z3, z4 { - eth.getBlockDifficulty(0) - z1, z2, z3, z4 := mload_internal(0) -} -function gaslimit() -> z1, z2, z3, z4 { - z4 := eth.getBlockGasLimit() -} - -function pop(x1, x2, x3, x4) { -} - - -function endian_swap_16(x) -> y { - let hi := i64.and(i64.shl(x, 8), 0xff00) - let lo := i64.and(i64.shr_u(x, 8), 0xff) - y := i64.or(hi, lo) -} - -function endian_swap_32(x) -> y { - let hi := i64.shl(endian_swap_16(x), 16) - let lo := endian_swap_16(i64.shr_u(x, 16)) - y := i64.or(hi, lo) -} - -function endian_swap(x) -> y { - let hi := i64.shl(endian_swap_32(x), 32) - let lo := endian_swap_32(i64.shr_u(x, 32)) - y := i64.or(hi, lo) -} -function save_temp_mem_32() -> t1, t2, t3, t4 { - t1 := i64.load(0) - t2 := i64.load(8) - t3 := i64.load(16) - t4 := i64.load(24) -} -function restore_temp_mem_32(t1, t2, t3, t4) { - i64.store(0, t1) - i64.store(8, t2) - i64.store(16, t3) - i64.store(24, t4) -} -function save_temp_mem_64() -> t1, t2, t3, t4, t5, t6, t7, t8 { - t1 := i64.load(0) - t2 := i64.load(8) - t3 := i64.load(16) - t4 := i64.load(24) - t5 := i64.load(32) - t6 := i64.load(40) - t7 := i64.load(48) - t8 := i64.load(54) -} -function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) { - i64.store(0, t1) - i64.store(8, t2) - i64.store(16, t3) - i64.store(24, t4) - i64.store(32, t5) - i64.store(40, t6) - i64.store(48, t7) - i64.store(54, t8) -} -function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - z1, z2, z3, z4 := mload_internal(to_internal_i32ptr(x1, x2, x3, x4)) -} -function mload_internal(pos) -> z1, z2, z3, z4 { - z1 := endian_swap(i64.load(pos)) - z2 := endian_swap(i64.load(i64.add(pos, 8))) - z3 := endian_swap(i64.load(i64.add(pos, 16))) - z4 := endian_swap(i64.load(i64.add(pos, 24))) -} -function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { - mstore_internal(to_internal_i32ptr(x1, x2, x3, x4), y1, y2, y3, y4) -} -function mstore_internal(pos, y1, y2, y3, y4) { - i64.store(pos, endian_swap(y1)) - i64.store(i64.add(pos, 8), endian_swap(y2)) - i64.store(i64.add(pos, 16), endian_swap(y3)) - i64.store(i64.add(pos, 24), endian_swap(y4)) -} -function mstore_address(pos, a1, a2, a3, a4) { - a1, a2, a3 := u256_to_address(a1, a2, a3, a4) - mstore_internal(pos, 0, a1, a2, a3) -} -function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) { - let v := u256_to_byte(y1, y2, y3, y4) - i64.store8(to_internal_i32ptr(x1, x2, x3, x4), v) -} -// Needed? -function msize() -> z1, z2, z3, z4 { - // TODO implement - unreachable() -} -function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 { - mstore_internal(0, x1, x2, x3, x4) - eth.storageLoad(0, 32) - z1, z2, z3, z4 := mload_internal(32) -} - -function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { - mstore_internal(0, x1, x2, x3, x4) - mstore_internal(32, y1, y2, y3, y4) - eth.storageStore(0, 32) -} - -function gas() -> z1, z2, z3, z4 { - z4 := eth.getGasLeft() -} - -function log0(p1, p2, p3, p4, s1, s2, s3, s4) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 0, 0, 0, 0, 0 - ) -} -function log1( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4 -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 1, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - 0, 0, 0 - ) -} -function log2( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4, - t2_1, t2_2, t2_3, t2_4 -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 2, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), - 0, 0 - ) -} -function log3( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4, - t2_1, t2_2, t2_3, t2_4, - t3_1, t3_2, t3_3, t3_4 -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 3, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), - to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), - 0 - ) -} -function log4( - p1, p2, p3, p4, s1, s2, s3, s4, - t1_1, t1_2, t1_3, t1_4, - t2_1, t2_2, t2_3, t2_4, - t3_1, t3_2, t3_3, t3_4, - t4_1, t4_2, t4_3, t4_4, -) { - eth.log( - to_internal_i32ptr(p1, p2, p3, p4), - u256_to_i32(s1, s2, s3, s4), - 4, - to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), - to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), - to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), - to_internal_i32ptr(t4_1, t4_2, t4_3, t4_4) - ) -} - -function create( - x1, x2, x3, x4, - y1, y2, y3, y4, - z1, z2, z3, z4 -) -> a1, a2, a3, a4 { - let v1, v2 := u256_to_u128(x1, x2, x3, x4) - mstore_internal(0, 0, 0, v1, v2) - - let r := eth.create(0, to_internal_i32ptr(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4), 32) - if i64.eqz(r) { - a1, a2, a3, a4 := mload_internal(32) - } -} -function call( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4, - g1, g2, g3, g4 -) -> x1, x2, x3, x4 { - let g := u256_to_i64(a1, a2, a3, a4) - mstore_address(0, b1, b2, b3, b4) - - let v1, v2 := u256_to_u128(c1, c2, c3, c4) - mstore_internal(32, 0, 0, v1, v2) - - x4 := eth.call(g, 12, 32, to_internal_i32ptr(d1, d2, d3, d4), u256_to_i32(e1, e2, e3, e4)) -} -function callcode( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4, - g1, g2, g3, g4 -) -> x1, x2, x3, x4 { - mstore_address(0, b1, b2, b3, b4) - - let v1, v2 := u256_to_u128(c1, c2, c3, c4) - mstore_internal(32, 0, 0, v1, v2) - - x4 := eth.callCode( - u256_to_i64(a1, a2, a3, a4), - 12, - 32, - to_internal_i32ptr(d1, d2, d3, d4), - u256_to_i32(e1, e2, e3, e4) - ) -} -function delegatecall( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4 -) -> x1, x2, x3, x4 { - mstore_address(0, b1, b2, b3, b4) - - x4 := eth.callDelegate( - u256_to_i64(a1, a2, a3, a4), - 12, - to_internal_i32ptr(c1, c2, c3, c4), - u256_to_i32(d1, d2, d3, d4) - ) -} -function staticcall( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4, - e1, e2, e3, e4, - f1, f2, f3, f4 -) -> x1, x2, x3, x4 { - mstore_address(0, b1, b2, b3, b4) - - x4 := eth.callStatic( - u256_to_i64(a1, a2, a3, a4), - 12, - to_internal_i32ptr(c1, c2, c3, c4), - u256_to_i32(d1, d2, d3, d4) - ) -} -function create2( - a1, a2, a3, a4, - b1, b2, b3, b4, - c1, c2, c3, c4, - d1, d2, d3, d4 -) -> x1, x2, x3, x4 { - // TODO: not part of current Ewasm spec - unreachable() -} -function selfdestruct(a1, a2, a3, a4) { - mstore_address(0, a1, a2, a3, a4) - // In EVM, addresses are padded to 32 bytes, so discard the first 12. - eth.selfDestruct(12) -} - -function return(x1, x2, x3, x4, y1, y2, y3, y4) { - eth.finish( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4) - ) -} -function revert(x1, x2, x3, x4, y1, y2, y3, y4) { - eth.revert( - to_internal_i32ptr(x1, x2, x3, x4), - u256_to_i32(y1, y2, y3, y4) - ) -} -function invalid() { - unreachable() -} -})"}; - -} - Object EVMToEwasmTranslator::run(Object const& _object) { if (!m_polyfill) @@ -801,7 +72,7 @@ Object EVMToEwasmTranslator::run(Object const& _object) FunctionHoister::run(context, ast); FunctionGrouper::run(context, ast); - MainFunction{}(ast); + MainFunction::run(context, ast); ForLoopConditionIntoBody::run(context, ast); ExpressionSplitter::run(context, ast); WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser); @@ -820,8 +91,12 @@ Object EVMToEwasmTranslator::run(Object const& _object) AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.qualifiedDataNames()); if (!analyzer.analyze(*ret.code)) { - // TODO the errors here are "wrong" because they have invalid source references! - string message; + string message = "Invalid code generated after EVM to wasm translation.\n"; + message += "Note that the source locations in the errors below will reference the original, not the translated code.\n"; + message += "Translated code:\n"; + message += "----------------------------------\n"; + message += ret.toString(&WasmDialect::instance()); + message += "----------------------------------\n"; for (auto const& err: errors) message += langutil::SourceReferenceFormatter::formatErrorInformation(*err); yulAssert(false, message); @@ -841,7 +116,17 @@ void EVMToEwasmTranslator::parsePolyfill() { ErrorList errors; ErrorReporter errorReporter(errors); - shared_ptr scanner{make_shared(CharStream(polyfill, ""))}; + shared_ptr scanner{make_shared(CharStream( + "{" + + string(solidity::yul::wasm::polyfill::Arithmetic) + + string(solidity::yul::wasm::polyfill::Bitwise) + + string(solidity::yul::wasm::polyfill::Comparison) + + string(solidity::yul::wasm::polyfill::Conversion) + + string(solidity::yul::wasm::polyfill::Interface) + + string(solidity::yul::wasm::polyfill::Keccak) + + string(solidity::yul::wasm::polyfill::Logical) + + string(solidity::yul::wasm::polyfill::Memory) + + "}", ""))}; m_polyfill = Parser(errorReporter, WasmDialect::instance()).parse(scanner, false); if (!errors.empty()) { diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.h b/libyul/backends/wasm/EVMToEwasmTranslator.h index ecb34ec1120b..251e3352b498 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.h +++ b/libyul/backends/wasm/EVMToEwasmTranslator.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/backends/wasm/TextTransform.cpp b/libyul/backends/wasm/TextTransform.cpp index 059f8960021a..65c2bbb37769 100644 --- a/libyul/backends/wasm/TextTransform.cpp +++ b/libyul/backends/wasm/TextTransform.cpp @@ -19,10 +19,13 @@ * Component that transforms internal Wasm representation to text. */ +#include #include #include +#include +#include #include #include @@ -39,11 +42,27 @@ using namespace solidity::util; string TextTransform::run(wasm::Module const& _module) { string ret = "(module\n"; - for (auto const& sub: _module.subModules) + for (auto const& [name, module]: _module.subModules) ret += - " ;; sub-module \"" + - sub.first + - "\" will be encoded as custom section in binary here, but is skipped in text mode.\n"; + " ;; custom section for sub-module\n" + " ;; The Keccak-256 hash of the text representation of \"" + + name + + "\": " + + toHex(keccak256(run(module))) + + "\n" + " ;; (@custom \"" + + name + + "\" \"" + + toHex(BinaryTransform::run(module)) + + "\")\n"; + for (auto const& [name, data]: _module.customSections) + ret += + " ;; custom section for data\n" + " ;; (@custom \"" + + name + + "\" \"" + + toHex(data) + + "\")\n"; for (wasm::FunctionImport const& imp: _module.imports) { ret += " (import \"" + imp.module + "\" \"" + imp.externalName + "\" (func $" + imp.internalName; @@ -56,8 +75,13 @@ string TextTransform::run(wasm::Module const& _module) // allocate one 64k page of memory and make it available to the Ethereum client ret += " (memory $memory (export \"memory\") 1)\n"; - // export the main function - ret += " (export \"main\" (func $main))\n"; + for (auto const& f: _module.functions) + if (f.name == "main") + { + // export the main function + ret += " (export \"main\" (func $main))\n"; + break; + } for (auto const& g: _module.globals) ret += " (global $" + g.variableName + " (mut " + encodeType(g.type) + ") (" + encodeType(g.type) + ".const 0))\n"; @@ -77,6 +101,8 @@ string TextTransform::operator()(wasm::Literal const& _literal) string TextTransform::operator()(wasm::StringLiteral const& _literal) { + // StringLiteral is a special AST element used for certain builtins. + // The output of this will not be valid WebAssembly. string quoted = boost::replace_all_copy(_literal.value, "\\", "\\\\"); boost::replace_all(quoted, "\"", "\\\""); return "\"" + quoted + "\""; @@ -96,8 +122,11 @@ string TextTransform::operator()(wasm::BuiltinCall const& _builtinCall) { string args = joinTransformed(_builtinCall.arguments); string funcName = _builtinCall.functionName; + // These are prefixed in the dialect, but are actually overloaded instructions in WebAssembly. if (funcName == "i32.drop" || funcName == "i64.drop") funcName = "drop"; + else if (funcName == "i32.select" || funcName == "i64.select") + funcName = "select"; return "(" + funcName + (args.empty() ? "" : " " + args) + ")"; } diff --git a/libyul/backends/wasm/WasmAST.h b/libyul/backends/wasm/WasmAST.h index ced3b7bc6492..67b642ec9b88 100644 --- a/libyul/backends/wasm/WasmAST.h +++ b/libyul/backends/wasm/WasmAST.h @@ -21,6 +21,8 @@ #pragma once +#include + #include #include #include @@ -61,6 +63,7 @@ using Expression = std::variant< >; struct Literal { std::variant value; }; +// This is a special AST element used for certain builtins. It is not mapped to actual WebAssembly. struct StringLiteral { std::string value; }; struct LocalVariable { std::string name; }; struct GlobalVariable { std::string name; }; @@ -108,6 +111,7 @@ struct Module std::vector imports; std::vector functions; std::map subModules; + std::map customSections; }; } diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index de87028a9864..b6bc026a4a3e 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -25,7 +25,7 @@ #include -#include +#include #include #include #include @@ -90,7 +90,7 @@ wasm::Expression WasmCodeTransform::generateMultiAssignment( return { std::move(block) }; } -wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDecl) +wasm::Expression WasmCodeTransform::operator()(yul::VariableDeclaration const& _varDecl) { vector variableNames; for (auto const& var: _varDecl.variables) @@ -105,7 +105,7 @@ wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDe return wasm::BuiltinCall{"nop", {}}; } -wasm::Expression WasmCodeTransform::operator()(Assignment const& _assignment) +wasm::Expression WasmCodeTransform::operator()(yul::Assignment const& _assignment) { vector variableNames; for (auto const& var: _assignment.variableNames) @@ -113,12 +113,12 @@ wasm::Expression WasmCodeTransform::operator()(Assignment const& _assignment) return generateMultiAssignment(move(variableNames), visit(*_assignment.value)); } -wasm::Expression WasmCodeTransform::operator()(ExpressionStatement const& _statement) +wasm::Expression WasmCodeTransform::operator()(yul::ExpressionStatement const& _statement) { return visitReturnByValue(_statement.expression); } -wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) +wasm::Expression WasmCodeTransform::operator()(yul::FunctionCall const& _call) { if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name)) { @@ -140,22 +140,20 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) m_functionsToImport[builtin->name] = std::move(imp); } } - else if (builtin->literalArguments && contains(builtin->literalArguments.value(), true)) + else { - vector literals; + vector arguments; for (size_t i = 0; i < _call.arguments.size(); i++) - if (builtin->literalArguments.value()[i]) - literals.emplace_back(wasm::StringLiteral{std::get(_call.arguments[i]).value.str()}); + if (builtin->literalArgument(i)) + { + yulAssert(builtin->literalArgument(i) == LiteralKind::String, ""); + arguments.emplace_back(wasm::StringLiteral{std::get(_call.arguments[i]).value.str()}); + } else - literals.emplace_back(visitReturnByValue(_call.arguments[i])); + arguments.emplace_back(visitReturnByValue(_call.arguments[i])); - return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)}; + return wasm::BuiltinCall{_call.functionName.name.str(), std::move(arguments)}; } - else - return wasm::BuiltinCall{ - _call.functionName.name.str(), - visit(_call.arguments) - }; } // If this function returns multiple values, then the first one will @@ -166,17 +164,17 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call) return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)}; } -wasm::Expression WasmCodeTransform::operator()(Identifier const& _identifier) +wasm::Expression WasmCodeTransform::operator()(yul::Identifier const& _identifier) { return wasm::LocalVariable{_identifier.name.str()}; } -wasm::Expression WasmCodeTransform::operator()(Literal const& _literal) +wasm::Expression WasmCodeTransform::operator()(yul::Literal const& _literal) { return makeLiteral(translatedType(_literal.type), valueOfLiteral(_literal)); } -wasm::Expression WasmCodeTransform::operator()(If const& _if) +wasm::Expression WasmCodeTransform::operator()(yul::If const& _if) { yul::Type conditionType = m_typeInfo.typeOf(*_if.condition); @@ -198,7 +196,7 @@ wasm::Expression WasmCodeTransform::operator()(If const& _if) return wasm::If{make_unique(move(condition)), visit(_if.body.statements), {}}; } -wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) +wasm::Expression WasmCodeTransform::operator()(yul::Switch const& _switch) { yul::Type expressionType = m_typeInfo.typeOf(*_switch.expression); YulString eq_instruction = YulString(expressionType.str() + ".eq"); @@ -242,13 +240,13 @@ wasm::Expression WasmCodeTransform::operator()(Switch const& _switch) return { std::move(block) }; } -wasm::Expression WasmCodeTransform::operator()(FunctionDefinition const&) +wasm::Expression WasmCodeTransform::operator()(yul::FunctionDefinition const&) { yulAssert(false, "Should not have visited here."); return {}; } -wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) +wasm::Expression WasmCodeTransform::operator()(yul::ForLoop const& _for) { string breakLabel = newLabel(); string continueLabel = newLabel(); @@ -275,23 +273,25 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for) return wasm::Block{breakLabel, move(statements)}; } -wasm::Expression WasmCodeTransform::operator()(Break const&) +wasm::Expression WasmCodeTransform::operator()(yul::Break const&) { + yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().first}}; } -wasm::Expression WasmCodeTransform::operator()(Continue const&) +wasm::Expression WasmCodeTransform::operator()(yul::Continue const&) { + yulAssert(m_breakContinueLabelNames.size() > 0, ""); return wasm::Branch{wasm::Label{m_breakContinueLabelNames.top().second}}; } -wasm::Expression WasmCodeTransform::operator()(Leave const&) +wasm::Expression WasmCodeTransform::operator()(yul::Leave const&) { yulAssert(!m_functionBodyLabel.empty(), ""); return wasm::Branch{wasm::Label{m_functionBodyLabel}}; } -wasm::Expression WasmCodeTransform::operator()(Block const& _block) +wasm::Expression WasmCodeTransform::operator()(yul::Block const& _block) { return wasm::Block{{}, visit(_block.statements)}; } diff --git a/libyul/backends/wasm/WasmCodeTransform.h b/libyul/backends/wasm/WasmCodeTransform.h index 79d3552c6474..1e9f1fd9cd55 100644 --- a/libyul/backends/wasm/WasmCodeTransform.h +++ b/libyul/backends/wasm/WasmCodeTransform.h @@ -22,7 +22,7 @@ #pragma once #include -#include +#include #include #include #include diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index edf113a13a5b..eb2d1712c11c 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -21,6 +21,7 @@ #include +#include #include using namespace std; @@ -85,42 +86,54 @@ WasmDialect::WasmDialect() addFunction("i64.extend_i32_u", {i32}, {i64}); addFunction("i32.store", {i32, i32}, {}, false); - m_functions["i32.store"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i32.store"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.store"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i64.store", {i32, i64}, {}, false); - m_functions["i64.store"_yulstring].sideEffects.invalidatesStorage = false; // TODO: add i32.store16, i64.store8, i64.store16, i64.store32 + m_functions["i64.store"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.store"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i32.store8", {i32, i32}, {}, false); - m_functions["i32.store8"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i32.store8"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.store8"_yulstring].sideEffects.otherState = SideEffects::None; + addFunction("i64.store8", {i32, i64}, {}, false); - m_functions["i64.store8"_yulstring].sideEffects.invalidatesStorage = false; + m_functions["i64.store8"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.store8"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i32.load", {i32}, {i32}, false); - m_functions["i32.load"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["i32.load"_yulstring].sideEffects.invalidatesMemory = false; - m_functions["i32.load"_yulstring].sideEffects.sideEffectFree = true; - m_functions["i32.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; + m_functions["i32.load"_yulstring].sideEffects.canBeRemoved = true; + m_functions["i32.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true; + m_functions["i32.load"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i32.load"_yulstring].sideEffects.memory = SideEffects::Read; + m_functions["i32.load"_yulstring].sideEffects.otherState = SideEffects::None; addFunction("i64.load", {i32}, {i64}, false); - m_functions["i64.load"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["i64.load"_yulstring].sideEffects.invalidatesMemory = false; - m_functions["i64.load"_yulstring].sideEffects.sideEffectFree = true; - m_functions["i64.load"_yulstring].sideEffects.sideEffectFreeIfNoMSize = true; // TODO: add i32.load8, i32.load16, i64.load8, i64.load16, i64.load32 + m_functions["i64.load"_yulstring].sideEffects.canBeRemoved = true; + m_functions["i64.load"_yulstring].sideEffects.canBeRemovedIfNoMSize = true; + m_functions["i64.load"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["i64.load"_yulstring].sideEffects.memory = SideEffects::Read; + m_functions["i64.load"_yulstring].sideEffects.otherState = SideEffects::None; // Drop is actually overloaded for all types, but Yul does not support that. // Because of that, we introduce "i32.drop" and "i64.drop". addFunction("i32.drop", {i32}, {}); addFunction("i64.drop", {i64}, {}); + // Select is also overloaded. + addFunction("i32.select", {i32, i32, i32}, {i32}); + addFunction("i64.select", {i64, i64, i32}, {i64}); + addFunction("nop", {}, {}); addFunction("unreachable", {}, {}, false); - m_functions["unreachable"_yulstring].sideEffects.invalidatesStorage = false; - m_functions["unreachable"_yulstring].sideEffects.invalidatesMemory = false; + m_functions["unreachable"_yulstring].sideEffects.storage = SideEffects::None; + m_functions["unreachable"_yulstring].sideEffects.memory = SideEffects::None; + m_functions["unreachable"_yulstring].sideEffects.otherState = SideEffects::None; m_functions["unreachable"_yulstring].controlFlowSideEffects.terminates = true; m_functions["unreachable"_yulstring].controlFlowSideEffects.reverts = true; - addFunction("datasize", {i64}, {i64}, true, {true}); - addFunction("dataoffset", {i64}, {i64}, true, {true}); + addFunction("datasize", {i64}, {i64}, true, {LiteralKind::String}); + addFunction("dataoffset", {i64}, {i64}, true, {LiteralKind::String}); addEthereumExternals(); } @@ -218,10 +231,24 @@ void WasmDialect::addEthereumExternals() f.returns.emplace_back(YulString(p)); // TODO some of them are side effect free. f.sideEffects = SideEffects::worst(); + f.sideEffects.cannotLoop = true; + f.sideEffects.movableApartFromEffects = !ext.controlFlowSideEffects.terminates; f.controlFlowSideEffects = ext.controlFlowSideEffects; f.isMSize = false; - f.sideEffects.invalidatesStorage = (ext.name == "storageStore"); - f.literalArguments.reset(); + f.literalArguments.clear(); + + static set const writesToStorage{ + "storageStore", + "call", + "callcode", + "callDelegate", + "create" + }; + static set const readsStorage{"storageLoad", "callStatic"}; + if (readsStorage.count(ext.name)) + f.sideEffects.storage = SideEffects::Read; + else if (!writesToStorage.count(ext.name)) + f.sideEffects.storage = SideEffects::None; } } @@ -230,7 +257,7 @@ void WasmDialect::addFunction( vector _params, vector _returns, bool _movable, - std::vector _literalArguments + vector> _literalArguments ) { YulString name{move(_name)}; @@ -240,9 +267,9 @@ void WasmDialect::addFunction( yulAssert(_returns.size() <= 1, "The Wasm 1.0 specification only allows up to 1 return value."); f.returns = std::move(_returns); f.sideEffects = _movable ? SideEffects{} : SideEffects::worst(); + f.sideEffects.cannotLoop = true; + // TODO This should be improved when LoopInvariantCodeMotion gets specialized for WASM + f.sideEffects.movableApartFromEffects = _movable; f.isMSize = false; - if (!_literalArguments.empty()) - f.literalArguments = std::move(_literalArguments); - else - f.literalArguments.reset(); + f.literalArguments = std::move(_literalArguments); } diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index a718f0b743af..5abc69fc4fb9 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -38,7 +38,7 @@ struct Object; * * Builtin functions are a subset of the wasm instructions. * - * There is a builtin function `i32.drop` that takes an i32, while `drop` takes i64. + * There is a builtin function `i32.drop` that takes an i32, while `i64.drop` takes i64. * */ struct WasmDialect: public Dialect @@ -62,7 +62,7 @@ struct WasmDialect: public Dialect std::vector _params, std::vector _returns, bool _movable = true, - std::vector _literalArguments = std::vector{} + std::vector> _literalArguments = std::vector>{} ); std::map m_functions; diff --git a/libyul/backends/wasm/WasmObjectCompiler.cpp b/libyul/backends/wasm/WasmObjectCompiler.cpp index 2b4f0c3558e4..bdd109d31ccf 100644 --- a/libyul/backends/wasm/WasmObjectCompiler.cpp +++ b/libyul/backends/wasm/WasmObjectCompiler.cpp @@ -51,8 +51,10 @@ wasm::Module WasmObjectCompiler::run(Object& _object) for (auto& subNode: _object.subObjects) if (Object* subObject = dynamic_cast(subNode.get())) module.subModules[subObject->name.str()] = run(*subObject); + else if (Data* subObject = dynamic_cast(subNode.get())) + module.customSections[subObject->name.str()] = subObject->data; else - yulAssert(false, "Data is not yet supported for Wasm."); + yulAssert(false, ""); return module; } diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 73dda9d0c663..ec5e3a0deadb 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include #include #include @@ -42,16 +42,16 @@ void WordSizeTransform::operator()(FunctionDefinition& _fd) void WordSizeTransform::operator()(FunctionCall& _fc) { - vector const* literalArguments = nullptr; + vector> const* literalArguments = nullptr; if (BuiltinFunction const* fun = m_inputDialect.builtin(_fc.functionName.name)) - if (fun->literalArguments) - literalArguments = &fun->literalArguments.value(); + if (!fun->literalArguments.empty()) + literalArguments = &fun->literalArguments; vector newArgs; for (size_t i = 0; i < _fc.arguments.size(); i++) - if (!literalArguments || !(*literalArguments)[i]) + if (!literalArguments || !(*literalArguments)[i].has_value()) newArgs += expandValueToVector(_fc.arguments[i]); else { @@ -109,7 +109,8 @@ void WordSizeTransform::operator()(Block& _block) if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*varDecl.value).functionName.name)) if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring) { - yulAssert(f->literalArguments && f->literalArguments.value()[0], ""); + yulAssert(f->literalArguments.size() == 1, ""); + yulAssert(f->literalArguments.at(0) == LiteralKind::String, ""); yulAssert(varDecl.variables.size() == 1, ""); auto newLhs = generateU64IdentifierNames(varDecl.variables[0].name); vector ret; @@ -169,7 +170,8 @@ void WordSizeTransform::operator()(Block& _block) if (BuiltinFunction const* f = m_inputDialect.builtin(std::get(*assignment.value).functionName.name)) if (f->name == "datasize"_yulstring || f->name == "dataoffset"_yulstring) { - yulAssert(f->literalArguments && f->literalArguments.value()[0], ""); + yulAssert(f->literalArguments.size() == 1, ""); + yulAssert(f->literalArguments[0] == LiteralKind::String, ""); yulAssert(assignment.variableNames.size() == 1, ""); auto newLhs = generateU64IdentifierNames(assignment.variableNames[0].name); vector ret; diff --git a/libyul/backends/wasm/polyfill/Arithmetic.yul b/libyul/backends/wasm/polyfill/Arithmetic.yul new file mode 100644 index 000000000000..7a4a80f5f1d5 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Arithmetic.yul @@ -0,0 +1,396 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Arithmetic.h`. + +// returns a + y + c plus carry value on 64 bit values. +// c should be at most 1 +function add_carry(x, y, c) -> r, r_c { + let t := i64.add(x, y) + r := i64.add(t, c) + r_c := i64.extend_i32_u(i32.or( + i64.lt_u(t, x), + i64.lt_u(r, t) + )) +} + +function add(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + let carry + r4, carry := add_carry(x4, y4, 0) + r3, carry := add_carry(x3, y3, carry) + r2, carry := add_carry(x2, y2, carry) + r1, carry := add_carry(x1, y1, carry) +} + +function sub(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // x - y = x + (~y + 1) + let carry + r4, carry := add_carry(x4, bit_negate(y4), 1) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} + +function sub320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { + // x - y = x + (~y + 1) + let carry + r5, carry := add_carry(x5, bit_negate(y5), 1) + r4, carry := add_carry(x4, bit_negate(y4), carry) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} + +function sub512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // x - y = x + (~y + 1) + let carry + r8, carry := add_carry(x8, bit_negate(y8), 1) + r7, carry := add_carry(x7, bit_negate(y7), carry) + r6, carry := add_carry(x6, bit_negate(y6), carry) + r5, carry := add_carry(x5, bit_negate(y5), carry) + r4, carry := add_carry(x4, bit_negate(y4), carry) + r3, carry := add_carry(x3, bit_negate(y3), carry) + r2, carry := add_carry(x2, bit_negate(y2), carry) + r1, carry := add_carry(x1, bit_negate(y1), carry) +} + +// Multiplies two 64 bit values resulting in a 128 bit +// value split into two 64 bit values. +function mul_64x64_128(x, y) -> hi, lo { + let xh, xl := split(x) + let yh, yl := split(y) + let t0 := i64.mul(xl, yl) + let t1 := i64.mul(xh, yl) + let t2 := i64.mul(xl, yh) + let t3 := i64.mul(xh, yh) + let t0h, t0l := split(t0) + let u1 := i64.add(t1, t0h) + let u1h, u1l := split(u1) + let u2 := i64.add(t2, u1l) + lo := i64.or(i64.shl(u2, 32), t0l) + hi := i64.add(t3, i64.add(i64.shr_u(u2, 32), u1h)) +} + +// Multiplies two 128 bit values resulting in a 256 bit +// value split into four 64 bit values. +function mul_128x128_256(x1, x2, y1, y2) -> r1, r2, r3, r4 { + let ah, al := mul_64x64_128(x1, y1) + let bh, bl := mul_64x64_128(x1, y2) + let ch, cl := mul_64x64_128(x2, y1) + let dh, dl := mul_64x64_128(x2, y2) + r4 := dl + let carry1, carry2 + let t1, t2 + r3, carry1 := add_carry(bl, cl, 0) + r3, carry2 := add_carry(r3, dh, 0) + t1, carry1 := add_carry(bh, ch, carry1) + r2, carry2 := add_carry(t1, al, carry2) + r1 := i64.add(i64.add(ah, carry1), carry2) +} + +// Multiplies two 256 bit values resulting in a 512 bit +// value split into eight 64 bit values. +function mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4, r5, r6, r7, r8 { + let a1, a2, a3, a4 := mul_128x128_256(x1, x2, y1, y2) + let b1, b2, b3, b4 := mul_128x128_256(x1, x2, y3, y4) + let c1, c2, c3, c4 := mul_128x128_256(x3, x4, y1, y2) + let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) + r8 := d4 + r7 := d3 + let carry1, carry2 + let t1, t2 + r6, carry1 := add_carry(b4, c4, 0) + r6, carry2 := add_carry(r6, d2, 0) + r5, carry1 := add_carry(b3, c3, carry1) + r5, carry2 := add_carry(r5, d1, carry2) + r4, carry1 := add_carry(a4, b2, carry1) + r4, carry2 := add_carry(r4, c2, carry2) + r3, carry1 := add_carry(a3, b1, carry1) + r3, carry2 := add_carry(r3, c1, carry2) + r2, carry1 := add_carry(a2, carry1, carry2) + r1 := i64.add(a1, carry1) +} + +function mul(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // TODO it would actually suffice to have mul_128x128_128 for the first two. + let b1, b2, b3, b4 := mul_128x128_256(x3, x4, y1, y2) + let c1, c2, c3, c4 := mul_128x128_256(x1, x2, y3, y4) + let d1, d2, d3, d4 := mul_128x128_256(x3, x4, y3, y4) + r4 := d4 + r3 := d3 + let t1, t2 + t1, t2, r1, r2 := add(0, 0, b3, b4, 0, 0, c3, c4) + t1, t2, r1, r2 := add(0, 0, r1, r2, 0, 0, d1, d2) +} + +function div(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/DIV.wast + if iszero256(y1, y2, y3, y4) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, x1, x2, x3, x4)) { + break + } + y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) + } + + for {} or_bool(m1, m2, m3, m4) {} { + if gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) { + x1, x2, x3, x4 := sub(x1, x2, x3, x4, y1, y2, y3, y4) + r1, r2, r3, r4 := add(r1, r2, r3, r4, m1, m2, m3, m4) + } + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) + } +} + +function sdiv(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SDIV.wast + + let sign:i32 := i32.wrap_i64(i64.shr_u(i64.xor(x1, y1), 63)) + + if i64.eqz(i64.clz(x1)) { + x1, x2, x3, x4 := xor( + x1, x2, x3, x4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) + } + + if i64.eqz(i64.clz(y1)) { + y1, y2, y3, y4 := xor( + y1, y2, y3, y4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) + } + + r1, r2, r3, r4 := div(x1, x2, x3, x4, y1, y2, y3, y4) + + if sign { + r1, r2, r3, r4 := xor( + r1, r2, r3, r4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) + } +} + +function mod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/MOD.wast + if iszero256(y1, y2, y3, y4) { + leave + } + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_256x256_64(y1, y2, y3, y4, r1, r2, r3, r4)) { + break + } + + y1, y2, y3, y4 := shl_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shl_internal(1, m1, m2, m3, m4) + } + + for {} or_bool(m1, m2, m3, m4) {} { + if gte_256x256_64(r1, r2, r3, r4, y1, y2, y3, y4) { + r1, r2, r3, r4 := sub(r1, r2, r3, r4, y1, y2, y3, y4) + } + + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + m1, m2, m3, m4 := shr_internal(1, m1, m2, m3, m4) + } +} + +function mod320(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> r1, r2, r3, r4, r5 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_320.wast + if iszero320(y1, y2, y3, y4, y5) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 0 + let m5 := 1 + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + r5 := x5 + + for {} true {} { + if i32.or(i64.eqz(i64.clz(y1)), gte_320x320_64(y1, y2, y3, y4, y5, r1, r2, r3, r4, r5)) { + break + } + y1, y2, y3, y4, y5 := shl320_internal(1, y1, y2, y3, y4, y5) + m1, m2, m3, m4, m5 := shl320_internal(1, m1, m2, m3, m4, m5) + } + + for {} or_bool_320(m1, m2, m3, m4, m5) {} { + if gte_320x320_64(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) { + r1, r2, r3, r4, r5 := sub320(r1, r2, r3, r4, r5, y1, y2, y3, y4, y5) + } + + y1, y2, y3, y4, y5 := shr320_internal(1, y1, y2, y3, y4, y5) + m1, m2, m3, m4, m5 := shr320_internal(1, m1, m2, m3, m4, m5) + } +} + +function mod512(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/mod_512.wast + if iszero512(y1, y2, y3, y4, y5, y6, y7, y8) { + leave + } + + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 0 + let m5 := 0 + let m6 := 0 + let m7 := 0 + let m8 := 1 + + r1 := x1 + r2 := x2 + r3 := x3 + r4 := x4 + r5 := x5 + r6 := x6 + r7 := x7 + r8 := x8 + + for {} true {} { + if i32.or( + i64.eqz(i64.clz(y1)), + gte_512x512_64(y1, y2, y3, y4, y5, y6, y7, y8, r1, r2, r3, r4, r5, r6, r7, r8) + ) + { + break + } + y1, y2, y3, y4, y5, y6, y7, y8 := shl512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) + m1, m2, m3, m4, m5, m6, m7, m8 := shl512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) + } + + for {} or_bool_512(m1, m2, m3, m4, m5, m6, m7, m8) {} { + if gte_512x512_64(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) { + r1, r2, r3, r4, r5, r6, r7, r8 := sub512(r1, r2, r3, r4, r5, r6, r7, r8, y1, y2, y3, y4, y5, y6, y7, y8) + } + + y1, y2, y3, y4, y5, y6, y7, y8 := shr512_internal(1, y1, y2, y3, y4, y5, y6, y7, y8) + m1, m2, m3, m4, m5, m6, m7, m8 := shr512_internal(1, m1, m2, m3, m4, m5, m6, m7, m8) + } +} + +function smod(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + // Based on https://github.com/ewasm/evm2wasm/blob/master/wasm/SMOD.wast + let m1 := 0 + let m2 := 0 + let m3 := 0 + let m4 := 1 + + let sign:i32 := i32.wrap_i64(i64.shr_u(x1, 63)) + + if i64.eqz(i64.clz(x1)) { + x1, x2, x3, x4 := xor( + x1, x2, x3, x4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + x1, x2, x3, x4 := add(x1, x2, x3, x4, 0, 0, 0, 1) + } + + if i64.eqz(i64.clz(y1)) { + y1, y2, y3, y4 := xor( + y1, y2, y3, y4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + y1, y2, y3, y4 := add(y1, y2, y3, y4, 0, 0, 0, 1) + } + + r1, r2, r3, r4 := mod(x1, x2, x3, x4, y1, y2, y3, y4) + + if sign { + r1, r2, r3, r4 := xor( + r1, r2, r3, r4, + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + r1, r2, r3, r4 := add(r1, r2, r3, r4, 0, 0, 0, 1) + } +} + +function exp(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r4 := 1 + for {} or_bool(y1, y2, y3, y4) {} { + if i32.wrap_i64(i64.and(y4, 1)) { + r1, r2, r3, r4 := mul(r1, r2, r3, r4, x1, x2, x3, x4) + } + x1, x2, x3, x4 := mul(x1, x2, x3, x4, x1, x2, x3, x4) + y1, y2, y3, y4 := shr_internal(1, y1, y2, y3, y4) + } +} + +function addmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { + let carry + z4, carry := add_carry(x4, y4, 0) + z3, carry := add_carry(x3, y3, carry) + z2, carry := add_carry(x2, y2, carry) + z1, carry := add_carry(x1, y1, carry) + + let z0 + z0, z1, z2, z3, z4 := mod320(carry, z1, z2, z3, z4, 0, m1, m2, m3, m4) +} + +function mulmod(x1, x2, x3, x4, y1, y2, y3, y4, m1, m2, m3, m4) -> z1, z2, z3, z4 { + let r1, r2, r3, r4, r5, r6, r7, r8 := mul_256x256_512(x1, x2, x3, x4, y1, y2, y3, y4) + let t1 + let t2 + let t3 + let t4 + t1, t2, t3, t4, z1, z2, z3, z4 := mod512(r1, r2, r3, r4, r5, r6, r7, r8, 0, 0, 0, 0, m1, m2, m3, m4) +} + +function signextend(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z1 := y1 + z2 := y2 + z3 := y3 + z4 := y4 + if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 32) { + let d := i64.mul(i64.sub(31, x4), 8) + z1, z2, z3, z4 := shl(0, 0, 0, d, z1, z2, z3, z4) + z1, z2, z3, z4 := sar(0, 0, 0, d, z1, z2, z3, z4) + } +} diff --git a/libyul/backends/wasm/polyfill/Bitwise.yul b/libyul/backends/wasm/polyfill/Bitwise.yul new file mode 100644 index 000000000000..3efa07bf50e5 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Bitwise.yul @@ -0,0 +1,222 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Bitwise.h`. + +function bit_negate(x) -> y { + y := i64.xor(x, 0xffffffffffffffff) +} + +function split(x) -> hi, lo { + hi := i64.shr_u(x, 32) + lo := i64.and(x, 0xffffffff) +} + +function shl_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.shl(x4, amount) +} + +function shr_internal(amount, x1, x2, x3, x4) -> r1, r2, r3, r4 { + // amount < 64 + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, amount) +} + +function shl320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) + r5 := i64.shl(x5, 1) +} + +function shr320_internal(amount, x1, x2, x3, x4, x5) -> r1, r2, r3, r4, r5 { + // amount < 64 + r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, 1) +} + +function shl512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // amount < 64 + r1 := i64.add(i64.shl(x1, amount), i64.shr_u(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shl(x2, amount), i64.shr_u(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shl(x3, amount), i64.shr_u(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shl(x4, amount), i64.shr_u(x5, i64.sub(64, amount))) + r5 := i64.add(i64.shl(x5, amount), i64.shr_u(x6, i64.sub(64, amount))) + r6 := i64.add(i64.shl(x6, amount), i64.shr_u(x7, i64.sub(64, amount))) + r7 := i64.add(i64.shl(x7, amount), i64.shr_u(x8, i64.sub(64, amount))) + r8 := i64.shl(x8, amount) +} + +function shr512_internal(amount, x1, x2, x3, x4, x5, x6, x7, x8) -> r1, r2, r3, r4, r5, r6, r7, r8 { + // amount < 64 + r8 := i64.add(i64.shr_u(x8, amount), i64.shl(x7, i64.sub(64, amount))) + r7 := i64.add(i64.shr_u(x7, amount), i64.shl(x6, i64.sub(64, amount))) + r6 := i64.add(i64.shr_u(x6, amount), i64.shl(x5, i64.sub(64, amount))) + r5 := i64.add(i64.shr_u(x5, amount), i64.shl(x4, i64.sub(64, amount))) + r4 := i64.add(i64.shr_u(x4, amount), i64.shl(x3, i64.sub(64, amount))) + r3 := i64.add(i64.shr_u(x3, amount), i64.shl(x2, i64.sub(64, amount))) + r2 := i64.add(i64.shr_u(x2, amount), i64.shl(x1, i64.sub(64, amount))) + r1 := i64.shr_u(x1, amount) +} + +function byte(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + if i64.eqz(i64.or(i64.or(x1, x2), x3)) { + let component + switch i64.div_u(x4, 8) + case 0 { component := y1 } + case 1 { component := y2 } + case 2 { component := y3 } + case 3 { component := y4 } + x4 := i64.mul(i64.rem_u(x4, 8), 8) + r4 := i64.shr_u(component, i64.sub(56, x4)) + r4 := i64.and(0xff, r4) + } +} + +function xor(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r1 := i64.xor(x1, y1) + r2 := i64.xor(x2, y2) + r3 := i64.xor(x3, y3) + r4 := i64.xor(x4, y4) +} + +function or(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r1 := i64.or(x1, y1) + r2 := i64.or(x2, y2) + r3 := i64.or(x3, y3) + r4 := i64.or(x4, y4) +} + +function and(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r1 := i64.and(x1, y1) + r2 := i64.and(x2, y2) + r3 := i64.and(x3, y3) + r4 := i64.and(x4, y4) +} + +function not(x1, x2, x3, x4) -> r1, r2, r3, r4 { + let mask := 0xffffffffffffffff + r1, r2, r3, r4 := xor(x1, x2, x3, x4, mask, mask, mask, mask) +} + +function shl_single(a, amount) -> x, y { + // amount < 64 + x := i64.shr_u(a, i64.sub(64, amount)) + y := i64.shl(a, amount) +} + +function shl(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + if i32.and(i64.eqz(x1), i64.eqz(x2)) { + if i64.eqz(x3) { + if i64.lt_u(x4, 256) { + if i64.ge_u(x4, 128) { + y1 := y3 + y2 := y4 + y3 := 0 + y4 := 0 + x4 := i64.sub(x4, 128) + } + if i64.ge_u(x4, 64) { + y1 := y2 + y2 := y3 + y3 := y4 + y4 := 0 + x4 := i64.sub(x4, 64) + } + let t, r + t, z4 := shl_single(y4, x4) + r, z3 := shl_single(y3, x4) + z3 := i64.or(z3, t) + t, z2 := shl_single(y2, x4) + z2 := i64.or(z2, r) + r, z1 := shl_single(y1, x4) + z1 := i64.or(z1, t) + } + } + } +} + +function shr_single(a, amount) -> x, y { + // amount < 64 + y := i64.shl(a, i64.sub(64, amount)) + x := i64.shr_u(a, amount) +} + +function shr(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + if i32.and(i64.eqz(x1), i64.eqz(x2)) { + if i64.eqz(x3) { + if i64.lt_u(x4, 256) { + if i64.ge_u(x4, 128) { + y4 := y2 + y3 := y1 + y2 := 0 + y1 := 0 + x4 := i64.sub(x4, 128) + } + if i64.ge_u(x4, 64) { + y4 := y3 + y3 := y2 + y2 := y1 + y1 := 0 + x4 := i64.sub(x4, 64) + } + let t + z4, t := shr_single(y4, x4) + z3, t := shr_single(y3, x4) + z4 := i64.or(z4, t) + z2, t := shr_single(y2, x4) + z3 := i64.or(z3, t) + z1, t := shr_single(y1, x4) + z2 := i64.or(z2, t) + } + } + } +} + +function sar(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + if i64.gt_u(i64.clz(y1), 0) { + z1, z2, z3, z4 := shr(x1, x2, x3, x4, y1, y2, y3, y4) + leave + } + + if gte_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { + z1 := 0xffffffffffffffff + z2 := 0xffffffffffffffff + z3 := 0xffffffffffffffff + z4 := 0xffffffffffffffff + } + if lt_256x256_64(x1, x2, x3, x4, 0, 0, 0, 256) { + y1, y2, y3, y4 := shr(0, 0, 0, x4, y1, y2, y3, y4) + z1, z2, z3, z4 := shl( + 0, 0, 0, i64.sub(256, x4), + 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + ) + z1, z2, z3, z4 := or(y1, y2, y3, y4, z1, z2, z3, z4) + } +} diff --git a/libyul/backends/wasm/polyfill/Comparison.yul b/libyul/backends/wasm/polyfill/Comparison.yul new file mode 100644 index 000000000000..dc052a14595d --- /dev/null +++ b/libyul/backends/wasm/polyfill/Comparison.yul @@ -0,0 +1,168 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Comparison.h`. + +function iszero(x1, x2, x3, x4) -> r1, r2, r3, r4 { + r4 := i64.extend_i32_u(iszero256(x1, x2, x3, x4)) +} + +function iszero256(x1, x2, x3, x4) -> r:i32 { + r := i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4))) +} + +function iszero320(x1, x2, x3, x4, x5) -> r:i32 { + r := i64.eqz(i64.or(i64.or(i64.or(x1, x2), i64.or(x3, x4)), x5)) +} + +function iszero512(x1, x2, x3, x4, x5, x6, x7, x8) -> r:i32 { + r := i32.and(iszero256(x1, x2, x3, x4), iszero256(x5, x6, x7, x8)) +} + +function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { + r4 := i64.extend_i32_u( + i32.and( + i64.eq(x1, y1), + i32.and( + i64.eq(x2, y2), + i32.and( + i64.eq(x3, y3), + i64.eq(x4, y4) + ) + ) + ) + ) +} + +// returns 0 if a == b, -1 if a < b and 1 if a > b +function cmp(a, b) -> r:i32 { + r := i32.select(0xffffffff:i32, i64.ne(a, b), i64.lt_u(a, b)) +} + +function lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { + switch cmp(x1, y1) + case 0:i32 { + switch cmp(x2, y2) + case 0:i32 { + switch cmp(x3, y3) + case 0:i32 { + switch cmp(x4, y4) + case 0:i32 { + z := i64.lt_u(x5, y5) + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } +} + +function lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { + switch cmp(x1, y1) + case 0:i32 { + switch cmp(x2, y2) + case 0:i32 { + switch cmp(x3, y3) + case 0:i32 { + switch cmp(x4, y4) + case 0:i32 { + switch cmp(x5, y5) + case 0:i32 { + switch cmp(x6, y6) + case 0:i32 { + switch cmp(x7, y7) + case 0:i32 { + z := i64.lt_u(x8, y8) + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } +} + +function lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { + switch cmp(x1, y1) + case 0:i32 { + switch cmp(x2, y2) + case 0:i32 { + switch cmp(x3, y3) + case 0:i32 { + z := i64.lt_u(x4, y4) + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } + } + case 1:i32 { z := 0:i32 } + default { z := 1:i32 } +} + +function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) +} + +function gte_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4) -> z:i32 { + z := i32.eqz(lt_256x256_64(x1, x2, x3, x4, y1, y2, y3, y4)) +} + +function gte_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5) -> z:i32 { + z := i32.eqz(lt_320x320_64(x1, x2, x3, x4, x5, y1, y2, y3, y4, y5)) +} + +function gte_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8) -> z:i32 { + z := i32.eqz(lt_512x512_64(x1, x2, x3, x4, x5, x6, x7, x8, y1, y2, y3, y4, y5, y6, y7, y8)) +} + +function gt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z1, z2, z3, z4 := lt(y1, y2, y3, y4, x1, x2, x3, x4) +} + +function slt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + // TODO correct? + x1 := i64.add(x1, 0x8000000000000000) + y1 := i64.add(y1, 0x8000000000000000) + z1, z2, z3, z4 := lt(x1, x2, x3, x4, y1, y2, y3, y4) +} + +function sgt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + z1, z2, z3, z4 := slt(y1, y2, y3, y4, x1, x2, x3, x4) +} diff --git a/libyul/backends/wasm/polyfill/Conversion.yul b/libyul/backends/wasm/polyfill/Conversion.yul new file mode 100644 index 000000000000..3168b80cae17 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Conversion.yul @@ -0,0 +1,78 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Conversion.h`. + +function u256_to_u128(x1, x2, x3, x4) -> v1, v2 { + if i64.ne(0, i64.or(x1, x2)) { invalid() } + v2 := x4 + v1 := x3 +} + +function u256_to_i64(x1, x2, x3, x4) -> v { + if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } + v := x4 +} + +function u256_to_i32(x1, x2, x3, x4) -> v:i32 { + if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } + if i64.ne(0, i64.shr_u(x4, 32)) { invalid() } + v := i32.wrap_i64(x4) +} + +function u256_to_byte(x1, x2, x3, x4) -> v { + if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { invalid() } + if i64.gt_u(x4, 255) { invalid() } + v := x4 +} + +function u256_to_i32ptr(x1, x2, x3, x4) -> v:i32 { + v := u256_to_i32(x1, x2, x3, x4) +} + +function to_internal_i32ptr(x1, x2, x3, x4) -> r:i32 { + let p:i32 := u256_to_i32ptr(x1, x2, x3, x4) + r := i32.add(p, 64:i32) + if i32.lt_u(r, p) { invalid() } +} + +function u256_to_address(x1, x2, x3, x4) -> r1, r2, r3 { + if i64.ne(0, x1) { invalid() } + if i64.ne(0, i64.shr_u(x2, 32)) { invalid() } + r1 := x2 + r2 := x3 + r3 := x4 +} + +function bswap16(x) -> y { + let hi := i64.and(i64.shl(x, 8), 0xff00) + let lo := i64.and(i64.shr_u(x, 8), 0xff) + y := i64.or(hi, lo) +} + +function bswap32(x) -> y { + let hi := i64.shl(bswap16(x), 16) + let lo := bswap16(i64.shr_u(x, 16)) + y := i64.or(hi, lo) +} + +function bswap64(x) -> y { + let hi := i64.shl(bswap32(x), 32) + let lo := bswap32(i64.shr_u(x, 32)) + y := i64.or(hi, lo) +} diff --git a/libyul/backends/wasm/polyfill/Interface.yul b/libyul/backends/wasm/polyfill/Interface.yul new file mode 100644 index 000000000000..6251409bfe1a --- /dev/null +++ b/libyul/backends/wasm/polyfill/Interface.yul @@ -0,0 +1,431 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Interface.h`. + +function address() -> z1, z2, z3, z4 { + eth.getAddress(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function balance(x1, x2, x3, x4) -> z1, z2, z3, z4 { + mstore_address(0:i32, x1, x2, x3, x4) + eth.getExternalBalance(12:i32, 48:i32) + z1, z2, z3, z4 := mload_internal(32:i32) +} + +function selfbalance() -> z1, z2, z3, z4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function chainid() -> z1, z2, z3, z4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function origin() -> z1, z2, z3, z4 { + eth.getTxOrigin(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function caller() -> z1, z2, z3, z4 { + eth.getCaller(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function callvalue() -> z1, z2, z3, z4 { + eth.getCallValue(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 { + calldatacopy(0, 0, 0, 0, x1, x2, x3, x4, 0, 0, 0, 32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function calldatasize() -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(eth.getCallDataSize()) +} + +function calldatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + let cds:i32 := eth.getCallDataSize() + let destination:i32 := u256_to_i32(x1, x2, x3, x4) + let offset:i32 := u256_to_i32(y1, y2, y3, y4) + let requested_size:i32 := u256_to_i32(z1, z2, z3, z4) + // overflow? + if i32.gt_u(offset, i32.sub(0xffffffff:i32, requested_size)) { + eth.revert(0:i32, 0:i32) + } + + let available_size:i32 := i32.sub(cds, offset) + if i32.gt_u(offset, cds) { + available_size := 0:i32 + } + + if i32.gt_u(available_size, 0:i32) { + eth.callDataCopy( + destination, + offset, + available_size + ) + } + + if i32.gt_u(requested_size, available_size) { + memset(i32.add(destination, available_size), 0:i32, i32.sub(requested_size, available_size)) + } +} + +// Needed? +function codesize() -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(eth.getCodeSize()) +} + +function codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + eth.codeCopy( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4), + u256_to_i32(z1, z2, z3, z4) + ) +} + +function datacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + // TODO correct? + codecopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) +} + +function gasprice() -> z1, z2, z3, z4 { + eth.getTxGasPrice(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function extcodesize_internal(x1, x2, x3, x4) -> r:i32 { + mstore_address(0:i32, x1, x2, x3, x4) + r := eth.getExternalCodeSize(12:i32) +} + +function extcodesize(x1, x2, x3, x4) -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(extcodesize_internal(x1, x2, x3, x4)) +} + +function extcodehash(x1, x2, x3, x4) -> z1, z2, z3, z4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function extcodecopy(a1, a2, a3, a4, p1, p2, p3, p4, o1, o2, o3, o4, l1, l2, l3, l4) { + mstore_address(0:i32, a1, a2, a3, a4) + let codeOffset:i32 := u256_to_i32(o1, o2, o3, o4) + let codeLength:i32 := u256_to_i32(l1, l2, l3, l4) + eth.externalCodeCopy(12:i32, to_internal_i32ptr(p1, p2, p3, p4), codeOffset, codeLength) +} + +function returndatasize() -> z1, z2, z3, z4 { + z4 := i64.extend_i32_u(eth.getReturnDataSize()) +} + +function returndatacopy(x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4) { + eth.returnDataCopy( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4), + u256_to_i32(z1, z2, z3, z4) + ) +} + +function blockhash(x1, x2, x3, x4) -> z1, z2, z3, z4 { + let r:i32 := eth.getBlockHash(u256_to_i64(x1, x2, x3, x4), 0:i32) + if i32.eqz(r) { + z1, z2, z3, z4 := mload_internal(0:i32) + } +} + +function coinbase() -> z1, z2, z3, z4 { + eth.getBlockCoinbase(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function timestamp() -> z1, z2, z3, z4 { + z4 := eth.getBlockTimestamp() +} + +function number() -> z1, z2, z3, z4 { + z4 := eth.getBlockNumber() +} + +function difficulty() -> z1, z2, z3, z4 { + eth.getBlockDifficulty(0:i32) + z1, z2, z3, z4 := mload_internal(0:i32) +} + +function gaslimit() -> z1, z2, z3, z4 { + z4 := eth.getBlockGasLimit() +} + +function mload(x1, x2, x3, x4) -> z1, z2, z3, z4 { + z1, z2, z3, z4 := mload_internal(to_internal_i32ptr(x1, x2, x3, x4)) +} + +function mload_internal(pos:i32) -> z1, z2, z3, z4 { + z1 := bswap64(i64.load(pos)) + z2 := bswap64(i64.load(i32.add(pos, 8:i32))) + z3 := bswap64(i64.load(i32.add(pos, 16:i32))) + z4 := bswap64(i64.load(i32.add(pos, 24:i32))) +} + +function mstore(x1, x2, x3, x4, y1, y2, y3, y4) { + mstore_internal(to_internal_i32ptr(x1, x2, x3, x4), y1, y2, y3, y4) +} + +function mstore_internal(pos:i32, y1, y2, y3, y4) { + i64.store(pos, bswap64(y1)) + i64.store(i32.add(pos, 8:i32), bswap64(y2)) + i64.store(i32.add(pos, 16:i32), bswap64(y3)) + i64.store(i32.add(pos, 24:i32), bswap64(y4)) +} + +function mstore_address(pos:i32, a1, a2, a3, a4) { + a1, a2, a3 := u256_to_address(a1, a2, a3, a4) + mstore_internal(pos, 0, a1, a2, a3) +} + +function mstore8(x1, x2, x3, x4, y1, y2, y3, y4) { + let v := u256_to_byte(y1, y2, y3, y4) + i64.store8(to_internal_i32ptr(x1, x2, x3, x4), v) +} + +// Needed? +function msize() -> z1, z2, z3, z4 { + // TODO implement + unreachable() +} + +function sload(x1, x2, x3, x4) -> z1, z2, z3, z4 { + mstore_internal(0:i32, x1, x2, x3, x4) + eth.storageLoad(0:i32, 32:i32) + z1, z2, z3, z4 := mload_internal(32:i32) +} + +function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { + mstore_internal(0:i32, x1, x2, x3, x4) + mstore_internal(32:i32, y1, y2, y3, y4) + eth.storageStore(0:i32, 32:i32) +} + +function gas() -> z1, z2, z3, z4 { + z4 := eth.getGasLeft() +} + +function log0(p1, p2, p3, p4, s1, s2, s3, s4) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 0:i32, 0:i32, 0:i32, 0:i32, 0:i32 + ) +} + +function log1( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4 +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 1:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + 0:i32, 0:i32, 0:i32 + ) +} + +function log2( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4, + t2_1, t2_2, t2_3, t2_4 +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 2:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), + 0:i32, 0:i32 + ) +} + +function log3( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4, + t2_1, t2_2, t2_3, t2_4, + t3_1, t3_2, t3_3, t3_4 +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 3:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), + to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), + 0:i32 + ) +} + +function log4( + p1, p2, p3, p4, s1, s2, s3, s4, + t1_1, t1_2, t1_3, t1_4, + t2_1, t2_2, t2_3, t2_4, + t3_1, t3_2, t3_3, t3_4, + t4_1, t4_2, t4_3, t4_4, +) { + eth.log( + to_internal_i32ptr(p1, p2, p3, p4), + u256_to_i32(s1, s2, s3, s4), + 4:i32, + to_internal_i32ptr(t1_1, t1_2, t1_3, t1_4), + to_internal_i32ptr(t2_1, t2_2, t2_3, t2_4), + to_internal_i32ptr(t3_1, t3_2, t3_3, t3_4), + to_internal_i32ptr(t4_1, t4_2, t4_3, t4_4) + ) +} + +function create( + x1, x2, x3, x4, + y1, y2, y3, y4, + z1, z2, z3, z4 +) -> a1, a2, a3, a4 { + let v1, v2 := u256_to_u128(x1, x2, x3, x4) + mstore_internal(0:i32, 0, 0, v1, v2) + + let r:i32 := eth.create(0:i32, to_internal_i32ptr(y1, y2, y3, y4), u256_to_i32(z1, z2, z3, z4), 32:i32) + if i32.eqz(r) { + a1, a2, a3, a4 := mload_internal(32:i32) + } +} + +function call( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4, + g1, g2, g3, g4 +) -> x1, x2, x3, x4 { + let g := u256_to_i64(a1, a2, a3, a4) + mstore_address(0:i32, b1, b2, b3, b4) + + let v1, v2 := u256_to_u128(c1, c2, c3, c4) + mstore_internal(32:i32, 0, 0, v1, v2) + + x4 := i64.extend_i32_u(eth.call(g, 12:i32, 32:i32, to_internal_i32ptr(d1, d2, d3, d4), u256_to_i32(e1, e2, e3, e4))) +} + +function callcode( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4, + g1, g2, g3, g4 +) -> x1, x2, x3, x4 { + mstore_address(0:i32, b1, b2, b3, b4) + + let v1, v2 := u256_to_u128(c1, c2, c3, c4) + mstore_internal(32:i32, 0, 0, v1, v2) + + x4 := i64.extend_i32_u(eth.callCode( + u256_to_i64(a1, a2, a3, a4), + 12:i32, + 32:i32, + to_internal_i32ptr(d1, d2, d3, d4), + u256_to_i32(e1, e2, e3, e4) + )) +} + +function delegatecall( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4 +) -> x1, x2, x3, x4 { + mstore_address(0:i32, b1, b2, b3, b4) + + x4 := i64.extend_i32_u(eth.callDelegate( + u256_to_i64(a1, a2, a3, a4), + 12:i32, + to_internal_i32ptr(c1, c2, c3, c4), + u256_to_i32(d1, d2, d3, d4) + )) +} + +function staticcall( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4, + e1, e2, e3, e4, + f1, f2, f3, f4 +) -> x1, x2, x3, x4 { + mstore_address(0:i32, b1, b2, b3, b4) + + x4 := i64.extend_i32_u(eth.callStatic( + u256_to_i64(a1, a2, a3, a4), + 12:i32, + to_internal_i32ptr(c1, c2, c3, c4), + u256_to_i32(d1, d2, d3, d4) + )) +} + +function create2( + a1, a2, a3, a4, + b1, b2, b3, b4, + c1, c2, c3, c4, + d1, d2, d3, d4 +) -> x1, x2, x3, x4 { + // TODO: not part of current Ewasm spec + unreachable() +} + +function selfdestruct(a1, a2, a3, a4) { + mstore_address(0:i32, a1, a2, a3, a4) + // In EVM, addresses are padded to 32 bytes, so discard the first 12. + eth.selfDestruct(12:i32) +} + +function return(x1, x2, x3, x4, y1, y2, y3, y4) { + eth.finish( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4) + ) +} + +function revert(x1, x2, x3, x4, y1, y2, y3, y4) { + eth.revert( + to_internal_i32ptr(x1, x2, x3, x4), + u256_to_i32(y1, y2, y3, y4) + ) +} + +function invalid() { + unreachable() +} + +function stop() { + eth.finish(0:i32, 0:i32) +} diff --git a/libyul/backends/wasm/polyfill/Keccak.yul b/libyul/backends/wasm/polyfill/Keccak.yul new file mode 100644 index 000000000000..2dae23394101 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Keccak.yul @@ -0,0 +1,24 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Keccak.h`. + +function keccak256(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 { + // TODO implement + unreachable() +} diff --git a/libyul/backends/wasm/polyfill/Logical.yul b/libyul/backends/wasm/polyfill/Logical.yul new file mode 100644 index 000000000000..8d297d3545e6 --- /dev/null +++ b/libyul/backends/wasm/polyfill/Logical.yul @@ -0,0 +1,31 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Logical.h`. + +function or_bool(a, b, c, d) -> r:i32 { + r := i32.eqz(i64.eqz(i64.or(i64.or(a, b), i64.or(c, d)))) +} + +function or_bool_320(a, b, c, d, e) -> r:i32 { + r := i32.or(or_bool(a, b, c, 0), or_bool(d, e, 0, 0)) +} + +function or_bool_512(a, b, c, d, e, f, g, h) -> r:i32 { + r := i32.or(or_bool(a, b, c, d), or_bool(e, f, g, h)) +} diff --git a/libyul/backends/wasm/polyfill/Memory.yul b/libyul/backends/wasm/polyfill/Memory.yul new file mode 100644 index 000000000000..aec8e86c680a --- /dev/null +++ b/libyul/backends/wasm/polyfill/Memory.yul @@ -0,0 +1,69 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +// NOTE: This file is used to generate `ewasmPolyfills/Memory.h`. + +function save_temp_mem_32() -> t1, t2, t3, t4 { + t1 := i64.load(0:i32) + t2 := i64.load(8:i32) + t3 := i64.load(16:i32) + t4 := i64.load(24:i32) +} + +function restore_temp_mem_32(t1, t2, t3, t4) { + i64.store(0:i32, t1) + i64.store(8:i32, t2) + i64.store(16:i32, t3) + i64.store(24:i32, t4) +} + +function save_temp_mem_64() -> t1, t2, t3, t4, t5, t6, t7, t8 { + t1 := i64.load(0:i32) + t2 := i64.load(8:i32) + t3 := i64.load(16:i32) + t4 := i64.load(24:i32) + t5 := i64.load(32:i32) + t6 := i64.load(40:i32) + t7 := i64.load(48:i32) + t8 := i64.load(54:i32) +} + +function restore_temp_mem_64(t1, t2, t3, t4, t5, t6, t7, t8) { + i64.store(0:i32, t1) + i64.store(8:i32, t2) + i64.store(16:i32, t3) + i64.store(24:i32, t4) + i64.store(32:i32, t5) + i64.store(40:i32, t6) + i64.store(48:i32, t7) + i64.store(54:i32, t8) +} + +function pop(x1, x2, x3, x4) { +} + +function memoryguard(x:i64) -> y1, y2, y3, y4 { + y4 := x +} + +function memset(ptr:i32, value:i32, length:i32) { + for { let i:i32 := 0:i32 } i32.lt_u(i, length) { i := i32.add(i, 1:i32) } + { + i32.store8(i32.add(ptr, i), value) + } +} \ No newline at end of file diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index 8dc08f6fa1d6..3588113583da 100644 --- a/libyul/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index 8afb50d97152..4cf69b0b7034 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include diff --git a/libyul/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp index 057f94b88782..d0ce29054844 100644 --- a/libyul/optimiser/ASTWalker.cpp +++ b/libyul/optimiser/ASTWalker.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index c62be4844ba5..107658dfc1f8 100644 --- a/libyul/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/BlockFlattener.cpp b/libyul/optimiser/BlockFlattener.cpp index 24f57594c5d2..e32972e66668 100644 --- a/libyul/optimiser/BlockFlattener.cpp +++ b/libyul/optimiser/BlockFlattener.cpp @@ -15,10 +15,13 @@ along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 + #include -#include +#include + #include #include + #include using namespace std; diff --git a/libyul/optimiser/BlockHasher.cpp b/libyul/optimiser/BlockHasher.cpp index 4fb199982e9b..02db3bc4369e 100644 --- a/libyul/optimiser/BlockHasher.cpp +++ b/libyul/optimiser/BlockHasher.cpp @@ -21,7 +21,9 @@ #include #include +#include #include + #include using namespace std; diff --git a/libyul/optimiser/BlockHasher.h b/libyul/optimiser/BlockHasher.h index 36826e1583be..a831efa23ccc 100644 --- a/libyul/optimiser/BlockHasher.h +++ b/libyul/optimiser/BlockHasher.h @@ -21,9 +21,8 @@ #pragma once #include -#include +#include #include -#include namespace solidity::yul { diff --git a/libyul/optimiser/CallGraphGenerator.cpp b/libyul/optimiser/CallGraphGenerator.cpp index 85fb1c66c9c9..5aeff50be5e7 100644 --- a/libyul/optimiser/CallGraphGenerator.cpp +++ b/libyul/optimiser/CallGraphGenerator.cpp @@ -19,11 +19,9 @@ * Specific AST walker that generates the call graph. */ -#include +#include #include -#include - #include using namespace std; diff --git a/libyul/optimiser/CircularReferencesPruner.cpp b/libyul/optimiser/CircularReferencesPruner.cpp index 2fe6a1d80734..1078779c5bba 100644 --- a/libyul/optimiser/CircularReferencesPruner.cpp +++ b/libyul/optimiser/CircularReferencesPruner.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 3b0b6bec179e..e1c7ccced12a 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include using namespace std; @@ -67,7 +67,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e) // We should not modify function arguments that have to be literals // Note that replacing the function call entirely is fine, // if the function call is movable. - if (!builtin->literalArguments || !builtin->literalArguments.value()[i - 1]) + if (!builtin->literalArgument(i - 1)) visit(funCall.arguments[i - 1]); descend = false; @@ -103,9 +103,9 @@ void CommonSubexpressionEliminator::visit(Expression& _e) for (auto const& [variable, value]: m_value) { assertThrow(value.value, OptimizerException, ""); - assertThrow(inScope(variable), OptimizerException, ""); if (SyntacticallyEqual{}(_e, *value.value)) { + assertThrow(inScope(variable), OptimizerException, ""); _e = Identifier{locationOf(_e), variable}; break; } diff --git a/libyul/optimiser/ConditionalSimplifier.cpp b/libyul/optimiser/ConditionalSimplifier.cpp index f95ef830a726..0a66e28dfb85 100644 --- a/libyul/optimiser/ConditionalSimplifier.cpp +++ b/libyul/optimiser/ConditionalSimplifier.cpp @@ -17,7 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include #include -#include +#include #include #include #include diff --git a/libyul/optimiser/ConditionalSimplifier.h b/libyul/optimiser/ConditionalSimplifier.h index 9a48b813f90f..e41ee579cc10 100644 --- a/libyul/optimiser/ConditionalSimplifier.h +++ b/libyul/optimiser/ConditionalSimplifier.h @@ -19,6 +19,7 @@ #include #include +#include #include #include diff --git a/libyul/optimiser/ConditionalUnsimplifier.cpp b/libyul/optimiser/ConditionalUnsimplifier.cpp index 28bc11a0c0e2..e51cbbf3c0f9 100644 --- a/libyul/optimiser/ConditionalUnsimplifier.cpp +++ b/libyul/optimiser/ConditionalUnsimplifier.cpp @@ -17,7 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include #include -#include +#include #include #include #include diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 044061d9d077..c617e2005adf 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 6755500b3a09..b845d2722238 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -24,9 +24,9 @@ #include #include +#include +#include #include -#include -#include #include @@ -39,10 +39,27 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::yul; +DataFlowAnalyzer::DataFlowAnalyzer( + Dialect const& _dialect, + map _functionSideEffects +): +m_dialect(_dialect), +m_functionSideEffects(std::move(_functionSideEffects)), +m_knowledgeBase(_dialect, m_value) +{ + if (auto const* builtin = _dialect.memoryStoreFunction(YulString{})) + m_storeFunctionName[static_cast(StoreLoadLocation::Memory)] = builtin->name; + if (auto const* builtin = _dialect.memoryLoadFunction(YulString{})) + m_loadFunctionName[static_cast(StoreLoadLocation::Memory)] = builtin->name; + if (auto const* builtin = _dialect.storageStoreFunction(YulString{})) + m_storeFunctionName[static_cast(StoreLoadLocation::Storage)] = builtin->name; + if (auto const* builtin = _dialect.storageLoadFunction(YulString{})) + m_loadFunctionName[static_cast(StoreLoadLocation::Storage)] = builtin->name; +} void DataFlowAnalyzer::operator()(ExpressionStatement& _statement) { - if (auto vars = isSimpleStore(evmasm::Instruction::SSTORE, _statement)) + if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement)) { ASTModifier::operator()(_statement); set keysToErase; @@ -56,7 +73,7 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement) m_storage.eraseKey(key); m_storage.set(vars->first, vars->second); } - else if (auto vars = isSimpleStore(evmasm::Instruction::MSTORE, _statement)) + else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement)) { ASTModifier::operator()(_statement); set keysToErase; @@ -82,7 +99,7 @@ void DataFlowAnalyzer::operator()(Assignment& _assignment) assertThrow(_assignment.value, OptimizerException, ""); clearKnowledgeIfInvalidated(*_assignment.value); visit(*_assignment.value); - handleAssignment(names, _assignment.value.get()); + handleAssignment(names, _assignment.value.get(), false); } void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) @@ -98,7 +115,7 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) visit(*_varDecl.value); } - handleAssignment(names, _varDecl.value.get()); + handleAssignment(names, _varDecl.value.get(), true); } void DataFlowAnalyzer::operator()(If& _if) @@ -161,7 +178,7 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun) for (auto const& var: _fun.returnVariables) { m_variableScopes.back().variables.emplace(var.name); - handleAssignment({var.name}, nullptr); + handleAssignment({var.name}, nullptr, true); } ASTModifier::operator()(_fun); @@ -220,9 +237,10 @@ void DataFlowAnalyzer::operator()(Block& _block) assertThrow(numScopes == m_variableScopes.size(), OptimizerException, ""); } -void DataFlowAnalyzer::handleAssignment(set const& _variables, Expression* _value) +void DataFlowAnalyzer::handleAssignment(set const& _variables, Expression* _value, bool _isDeclaration) { - clearValues(_variables); + if (!_isDeclaration) + clearValues(_variables); MovableChecker movableChecker{m_dialect, &m_functionSideEffects}; if (_value) @@ -244,14 +262,17 @@ void DataFlowAnalyzer::handleAssignment(set const& _variables, Expres for (auto const& name: _variables) { m_references.set(name, referencedVariables); - // assignment to slot denoted by "name" - m_storage.eraseKey(name); - // assignment to slot contents denoted by "name" - m_storage.eraseValue(name); - // assignment to slot denoted by "name" - m_memory.eraseKey(name); - // assignment to slot contents denoted by "name" - m_memory.eraseValue(name); + if (!_isDeclaration) + { + // assignment to slot denoted by "name" + m_storage.eraseKey(name); + // assignment to slot contents denoted by "name" + m_storage.eraseValue(name); + // assignment to slot denoted by "name" + m_memory.eraseKey(name); + // assignment to slot contents denoted by "name" + m_memory.eraseValue(name); + } } if (_value && _variables.size() == 1) @@ -262,9 +283,9 @@ void DataFlowAnalyzer::handleAssignment(set const& _variables, Expres // This might erase additional knowledge about the slot. // On the other hand, if we knew the value in the slot // already, then the sload() / mload() would have been replaced by a variable anyway. - if (auto key = isSimpleLoad(evmasm::Instruction::MLOAD, *_value)) + if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value)) m_memory.set(*key, variable); - else if (auto key = isSimpleLoad(evmasm::Instruction::SLOAD, *_value)) + else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value)) m_storage.set(*key, variable); } } @@ -388,53 +409,27 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const } std::optional> DataFlowAnalyzer::isSimpleStore( - evmasm::Instruction _store, + StoreLoadLocation _location, ExpressionStatement const& _statement ) const { - yulAssert( - _store == evmasm::Instruction::MSTORE || - _store == evmasm::Instruction::SSTORE, - "" - ); - if (holds_alternative(_statement.expression)) - { - FunctionCall const& funCall = std::get(_statement.expression); - if (EVMDialect const* dialect = dynamic_cast(&m_dialect)) - if (auto const* builtin = dialect->builtin(funCall.functionName.name)) - if (builtin->instruction == _store) - if ( - holds_alternative(funCall.arguments.at(0)) && - holds_alternative(funCall.arguments.at(1)) - ) - { - YulString key = std::get(funCall.arguments.at(0)).name; - YulString value = std::get(funCall.arguments.at(1)).name; - return make_pair(key, value); - } - } + if (FunctionCall const* funCall = get_if(&_statement.expression)) + if (funCall->functionName.name == m_storeFunctionName[static_cast(_location)]) + if (Identifier const* key = std::get_if(&funCall->arguments.front())) + if (Identifier const* value = std::get_if(&funCall->arguments.back())) + return make_pair(key->name, value->name); return {}; } std::optional DataFlowAnalyzer::isSimpleLoad( - evmasm::Instruction _load, + StoreLoadLocation _location, Expression const& _expression ) const { - yulAssert( - _load == evmasm::Instruction::MLOAD || - _load == evmasm::Instruction::SLOAD, - "" - ); - if (holds_alternative(_expression)) - { - FunctionCall const& funCall = std::get(_expression); - if (EVMDialect const* dialect = dynamic_cast(&m_dialect)) - if (auto const* builtin = dialect->builtin(funCall.functionName.name)) - if (builtin->instruction == _load) - if (holds_alternative(funCall.arguments.at(0))) - return std::get(funCall.arguments.at(0)).name; - } + if (FunctionCall const* funCall = get_if(&_expression)) + if (funCall->functionName.name == m_loadFunctionName[static_cast(_location)]) + if (Identifier const* key = std::get_if(&funCall->arguments.front())) + return key->name; return {}; } diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index 237b4859701d..7a9b36a24d2f 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -26,12 +26,9 @@ #include #include #include -#include +#include // Needed for m_zero below. #include -// TODO avoid -#include - #include #include @@ -89,11 +86,7 @@ class DataFlowAnalyzer: public ASTModifier explicit DataFlowAnalyzer( Dialect const& _dialect, std::map _functionSideEffects = {} - ): - m_dialect(_dialect), - m_functionSideEffects(std::move(_functionSideEffects)), - m_knowledgeBase(_dialect, m_value) - {} + ); using ASTModifier::operator(); void operator()(ExpressionStatement& _statement) override; @@ -107,7 +100,7 @@ class DataFlowAnalyzer: public ASTModifier protected: /// Registers the assignment. - void handleAssignment(std::set const& _names, Expression* _value); + void handleAssignment(std::set const& _names, Expression* _value, bool _isDeclaration); /// Creates a new inner scope. void pushScope(bool _functionScope); @@ -143,17 +136,23 @@ class DataFlowAnalyzer: public ASTModifier /// Returns true iff the variable is in scope. bool inScope(YulString _variableName) const; + enum class StoreLoadLocation { + Memory = 0, + Storage = 1, + Last = Storage + }; + /// Checks if the statement is sstore(a, b) / mstore(a, b) /// where a and b are variables and returns these variables in that case. std::optional> isSimpleStore( - evmasm::Instruction _store, + StoreLoadLocation _location, ExpressionStatement const& _statement ) const; /// Checks if the expression is sload(a) / mload(a) /// where a is a variable and returns the variable in that case. std::optional isSimpleLoad( - evmasm::Instruction _load, + StoreLoadLocation _location, Expression const& _expression ) const; @@ -173,6 +172,9 @@ class DataFlowAnalyzer: public ASTModifier KnowledgeBase m_knowledgeBase; + YulString m_storeFunctionName[static_cast(StoreLoadLocation::Last) + 1]; + YulString m_loadFunctionName[static_cast(StoreLoadLocation::Last) + 1]; + /// Current nesting depth of loops. size_t m_loopDepth{0}; diff --git a/libyul/optimiser/DeadCodeEliminator.cpp b/libyul/optimiser/DeadCodeEliminator.cpp index 1bb4f460d278..1c6818e9da2e 100644 --- a/libyul/optimiser/DeadCodeEliminator.cpp +++ b/libyul/optimiser/DeadCodeEliminator.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/libyul/optimiser/Disambiguator.cpp b/libyul/optimiser/Disambiguator.cpp index 2e20d4be29ad..8bcc07d2bf1a 100644 --- a/libyul/optimiser/Disambiguator.cpp +++ b/libyul/optimiser/Disambiguator.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index 6d8a3775cff4..062173882761 100644 --- a/libyul/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include #include diff --git a/libyul/optimiser/EquivalentFunctionCombiner.cpp b/libyul/optimiser/EquivalentFunctionCombiner.cpp index 8cea5d63edda..90091f6ae6bb 100644 --- a/libyul/optimiser/EquivalentFunctionCombiner.cpp +++ b/libyul/optimiser/EquivalentFunctionCombiner.cpp @@ -20,7 +20,7 @@ */ #include -#include +#include #include using namespace std; diff --git a/libyul/optimiser/EquivalentFunctionCombiner.h b/libyul/optimiser/EquivalentFunctionCombiner.h index d43fa3986ed3..5337f76842d6 100644 --- a/libyul/optimiser/EquivalentFunctionCombiner.h +++ b/libyul/optimiser/EquivalentFunctionCombiner.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace solidity::yul { diff --git a/libyul/optimiser/EquivalentFunctionDetector.cpp b/libyul/optimiser/EquivalentFunctionDetector.cpp index 5c49ddb89fdf..b25600c21c9b 100644 --- a/libyul/optimiser/EquivalentFunctionDetector.cpp +++ b/libyul/optimiser/EquivalentFunctionDetector.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include using namespace std; diff --git a/libyul/optimiser/EquivalentFunctionDetector.h b/libyul/optimiser/EquivalentFunctionDetector.h index 107b355e6048..d3ddc25f7bdb 100644 --- a/libyul/optimiser/EquivalentFunctionDetector.h +++ b/libyul/optimiser/EquivalentFunctionDetector.h @@ -22,7 +22,7 @@ #include #include -#include +#include namespace solidity::yul { diff --git a/libyul/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp index ca29a58290e4..ce0d1c739101 100644 --- a/libyul/optimiser/ExpressionInliner.cpp +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index f3a8833dfc34..ff7da67b8832 100644 --- a/libyul/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -21,7 +21,7 @@ #pragma once #include -#include +#include #include #include diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp index fa9aac9ceb50..7e97fedb257e 100644 --- a/libyul/optimiser/ExpressionJoiner.cpp +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include diff --git a/libyul/optimiser/ExpressionJoiner.h b/libyul/optimiser/ExpressionJoiner.h index db3217de0aee..eb22ad79fbdb 100644 --- a/libyul/optimiser/ExpressionJoiner.h +++ b/libyul/optimiser/ExpressionJoiner.h @@ -21,7 +21,7 @@ */ #pragma once -#include +#include #include #include @@ -32,7 +32,6 @@ namespace solidity::yul class NameCollector; struct OptimiserStepContext; - /** * Optimiser component that modifies an AST in place, turning sequences * of variable declarations into complex expressions, if the variables diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index ec3a6eb78ce1..21107c0268a8 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -22,11 +22,8 @@ #include #include -#include #include -#include - -#include +#include using namespace std; using namespace solidity; @@ -40,17 +37,7 @@ void ExpressionSimplifier::run(OptimiserStepContext& _context, Block& _ast) void ExpressionSimplifier::visit(Expression& _expression) { ASTModifier::visit(_expression); - while (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value)) - { - // Do not apply the rule if it removes non-constant parts of the expression. - // TODO: The check could actually be less strict than "movable". - // We only require "Does not cause side-effects". - // Note: References to variables that are only assigned once are always movable, - // so if the value of the variable is not movable, the expression that references - // the variable still is. - - if (match->removesNonConstants && !SideEffectsCollector(m_dialect, _expression).movable()) - return; + + while (auto const* match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value)) _expression = match->action().toExpression(locationOf(_expression)); - } } diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index b8703e2a27f3..f9d4e8da5441 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include @@ -33,7 +33,8 @@ struct OptimiserStepContext; /** * Applies simplification rules to all expressions. * The component will work best if the code is in SSA form, but - * this is not required for correctness. + * this is not required for correctness. Using CommonSubexpressionEliminator + * also helps this component track equivalent sub-expressions. * * It tracks the current values of variables using the DataFlowAnalyzer * and takes them into account for replacements. diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 2c15b6eb528b..7aad0bd5890e 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -46,14 +46,10 @@ void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast) void ExpressionSplitter::operator()(FunctionCall& _funCall) { - vector const* literalArgs = nullptr; - - if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name)) - if (builtin->literalArguments) - literalArgs = &builtin->literalArguments.value(); + BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name); for (size_t i = _funCall.arguments.size(); i > 0; i--) - if (!literalArgs || !(*literalArgs)[i - 1]) + if (!builtin || !builtin->literalArgument(i - 1)) outlineExpression(_funCall.arguments[i - 1]); } diff --git a/libyul/optimiser/ExpressionSplitter.h b/libyul/optimiser/ExpressionSplitter.h index ebcdd7f6d394..bbde8d6a2df0 100644 --- a/libyul/optimiser/ExpressionSplitter.h +++ b/libyul/optimiser/ExpressionSplitter.h @@ -21,7 +21,7 @@ */ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp index a5b907ce71b8..0594797572f2 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.cpp +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -18,7 +18,8 @@ #include #include -#include +#include + #include using namespace std; diff --git a/libyul/optimiser/ForLoopConditionOutOfBody.cpp b/libyul/optimiser/ForLoopConditionOutOfBody.cpp index f13b417e8b84..1ddc3c25739c 100644 --- a/libyul/optimiser/ForLoopConditionOutOfBody.cpp +++ b/libyul/optimiser/ForLoopConditionOutOfBody.cpp @@ -18,8 +18,9 @@ #include #include -#include +#include #include + #include using namespace std; diff --git a/libyul/optimiser/ForLoopInitRewriter.cpp b/libyul/optimiser/ForLoopInitRewriter.cpp index 62d5201998de..345dd126642e 100644 --- a/libyul/optimiser/ForLoopInitRewriter.cpp +++ b/libyul/optimiser/ForLoopInitRewriter.cpp @@ -16,8 +16,10 @@ */ // SPDX-License-Identifier: GPL-3.0 #include -#include +#include + #include + #include using namespace std; diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 2e9dd1e05044..189a0616401a 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -28,8 +28,9 @@ #include #include #include +#include #include -#include +#include #include #include @@ -41,7 +42,9 @@ using namespace solidity::yul; void FullInliner::run(OptimiserStepContext& _context, Block& _ast) { - FullInliner{_ast, _context.dispenser, _context.dialect}.run(); + FullInliner inliner{_ast, _context.dispenser, _context.dialect}; + inliner.run(Pass::InlineTiny); + inliner.run(Pass::InlineRest); } FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect): @@ -72,19 +75,86 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& } } -void FullInliner::run() +void FullInliner::run(Pass _pass) { + m_pass = _pass; + + // Note that the order of inlining can result in very different code. + // Since AST IDs and thus function names depend on whether or not a contract + // is compiled together with other source files, a change in AST IDs + // should have as little an impact as possible. This is the case + // if we handle inlining in source (and thus, for the IR generator, + // function name) order. + // We use stable_sort below to keep the inlining order of two functions + // with the same depth. + map depths = callDepths(); + vector functions; + for (auto& statement: m_ast.statements) + if (holds_alternative(statement)) + functions.emplace_back(&std::get(statement)); + std::stable_sort(functions.begin(), functions.end(), [depths]( + FunctionDefinition const* _a, + FunctionDefinition const* _b + ) { + return depths.at(_a->name) < depths.at(_b->name); + }); + for (FunctionDefinition* fun: functions) + { + handleBlock(fun->name, fun->body); + updateCodeSize(*fun); + } + for (auto& statement: m_ast.statements) if (holds_alternative(statement)) handleBlock({}, std::get(statement)); +} - // TODO it might be good to determine a visiting order: - // first handle functions that are called from many places. - for (auto const& fun: m_functions) +map FullInliner::callDepths() const +{ + CallGraph cg = CallGraphGenerator::callGraph(m_ast); + cg.functionCalls.erase(""_yulstring); + + // Remove calls to builtin functions. + for (auto& call: cg.functionCalls) + for (auto it = call.second.begin(); it != call.second.end();) + if (m_dialect.builtin(*it)) + it = call.second.erase(it); + else + ++it; + + map depths; + size_t currentDepth = 0; + + while (true) { - handleBlock(fun.second->name, fun.second->body); - updateCodeSize(*fun.second); + vector removed; + for (auto it = cg.functionCalls.begin(); it != cg.functionCalls.end();) + { + auto const& [fun, callees] = *it; + if (callees.empty()) + { + removed.emplace_back(fun); + depths[fun] = currentDepth; + it = cg.functionCalls.erase(it); + } + else + ++it; + } + + for (auto& call: cg.functionCalls) + call.second -= removed; + + currentDepth++; + + if (removed.empty()) + break; } + + // Only recursive functions left here. + for (auto const& fun: cg.functionCalls) + depths[fun.first] = currentDepth; + + return depths; } bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) @@ -105,6 +175,10 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) if (size <= 1) return true; + // In the first pass, only inline tiny functions. + if (m_pass == Pass::InlineTiny) + return false; + // Do not inline into already big functions. if (m_functionSizes.at(_callSite) > 45) return false; diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index 8092e478e57b..a743bf3238ec 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -20,7 +20,7 @@ */ #pragma once -#include +#include #include #include @@ -91,13 +91,20 @@ class FullInliner: public ASTModifier void tentativelyUpdateCodeSize(YulString _function, YulString _callSite); private: + enum Pass { InlineTiny, InlineRest }; + FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect); - void run(); + void run(Pass _pass); + + /// @returns a map containing the maximum depths of a call chain starting at each + /// function. For recursive functions, the value is one larger than for all others. + std::map callDepths() const; void updateCodeSize(FunctionDefinition const& _fun); void handleBlock(YulString _currentFunctionName, Block& _block); bool recursive(FunctionDefinition const& _fun) const; + Pass m_pass; /// The AST to be modified. The root block itself will not be modified, because /// we store pointers to functions. Block& m_ast; diff --git a/libyul/optimiser/FunctionCallFinder.cpp b/libyul/optimiser/FunctionCallFinder.cpp new file mode 100644 index 000000000000..b8f206440411 --- /dev/null +++ b/libyul/optimiser/FunctionCallFinder.cpp @@ -0,0 +1,39 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; + +vector FunctionCallFinder::run(Block& _block, YulString _functionName) +{ + FunctionCallFinder functionCallFinder(_functionName); + functionCallFinder(_block); + return functionCallFinder.m_calls; +} + +FunctionCallFinder::FunctionCallFinder(YulString _functionName): m_functionName(_functionName) {} + +void FunctionCallFinder::operator()(FunctionCall& _functionCall) +{ + ASTModifier::operator()(_functionCall); + if (_functionCall.functionName.name == m_functionName) + m_calls.emplace_back(&_functionCall); +} diff --git a/libyul/optimiser/FunctionCallFinder.h b/libyul/optimiser/FunctionCallFinder.h new file mode 100644 index 000000000000..365f86688b74 --- /dev/null +++ b/libyul/optimiser/FunctionCallFinder.h @@ -0,0 +1,47 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * AST walker that finds all calls to a function of a given name. + */ + +#pragma once + +#include + +#include + +namespace solidity::yul +{ + +/** + * AST walker that finds all calls to a function of a given name. + * + * Prerequisite: Disambiguator + */ +class FunctionCallFinder: ASTModifier +{ +public: + static std::vector run(Block& _block, YulString _functionName); +private: + FunctionCallFinder(YulString _functionName); + using ASTModifier::operator(); + void operator()(FunctionCall& _functionCall) override; + YulString m_functionName; + std::vector m_calls; +}; + +} diff --git a/libyul/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp index 86d59dd53455..6ebab56db0e7 100644 --- a/libyul/optimiser/FunctionGrouper.cpp +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -22,7 +22,7 @@ #include -#include +#include #include diff --git a/libyul/optimiser/FunctionGrouper.h b/libyul/optimiser/FunctionGrouper.h index 0bfbb2f069f5..fa5409936936 100644 --- a/libyul/optimiser/FunctionGrouper.h +++ b/libyul/optimiser/FunctionGrouper.h @@ -16,13 +16,13 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Optimiser component that changes the code of a black so that all non-function definition + * Optimiser component that changes the code of a block so that all non-function definition * instructions are moved to a block of their own followed by all function definitions. */ #pragma once -#include +#include namespace solidity::yul { diff --git a/libyul/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp index a02feeda8e4b..aa5c373e9393 100644 --- a/libyul/optimiser/FunctionHoister.cpp +++ b/libyul/optimiser/FunctionHoister.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include diff --git a/libyul/optimiser/FunctionHoister.h b/libyul/optimiser/FunctionHoister.h index 39e692c8a516..04d7f64e3d73 100644 --- a/libyul/optimiser/FunctionHoister.h +++ b/libyul/optimiser/FunctionHoister.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include namespace solidity::yul diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp index c7830d48098f..8c9b80265af8 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.h b/libyul/optimiser/InlinableExpressionFunctionFinder.h index 3c0b768e67cf..587bdca91172 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.h +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 0692d0c0665a..623db6a70fdf 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include #include #include diff --git a/libyul/optimiser/KnowledgeBase.h b/libyul/optimiser/KnowledgeBase.h index 7d4cdfb6b860..a240a108abba 100644 --- a/libyul/optimiser/KnowledgeBase.h +++ b/libyul/optimiser/KnowledgeBase.h @@ -21,8 +21,9 @@ #pragma once -#include +#include #include + #include namespace solidity::yul diff --git a/libyul/optimiser/LoadResolver.cpp b/libyul/optimiser/LoadResolver.cpp index 7635f9d61a11..21992e5472c0 100644 --- a/libyul/optimiser/LoadResolver.cpp +++ b/libyul/optimiser/LoadResolver.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include using namespace std; using namespace solidity; @@ -46,21 +46,18 @@ void LoadResolver::visit(Expression& _e) { DataFlowAnalyzer::visit(_e); - if (!dynamic_cast(&m_dialect)) - return; - - if (holds_alternative(_e)) - { - FunctionCall const& funCall = std::get(_e); - if (auto const* builtin = dynamic_cast(m_dialect).builtin(funCall.functionName.name)) - if (builtin->instruction) - tryResolve(_e, *builtin->instruction, funCall.arguments); - } + if (FunctionCall const* funCall = std::get_if(&_e)) + for (auto location: { StoreLoadLocation::Memory, StoreLoadLocation::Storage }) + if (funCall->functionName.name == m_loadFunctionName[static_cast(location)]) + { + tryResolve(_e, location, funCall->arguments); + break; + } } void LoadResolver::tryResolve( Expression& _e, - evmasm::Instruction _instruction, + StoreLoadLocation _location, vector const& _arguments ) { @@ -69,13 +66,13 @@ void LoadResolver::tryResolve( YulString key = std::get(_arguments.at(0)).name; if ( - _instruction == evmasm::Instruction::SLOAD && + _location == StoreLoadLocation::Storage && m_storage.values.count(key) ) _e = Identifier{locationOf(_e), m_storage.values[key]}; else if ( m_optimizeMLoad && - _instruction == evmasm::Instruction::MLOAD && + _location == StoreLoadLocation::Memory && m_memory.values.count(key) ) _e = Identifier{locationOf(_e), m_memory.values[key]}; diff --git a/libyul/optimiser/LoadResolver.h b/libyul/optimiser/LoadResolver.h index 00579e95fb88..a376ab059615 100644 --- a/libyul/optimiser/LoadResolver.h +++ b/libyul/optimiser/LoadResolver.h @@ -24,14 +24,10 @@ #include #include -#include namespace solidity::yul { -struct EVMDialect; -struct BuiltinFunctionForEVM; - /** * Optimisation stage that replaces expressions of type ``sload(x)`` and ``mload(x)`` by the value * currently stored in storage resp. memory, if known. @@ -63,7 +59,7 @@ class LoadResolver: public DataFlowAnalyzer void tryResolve( Expression& _e, - evmasm::Instruction _instruction, + StoreLoadLocation _location, std::vector const& _arguments ); diff --git a/libyul/optimiser/LoopInvariantCodeMotion.cpp b/libyul/optimiser/LoopInvariantCodeMotion.cpp index 9a81e5fc8d5e..fe8ddff97ed6 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.cpp +++ b/libyul/optimiser/LoopInvariantCodeMotion.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -35,9 +35,9 @@ void LoopInvariantCodeMotion::run(OptimiserStepContext& _context, Block& _ast) { map functionSideEffects = SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)); - + bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast); set ssaVars = SSAValueTracker::ssaVariables(_ast); - LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects}(_ast); + LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects, containsMSize}(_ast); } void LoopInvariantCodeMotion::operator()(Block& _block) @@ -57,7 +57,8 @@ void LoopInvariantCodeMotion::operator()(Block& _block) bool LoopInvariantCodeMotion::canBePromoted( VariableDeclaration const& _varDecl, - set const& _varsDefinedInCurrentScope + set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects ) const { // A declaration can be promoted iff @@ -73,7 +74,8 @@ bool LoopInvariantCodeMotion::canBePromoted( for (auto const& ref: ReferencesCounter::countReferences(*_varDecl.value, ReferencesCounter::OnlyVariables)) if (_varsDefinedInCurrentScope.count(ref.first) || !m_ssaVariables.count(ref.first)) return false; - if (!SideEffectsCollector{m_dialect, *_varDecl.value, &m_functionSideEffects}.movable()) + SideEffectsCollector sideEffects{m_dialect, *_varDecl.value, &m_functionSideEffects}; + if (!sideEffects.movableRelativeTo(_forLoopSideEffects, m_containsMSize)) return false; } return true; @@ -82,6 +84,10 @@ bool LoopInvariantCodeMotion::canBePromoted( optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) { assertThrow(_for.pre.statements.empty(), OptimizerException, ""); + + auto forLoopSideEffects = + SideEffectsCollector{m_dialect, _for, &m_functionSideEffects}.sideEffects(); + vector replacement; for (Block* block: {&_for.post, &_for.body}) { @@ -93,7 +99,7 @@ optional> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) if (holds_alternative(_s)) { VariableDeclaration const& varDecl = std::get(_s); - if (canBePromoted(varDecl, varsDefinedInScope)) + if (canBePromoted(varDecl, varsDefinedInScope, forLoopSideEffects)) { replacement.emplace_back(std::move(_s)); // Do not add the variables declared here to varsDefinedInScope because we are moving them. diff --git a/libyul/optimiser/LoopInvariantCodeMotion.h b/libyul/optimiser/LoopInvariantCodeMotion.h index a3bf1784f822..1b264792470c 100644 --- a/libyul/optimiser/LoopInvariantCodeMotion.h +++ b/libyul/optimiser/LoopInvariantCodeMotion.h @@ -49,17 +49,24 @@ class LoopInvariantCodeMotion: public ASTModifier explicit LoopInvariantCodeMotion( Dialect const& _dialect, std::set const& _ssaVariables, - std::map const& _functionSideEffects + std::map const& _functionSideEffects, + bool _containsMSize ): + m_containsMSize(_containsMSize), m_dialect(_dialect), m_ssaVariables(_ssaVariables), m_functionSideEffects(_functionSideEffects) { } /// @returns true if the given variable declaration can be moved to in front of the loop. - bool canBePromoted(VariableDeclaration const& _varDecl, std::set const& _varsDefinedInCurrentScope) const; + bool canBePromoted( + VariableDeclaration const& _varDecl, + std::set const& _varsDefinedInCurrentScope, + SideEffects const& _forLoopSideEffects + ) const; std::optional> rewriteLoop(ForLoop& _for); + bool m_containsMSize = true; Dialect const& m_dialect; std::set const& m_ssaVariables; std::map const& m_functionSideEffects; diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp index 83968651d174..646bc8faf5bb 100644 --- a/libyul/optimiser/MainFunction.cpp +++ b/libyul/optimiser/MainFunction.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include diff --git a/libyul/optimiser/MainFunction.h b/libyul/optimiser/MainFunction.h index 92781f81ccd7..c2f137e48990 100644 --- a/libyul/optimiser/MainFunction.h +++ b/libyul/optimiser/MainFunction.h @@ -22,7 +22,7 @@ #pragma once -#include +#include namespace solidity::yul { diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index 5a7162294a02..0af4d4572ac0 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include #include diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index 5c6f2ab79fc6..bc0fccda4f51 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -21,7 +21,7 @@ #include -#include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index cfc7fdcb961d..01d33919f093 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -22,12 +22,12 @@ #include #include -#include +#include +#include #include -#include -#include +#include -#include +#include using namespace std; using namespace solidity; @@ -35,8 +35,9 @@ using namespace solidity::yul; using namespace solidity::util; NameDispenser::NameDispenser(Dialect const& _dialect, Block const& _ast, set _reservedNames): - NameDispenser(_dialect, NameCollector(_ast).names() + std::move(_reservedNames)) + NameDispenser(_dialect, NameCollector(_ast).names() + _reservedNames) { + m_reservedNames = move(_reservedNames); } NameDispenser::NameDispenser(Dialect const& _dialect, set _usedNames): @@ -59,9 +60,11 @@ YulString NameDispenser::newName(YulString _nameHint) bool NameDispenser::illegalName(YulString _name) { - if (_name.empty() || m_usedNames.count(_name) || m_dialect.builtin(_name)) - return true; - if (dynamic_cast(&m_dialect)) - return Parser::instructions().count(_name.str()); - return false; + return isRestrictedIdentifier(m_dialect, _name) || m_usedNames.count(_name); +} + +void NameDispenser::reset(Block const& _ast) +{ + m_usedNames = NameCollector(_ast).names() + m_reservedNames; + m_counter = 0; } diff --git a/libyul/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h index 1ef8c92e7014..6e655e416b51 100644 --- a/libyul/optimiser/NameDispenser.h +++ b/libyul/optimiser/NameDispenser.h @@ -20,7 +20,7 @@ */ #pragma once -#include +#include #include @@ -51,11 +51,19 @@ class NameDispenser /// return it. void markUsed(YulString _name) { m_usedNames.insert(_name); } -private: + std::set const& usedNames() { return m_usedNames; } + + /// Returns true if `_name` is either used or is a restricted identifier. bool illegalName(YulString _name); + /// Resets `m_usedNames` with *only* the names that are used in the AST. Also resets value of + /// `m_counter` to zero. + void reset(Block const& _ast); + +private: Dialect const& m_dialect; std::set m_usedNames; + std::set m_reservedNames; size_t m_counter = 0; }; diff --git a/libyul/optimiser/NameDisplacer.cpp b/libyul/optimiser/NameDisplacer.cpp index 81248819cd3a..4d1a61a8ef39 100644 --- a/libyul/optimiser/NameDisplacer.cpp +++ b/libyul/optimiser/NameDisplacer.cpp @@ -21,8 +21,7 @@ #include -#include - +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/NameDisplacer.h b/libyul/optimiser/NameDisplacer.h index d1b3e4363b07..336dd04e42de 100644 --- a/libyul/optimiser/NameDisplacer.h +++ b/libyul/optimiser/NameDisplacer.h @@ -60,6 +60,8 @@ class NameDisplacer: public ASTModifier void operator()(FunctionCall& _funCall) override; void operator()(Block& _block) override; + std::map const& translations() const { return m_translations; } + protected: /// Check if the newly introduced identifier @a _name has to be replaced. void checkAndReplaceNew(YulString& _name); diff --git a/libyul/optimiser/NameSimplifier.cpp b/libyul/optimiser/NameSimplifier.cpp new file mode 100644 index 000000000000..81379297de92 --- /dev/null +++ b/libyul/optimiser/NameSimplifier.cpp @@ -0,0 +1,118 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace solidity::yul; +using namespace std; + +NameSimplifier::NameSimplifier(OptimiserStepContext& _context, Block const& _ast): + m_context(_context) +{ + for (YulString name: _context.reservedIdentifiers) + m_translations[name] = name; + + for (YulString const& name: NameCollector(_ast).names()) + findSimplification(name); +} + +void NameSimplifier::operator()(FunctionDefinition& _funDef) +{ + translate(_funDef.name); + renameVariables(_funDef.parameters); + renameVariables(_funDef.returnVariables); + ASTModifier::operator()(_funDef); +} + +void NameSimplifier::operator()(VariableDeclaration& _varDecl) +{ + renameVariables(_varDecl.variables); + ASTModifier::operator()(_varDecl); +} + +void NameSimplifier::renameVariables(vector& _variables) +{ + for (TypedName& typedName: _variables) + translate(typedName.name); +} + +void NameSimplifier::operator()(Identifier& _identifier) +{ + translate(_identifier.name); +} + +void NameSimplifier::operator()(FunctionCall& _funCall) +{ + // The visitor on its own does not visit the function name. + if (!m_context.dialect.builtin(_funCall.functionName.name)) + (*this)(_funCall.functionName); + ASTModifier::operator()(_funCall); +} + +void NameSimplifier::findSimplification(YulString const& _name) +{ + if (m_translations.count(_name)) + return; + + string name = _name.str(); + + static auto replacements = vector>{ + {regex("_\\$\\d+"), ""}, // removes AST IDs + {regex("(abi_..code.*)_to_.*"), "$1"}, // removes _to... for abi functions + {regex("(stringliteral_[0-9a-f][0-9a-f][0-9a-f][0-9a-f])[0-9a-f]*"), "$1"}, // shorten string literal + {regex("tuple_t_"), ""}, + {regex("_memory_ptr"), ""}, + {regex("_calldata_ptr"), "_calldata"}, + {regex("_fromStack"), ""}, + {regex("_storage_storage"), "_storage"}, + {regex("_memory_memory"), "_memory"}, + {regex("t_contract\\$_([^_]*)_"), "$1_"}, + {regex("index_access_t_array"), "index_access"}, + {regex("[0-9]*_$"), ""} + }; + + for (auto const& [pattern, substitute]: replacements) + { + string candidate = regex_replace(name, pattern, substitute); + if (!m_context.dispenser.illegalName(YulString(candidate))) + name = candidate; + } + + if (name != _name.str()) + { + YulString newName{name}; + m_context.dispenser.markUsed(newName); + m_translations[_name] = move(newName); + } +} + +void NameSimplifier::translate(YulString& _name) +{ + auto it = m_translations.find(_name); + if (it != m_translations.end()) + _name = it->second; +} diff --git a/libyul/optimiser/NameSimplifier.h b/libyul/optimiser/NameSimplifier.h new file mode 100644 index 000000000000..34cf6945115b --- /dev/null +++ b/libyul/optimiser/NameSimplifier.h @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace solidity::yul +{ + +struct Dialect; + +/** + * Pass to "simplify" all identifier names. + * + * The purpose of this is to make generated code more readable, but also + * to remove AST identifiers that could lead to a different sorting order + * and thus influence e.g. the order of function inlining. + * + * Prerequisites: Disambiguator, FunctionHoister, FunctionGrouper + */ +class NameSimplifier: public ASTModifier +{ +public: + static constexpr char const* name{"NameSimplifier"}; + static void run(OptimiserStepContext& _context, Block& _ast) + { + NameSimplifier{_context, _ast}(_ast); + } + + using ASTModifier::operator(); + void operator()(VariableDeclaration& _varDecl) override; + void operator()(Identifier& _identifier) override; + void operator()(FunctionCall& _funCall) override; + void operator()(FunctionDefinition& _funDef) override; + +private: + NameSimplifier(OptimiserStepContext& _context, Block const& _ast); + + /// Tries to rename a list of variables. + void renameVariables(std::vector& _variables); + + void findSimplification(YulString const& _name); + void translate(YulString& _name); + + OptimiserStepContext& m_context; + std::map m_translations; +}; + +} diff --git a/libyul/optimiser/OptimiserStep.h b/libyul/optimiser/OptimiserStep.h index 3da161957d5d..7dc2d3ea2c9c 100644 --- a/libyul/optimiser/OptimiserStep.h +++ b/libyul/optimiser/OptimiserStep.h @@ -20,6 +20,7 @@ #include +#include #include #include @@ -49,17 +50,41 @@ struct OptimiserStep virtual ~OptimiserStep() = default; virtual void run(OptimiserStepContext&, Block&) const = 0; + /// @returns non-nullopt if the step cannot be run, for example because it requires + /// an SMT solver to be loaded, but none is available. In that case, the string + /// contains a human-readable reason. + virtual std::optional invalidInCurrentEnvironment() const = 0; std::string name; }; template struct OptimiserStepInstance: public OptimiserStep { +private: + template + struct HasInvalidInCurrentEnvironmentMethod + { + private: + template static auto test(int) -> decltype(U::invalidInCurrentEnvironment(), std::true_type()); + template static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + +public: OptimiserStepInstance(): OptimiserStep{Step::name} {} void run(OptimiserStepContext& _context, Block& _ast) const override { Step::run(_context, _ast); } + std::optional invalidInCurrentEnvironment() const override + { + if constexpr (HasInvalidInCurrentEnvironmentMethod::value) + return Step::invalidInCurrentEnvironment(); + else + return std::nullopt; + } }; diff --git a/libyul/optimiser/OptimizerUtilities.cpp b/libyul/optimiser/OptimizerUtilities.cpp index 3cb66d68930e..52019356ec41 100644 --- a/libyul/optimiser/OptimizerUtilities.cpp +++ b/libyul/optimiser/OptimizerUtilities.cpp @@ -21,15 +21,19 @@ #include -#include +#include +#include +#include #include #include using namespace std; using namespace solidity; +using namespace solidity::langutil; using namespace solidity::util; +using namespace solidity::yul; void yul::removeEmptyBlocks(Block& _block) { @@ -38,3 +42,8 @@ void yul::removeEmptyBlocks(Block& _block) }; boost::range::remove_erase_if(_block.statements, isEmptyBlock); } + +bool yul::isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifier) +{ + return _identifier.empty() || TokenTraits::isYulKeyword(_identifier.str()) || _dialect.reservedIdentifier(_identifier); +} diff --git a/libyul/optimiser/OptimizerUtilities.h b/libyul/optimiser/OptimizerUtilities.h index 61d970877e72..22191538b8b7 100644 --- a/libyul/optimiser/OptimizerUtilities.h +++ b/libyul/optimiser/OptimizerUtilities.h @@ -22,7 +22,9 @@ #pragma once #include -#include +#include +#include +#include namespace solidity::yul { @@ -30,4 +32,8 @@ namespace solidity::yul /// Removes statements that are just empty blocks (non-recursive). void removeEmptyBlocks(Block& _block); +/// Returns true if a given literal can not be used as an identifier. +/// This includes Yul keywords and builtins of the given dialect. +bool isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifier); + } diff --git a/libyul/optimiser/ReasoningBasedSimplifier.cpp b/libyul/optimiser/ReasoningBasedSimplifier.cpp new file mode 100644 index 000000000000..f61bee321c3b --- /dev/null +++ b/libyul/optimiser/ReasoningBasedSimplifier.cpp @@ -0,0 +1,337 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::util; +using namespace solidity::yul; +using namespace solidity::smtutil; + +void ReasoningBasedSimplifier::run(OptimiserStepContext& _context, Block& _ast) +{ + set ssaVars = SSAValueTracker::ssaVariables(_ast); + ReasoningBasedSimplifier{_context.dialect, ssaVars}(_ast); +} + +std::optional ReasoningBasedSimplifier::invalidInCurrentEnvironment() +{ + // SMTLib2 interface is always available, but we would like to have synchronous answers. + if (smtutil::SMTPortfolio{}.solvers() <= 1) + return string{"No SMT solvers available."}; + else + return nullopt; +} + +void ReasoningBasedSimplifier::operator()(VariableDeclaration& _varDecl) +{ + if (_varDecl.variables.size() != 1 || !_varDecl.value) + return; + YulString varName = _varDecl.variables.front().name; + if (!m_ssaVariables.count(varName)) + return; + bool const inserted = m_variables.insert({varName, m_solver->newVariable("yul_" + varName.str(), defaultSort())}).second; + yulAssert(inserted, ""); + m_solver->addAssertion(m_variables.at(varName) == encodeExpression(*_varDecl.value)); +} + +void ReasoningBasedSimplifier::operator()(If& _if) +{ + if (!SideEffectsCollector{m_dialect, *_if.condition}.movable()) + return; + + smtutil::Expression condition = encodeExpression(*_if.condition); + m_solver->push(); + m_solver->addAssertion(condition == constantValue(0)); + CheckResult result = m_solver->check({}).first; + m_solver->pop(); + if (result == CheckResult::UNSATISFIABLE) + { + Literal trueCondition = m_dialect.trueLiteral(); + trueCondition.location = locationOf(*_if.condition); + _if.condition = make_unique(move(trueCondition)); + } + else + { + m_solver->push(); + m_solver->addAssertion(condition != constantValue(0)); + CheckResult result2 = m_solver->check({}).first; + m_solver->pop(); + if (result2 == CheckResult::UNSATISFIABLE) + { + Literal falseCondition = m_dialect.zeroLiteralForType(m_dialect.boolType); + falseCondition.location = locationOf(*_if.condition); + _if.condition = make_unique(move(falseCondition)); + _if.body = yul::Block{}; + // Nothing left to be done. + return; + } + } + + m_solver->push(); + m_solver->addAssertion(condition != constantValue(0)); + + ASTModifier::operator()(_if.body); + + m_solver->pop(); +} + +ReasoningBasedSimplifier::ReasoningBasedSimplifier( + Dialect const& _dialect, + set const& _ssaVariables +): + m_dialect(_dialect), + m_ssaVariables(_ssaVariables), + m_solver(make_unique()) +{ +} + +smtutil::Expression ReasoningBasedSimplifier::encodeExpression(yul::Expression const& _expression) +{ + return std::visit(GenericVisitor{ + [&](FunctionCall const& _functionCall) + { + if (auto const* dialect = dynamic_cast(&m_dialect)) + if (auto const* builtin = dialect->builtin(_functionCall.functionName.name)) + if (builtin->instruction) + return encodeEVMBuiltin(*builtin->instruction, _functionCall.arguments); + return newRestrictedVariable(); + }, + [&](Identifier const& _identifier) + { + if ( + m_ssaVariables.count(_identifier.name) && + m_variables.count(_identifier.name) + ) + return m_variables.at(_identifier.name); + else + return newRestrictedVariable(); + }, + [&](Literal const& _literal) + { + return literalValue(_literal); + } + }, _expression); +} + +smtutil::Expression ReasoningBasedSimplifier::encodeEVMBuiltin( + evmasm::Instruction _instruction, + vector const& _arguments +) +{ + vector arguments = applyMap( + _arguments, + [this](yul::Expression const& _expr) { return encodeExpression(_expr); } + ); + switch (_instruction) + { + case evmasm::Instruction::ADD: + return wrap(arguments.at(0) + arguments.at(1)); + case evmasm::Instruction::MUL: + return wrap(arguments.at(0) * arguments.at(1)); + case evmasm::Instruction::SUB: + return wrap(arguments.at(0) - arguments.at(1)); + case evmasm::Instruction::DIV: + return smtutil::Expression::ite( + arguments.at(1) == constantValue(0), + constantValue(0), + arguments.at(0) / arguments.at(1) + ); + case evmasm::Instruction::SDIV: + return smtutil::Expression::ite( + arguments.at(1) == constantValue(0), + constantValue(0), + // No `wrap()` needed here, because -2**255 / -1 results + // in 2**255 which is "converted" to its two's complement + // representation 2**255 in `signedToUnsigned` + signedToUnsigned(smtutil::signedDivisionEVM( + unsignedToSigned(arguments.at(0)), + unsignedToSigned(arguments.at(1)) + )) + ); + case evmasm::Instruction::MOD: + return smtutil::Expression::ite( + arguments.at(1) == constantValue(0), + constantValue(0), + arguments.at(0) % arguments.at(1) + ); + case evmasm::Instruction::SMOD: + return smtutil::Expression::ite( + arguments.at(1) == constantValue(0), + constantValue(0), + signedToUnsigned(signedModuloEVM( + unsignedToSigned(arguments.at(0)), + unsignedToSigned(arguments.at(1)) + )) + ); + case evmasm::Instruction::LT: + return booleanValue(arguments.at(0) < arguments.at(1)); + case evmasm::Instruction::SLT: + return booleanValue(unsignedToSigned(arguments.at(0)) < unsignedToSigned(arguments.at(1))); + case evmasm::Instruction::GT: + return booleanValue(arguments.at(0) > arguments.at(1)); + case evmasm::Instruction::SGT: + return booleanValue(unsignedToSigned(arguments.at(0)) > unsignedToSigned(arguments.at(1))); + case evmasm::Instruction::EQ: + return booleanValue(arguments.at(0) == arguments.at(1)); + case evmasm::Instruction::ISZERO: + return booleanValue(arguments.at(0) == constantValue(0)); + case evmasm::Instruction::AND: + return smtutil::Expression::ite( + (arguments.at(0) == 0 || arguments.at(0) == 1) && + (arguments.at(1) == 0 || arguments.at(1) == 1), + booleanValue(arguments.at(0) == 1 && arguments.at(1) == 1), + bv2int(int2bv(arguments.at(0)) & int2bv(arguments.at(1))) + ); + case evmasm::Instruction::OR: + return smtutil::Expression::ite( + (arguments.at(0) == 0 || arguments.at(0) == 1) && + (arguments.at(1) == 0 || arguments.at(1) == 1), + booleanValue(arguments.at(0) == 1 || arguments.at(1) == 1), + bv2int(int2bv(arguments.at(0)) | int2bv(arguments.at(1))) + ); + case evmasm::Instruction::XOR: + return bv2int(int2bv(arguments.at(0)) ^ int2bv(arguments.at(1))); + case evmasm::Instruction::NOT: + return smtutil::Expression(u256(-1)) - arguments.at(0); + case evmasm::Instruction::SHL: + return smtutil::Expression::ite( + arguments.at(0) > 255, + constantValue(0), + bv2int(int2bv(arguments.at(1)) << int2bv(arguments.at(0))) + ); + case evmasm::Instruction::SHR: + return smtutil::Expression::ite( + arguments.at(0) > 255, + constantValue(0), + bv2int(int2bv(arguments.at(1)) >> int2bv(arguments.at(0))) + ); + case evmasm::Instruction::SAR: + return smtutil::Expression::ite( + arguments.at(0) > 255, + constantValue(0), + bv2int(smtutil::Expression::ashr(int2bv(arguments.at(1)), int2bv(arguments.at(0)))) + ); + case evmasm::Instruction::ADDMOD: + return smtutil::Expression::ite( + arguments.at(2) == constantValue(0), + constantValue(0), + (arguments.at(0) + arguments.at(1)) % arguments.at(2) + ); + case evmasm::Instruction::MULMOD: + return smtutil::Expression::ite( + arguments.at(2) == constantValue(0), + constantValue(0), + (arguments.at(0) * arguments.at(1)) % arguments.at(2) + ); + // TODO SIGNEXTEND + default: + break; + } + return newRestrictedVariable(); +} + +smtutil::Expression ReasoningBasedSimplifier::int2bv(smtutil::Expression _arg) const +{ + return smtutil::Expression::int2bv(std::move(_arg), 256); +} + +smtutil::Expression ReasoningBasedSimplifier::bv2int(smtutil::Expression _arg) const +{ + return smtutil::Expression::bv2int(std::move(_arg)); +} + +smtutil::Expression ReasoningBasedSimplifier::newVariable() +{ + return m_solver->newVariable(uniqueName(), defaultSort()); +} + +smtutil::Expression ReasoningBasedSimplifier::newRestrictedVariable() +{ + smtutil::Expression var = newVariable(); + m_solver->addAssertion(0 <= var && var < smtutil::Expression(bigint(1) << 256)); + return var; +} + +string ReasoningBasedSimplifier::uniqueName() +{ + return "expr_" + to_string(m_varCounter++); +} + +shared_ptr ReasoningBasedSimplifier::defaultSort() const +{ + return SortProvider::intSort(); +} + +smtutil::Expression ReasoningBasedSimplifier::booleanValue(smtutil::Expression _value) const +{ + return smtutil::Expression::ite(_value, constantValue(1), constantValue(0)); +} + +smtutil::Expression ReasoningBasedSimplifier::constantValue(size_t _value) const +{ + return _value; +} + +smtutil::Expression ReasoningBasedSimplifier::literalValue(Literal const& _literal) const +{ + return smtutil::Expression(valueOfLiteral(_literal)); +} + +smtutil::Expression ReasoningBasedSimplifier::unsignedToSigned(smtutil::Expression _value) +{ + return smtutil::Expression::ite( + _value < smtutil::Expression(bigint(1) << 255), + _value, + _value - smtutil::Expression(bigint(1) << 256) + ); +} + +smtutil::Expression ReasoningBasedSimplifier::signedToUnsigned(smtutil::Expression _value) +{ + return smtutil::Expression::ite( + _value >= 0, + _value, + _value + smtutil::Expression(bigint(1) << 256) + ); +} + +smtutil::Expression ReasoningBasedSimplifier::wrap(smtutil::Expression _value) +{ + smtutil::Expression rest = newRestrictedVariable(); + smtutil::Expression multiplier = newVariable(); + m_solver->addAssertion(_value == multiplier * smtutil::Expression(bigint(1) << 256) + rest); + return rest; +} diff --git a/libyul/optimiser/ReasoningBasedSimplifier.h b/libyul/optimiser/ReasoningBasedSimplifier.h new file mode 100644 index 000000000000..18fcf358350e --- /dev/null +++ b/libyul/optimiser/ReasoningBasedSimplifier.h @@ -0,0 +1,98 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#pragma once + +#include +#include +#include + +// because of instruction +#include + +#include + +namespace solidity::smtutil +{ +class SolverInterface; +class Expression; +struct Sort; +} + +namespace solidity::yul +{ + +/** + * Reasoning-based simplifier. + * This optimizer uses SMT solvers to check whether `if` conditions are constant. + * - If `constraints AND condition` is UNSAT, the condition is never true and the whole body can be removed. + * - If `constraints AND NOT condition` is UNSAT, the condition is always true and can be replaced by `1`. + * The simplifications above can only be applied if the condition is movable. + * + * It is only effective on the EVM dialect, but safe to use on other dialects. + * + * Prerequisite: Disambiguator, SSATransform. + */ +class ReasoningBasedSimplifier: public ASTModifier +{ +public: + static constexpr char const* name{"ReasoningBasedSimplifier"}; + static void run(OptimiserStepContext& _context, Block& _ast); + static std::optional invalidInCurrentEnvironment(); + + using ASTModifier::operator(); + void operator()(VariableDeclaration& _varDecl) override; + void operator()(If& _if) override; + +private: + explicit ReasoningBasedSimplifier( + Dialect const& _dialect, + std::set const& _ssaVariables + ); + + smtutil::Expression encodeExpression( + Expression const& _expression + ); + + virtual smtutil::Expression encodeEVMBuiltin( + evmasm::Instruction _instruction, + std::vector const& _arguments + ); + + smtutil::Expression int2bv(smtutil::Expression _arg) const; + smtutil::Expression bv2int(smtutil::Expression _arg) const; + + smtutil::Expression newVariable(); + virtual smtutil::Expression newRestrictedVariable(); + std::string uniqueName(); + + virtual std::shared_ptr defaultSort() const; + virtual smtutil::Expression booleanValue(smtutil::Expression _value) const; + virtual smtutil::Expression constantValue(size_t _value) const; + virtual smtutil::Expression literalValue(Literal const& _literal) const; + virtual smtutil::Expression unsignedToSigned(smtutil::Expression _value); + virtual smtutil::Expression signedToUnsigned(smtutil::Expression _value); + virtual smtutil::Expression wrap(smtutil::Expression _value); + + Dialect const& m_dialect; + std::set const& m_ssaVariables; + std::unique_ptr m_solver; + std::map m_variables; + + size_t m_varCounter = 0; +}; + +} diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp index 170cf1d79772..cd528aedfacb 100644 --- a/libyul/optimiser/RedundantAssignEliminator.cpp +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h index 81e977e8d2e5..bccc1e8ef951 100644 --- a/libyul/optimiser/RedundantAssignEliminator.h +++ b/libyul/optimiser/RedundantAssignEliminator.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 53974616cec3..abd1bcdd8a98 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp index decdc4ea4489..d59a93d0e22d 100644 --- a/libyul/optimiser/SSAReverser.cpp +++ b/libyul/optimiser/SSAReverser.cpp @@ -17,7 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include #include -#include +#include #include #include @@ -73,7 +73,7 @@ void SSAReverser::operator()(Block& _block) VariableDeclaration{ std::move(varDecl->location), std::move(varDecl->variables), - std::make_unique(std::move(assignment->variableNames.front())) + std::make_unique(assignment->variableNames.front()) } ); } diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index 332b7af1dc87..f77e836f2e64 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include diff --git a/libyul/optimiser/SSATransform.h b/libyul/optimiser/SSATransform.h index 74e52b62ac92..66015362a15a 100644 --- a/libyul/optimiser/SSATransform.h +++ b/libyul/optimiser/SSATransform.h @@ -21,7 +21,7 @@ */ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp index 452123447953..df3c5adce191 100644 --- a/libyul/optimiser/SSAValueTracker.cpp +++ b/libyul/optimiser/SSAValueTracker.cpp @@ -22,7 +22,7 @@ #include -#include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h index f206cab5e89f..f1c93ff229de 100644 --- a/libyul/optimiser/SSAValueTracker.h +++ b/libyul/optimiser/SSAValueTracker.h @@ -23,7 +23,7 @@ #pragma once #include -#include +#include // Needed for m_zero below. #include #include diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 4a7b8db12875..070e83c3e9d3 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include @@ -62,6 +62,16 @@ SideEffectsCollector::SideEffectsCollector( operator()(_ast); } +SideEffectsCollector::SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + map const* _functionSideEffects +): + SideEffectsCollector(_dialect, _functionSideEffects) +{ + operator()(_ast); +} + void SideEffectsCollector::operator()(FunctionCall const& _functionCall) { ASTWalker::operator()(_functionCall); @@ -106,8 +116,9 @@ map SideEffectsPropagator::sideEffects( for (auto const& function: _directCallGraph.functionsWithLoops + _directCallGraph.recursiveFunctions()) { ret[function].movable = false; - ret[function].sideEffectFree = false; - ret[function].sideEffectFreeIfNoMSize = false; + ret[function].canBeRemoved = false; + ret[function].canBeRemovedIfNoMSize = false; + ret[function].cannotLoop = false; } for (auto const& call: _directCallGraph.functionCalls) diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index a4f7b810230a..3426f23d8d37 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include @@ -54,21 +54,60 @@ class SideEffectsCollector: public ASTWalker Block const& _ast, std::map const* _functionSideEffects = nullptr ); + SideEffectsCollector( + Dialect const& _dialect, + ForLoop const& _ast, + std::map const* _functionSideEffects = nullptr + ); using ASTWalker::operator(); void operator()(FunctionCall const& _functionCall) override; bool movable() const { return m_sideEffects.movable; } - bool sideEffectFree(bool _allowMSizeModification = false) const + + bool movableRelativeTo(SideEffects const& _other, bool _codeContainsMSize) + { + if (!m_sideEffects.cannotLoop) + return false; + + if (m_sideEffects.movable) + return true; + + if ( + !m_sideEffects.movableApartFromEffects || + m_sideEffects.storage == SideEffects::Write || + m_sideEffects.otherState == SideEffects::Write || + m_sideEffects.memory == SideEffects::Write + ) + return false; + + if (m_sideEffects.otherState == SideEffects::Read) + if (_other.otherState == SideEffects::Write) + return false; + + if (m_sideEffects.storage == SideEffects::Read) + if (_other.storage == SideEffects::Write) + return false; + + if (m_sideEffects.memory == SideEffects::Read) + if (_codeContainsMSize || _other.memory == SideEffects::Write) + return false; + + return true; + } + + bool canBeRemoved(bool _allowMSizeModification = false) const { if (_allowMSizeModification) - return sideEffectFreeIfNoMSize(); + return m_sideEffects.canBeRemovedIfNoMSize; else - return m_sideEffects.sideEffectFree; + return m_sideEffects.canBeRemoved; } - bool sideEffectFreeIfNoMSize() const { return m_sideEffects.sideEffectFreeIfNoMSize; } - bool invalidatesStorage() const { return m_sideEffects.invalidatesStorage; } - bool invalidatesMemory() const { return m_sideEffects.invalidatesMemory; } + bool cannotLoop() const { return m_sideEffects.cannotLoop; } + bool invalidatesStorage() const { return m_sideEffects.storage == SideEffects::Write; } + bool invalidatesMemory() const { return m_sideEffects.memory == SideEffects::Write; } + + SideEffects sideEffects() { return m_sideEffects; } private: Dialect const& m_dialect; diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 2fe5de3e546c..8e55e8c5ec90 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -171,12 +171,22 @@ bool Pattern::matches( return false; assertThrow(m_arguments.size() == instrAndArgs->second->size(), OptimizerException, ""); for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i].matches(instrAndArgs->second->at(i), _dialect, _ssaValues)) + { + Expression const& arg = instrAndArgs->second->at(i); + // If this is a direct function call instead of a variable or literal, + // we reject the match because side-effects could prevent us from + // arbitrarily modifying the code. + if ( + holds_alternative(arg) || + !m_arguments[i].matches(arg, _dialect, _ssaValues) + ) return false; + } } else { assertThrow(m_arguments.empty(), OptimizerException, "\"Any\" should not have arguments."); + assertThrow(!holds_alternative(*expr), OptimizerException, "\"Any\" at top-level."); } if (m_matchGroup) @@ -197,9 +207,14 @@ bool Pattern::matches( assertThrow(m_kind == PatternKind::Any, OptimizerException, "Match group repetition for non-any."); Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; assertThrow(firstMatch, OptimizerException, "Match set but to null."); - return - SyntacticallyEqual{}(*firstMatch, _expr) && - SideEffectsCollector(_dialect, _expr).movable(); + assertThrow( + !holds_alternative(_expr) && + !holds_alternative(*firstMatch), + OptimizerException, + "Group matches have to be literals or variables." + ); + + return SyntacticallyEqual{}(*firstMatch, _expr); } else if (m_kind == PatternKind::Any) (*m_matchGroups)[m_matchGroup] = &_expr; diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index 05e4e0d590bd..ca0b7a801ee0 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -23,12 +23,13 @@ #include -#include -#include +#include +#include #include #include +#include #include diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index d011c64b8b8e..a8dc9d8713aa 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -30,7 +30,7 @@ #include -#include +#include using namespace std; using namespace solidity; @@ -168,7 +168,7 @@ bool StackCompressor::run( bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code); for (size_t iterations = 0; iterations < _maxIterations; iterations++) { - map stackSurplus = CompilabilityChecker::run(_dialect, _object, _optimizeStackAllocation); + map stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit; if (stackSurplus.empty()) return true; diff --git a/libyul/optimiser/StackCompressor.h b/libyul/optimiser/StackCompressor.h index f10a6ba1767b..d18618667f71 100644 --- a/libyul/optimiser/StackCompressor.h +++ b/libyul/optimiser/StackCompressor.h @@ -22,6 +22,8 @@ #pragma once +#include + #include namespace solidity::yul diff --git a/libyul/optimiser/StackLimitEvader.cpp b/libyul/optimiser/StackLimitEvader.cpp new file mode 100644 index 000000000000..a7169f08a533 --- /dev/null +++ b/libyul/optimiser/StackLimitEvader.cpp @@ -0,0 +1,144 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; + +namespace +{ +/** + * Walks the call graph using a Depth-First-Search assigning memory slots to variables. + * - The leaves of the call graph will get the lowest slot, increasing towards the root. + * - ``slotsRequiredForFunction`` maps a function to the number of slots it requires (which is also the + * next available slot that can be used by another function that calls this function). + * - For each function starting from the root of the call graph: + * - Visit all children that are not already visited. + * - Determine the maximum value ``n`` of the values of ``slotsRequiredForFunction`` among the children. + * - If the function itself contains variables that need memory slots, but is contained in a cycle, + * abort the process as failure. + * - If not, assign each variable its slot starting from ``n`` (incrementing it). + * - Assign ``n`` to ``slotsRequiredForFunction`` of the function. + */ +struct MemoryOffsetAllocator +{ + uint64_t run(YulString _function = YulString{}) + { + if (slotsRequiredForFunction.count(_function)) + return slotsRequiredForFunction[_function]; + + // Assign to zero early to guard against recursive calls. + slotsRequiredForFunction[_function] = 0; + + uint64_t requiredSlots = 0; + if (callGraph.count(_function)) + for (YulString child: callGraph.at(_function)) + requiredSlots = std::max(run(child), requiredSlots); + + if (unreachableVariables.count(_function)) + { + yulAssert(!slotAllocations.count(_function), ""); + for (YulString variable: unreachableVariables.at(_function)) + if (variable.empty()) + { + // TODO: Too many function arguments or return parameters. + } + else + slotAllocations[variable] = requiredSlots++; + } + + return slotsRequiredForFunction[_function] = requiredSlots; + } + + map> const& unreachableVariables; + map> const& callGraph; + + map slotAllocations{}; + map slotsRequiredForFunction{}; +}; + +u256 literalArgumentValue(FunctionCall const& _call) +{ + yulAssert(_call.arguments.size() == 1, ""); + Literal const* literal = std::get_if(&_call.arguments.front()); + yulAssert(literal && literal->kind == LiteralKind::Number, ""); + return valueOfLiteral(*literal); +} +} + +void StackLimitEvader::run( + OptimiserStepContext& _context, + Object& _object, + map> const& _unreachableVariables +) +{ + yulAssert(_object.code, ""); + auto const* evmDialect = dynamic_cast(&_context.dialect); + yulAssert( + evmDialect && evmDialect->providesObjectAccess(), + "StackLimitEvader can only be run on objects using the EVMDialect with object access." + ); + + vector memoryGuardCalls = FunctionCallFinder::run( + *_object.code, + "memoryguard"_yulstring + ); + // Do not optimise, if no ``memoryguard`` call is found. + if (memoryGuardCalls.empty()) + return; + + // Make sure all calls to ``memoryguard`` we found have the same value as argument (otherwise, abort). + u256 reservedMemory = literalArgumentValue(*memoryGuardCalls.front()); + for (FunctionCall const* memoryGuardCall: memoryGuardCalls) + if (reservedMemory != literalArgumentValue(*memoryGuardCall)) + return; + + CallGraph callGraph = CallGraphGenerator::callGraph(*_object.code); + + // We cannot move variables in recursive functions to fixed memory offsets. + for (YulString function: callGraph.recursiveFunctions()) + if (_unreachableVariables.count(function)) + return; + + MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls}; + uint64_t requiredSlots = memoryOffsetAllocator.run(); + + StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, *_object.code); + + yulAssert(requiredSlots < std::numeric_limits::max() / 32, ""); + reservedMemory += 32 * requiredSlots; + for (FunctionCall* memoryGuardCall: FunctionCallFinder::run(*_object.code, "memoryguard"_yulstring)) + { + Literal* literal = std::get_if(&memoryGuardCall->arguments.front()); + yulAssert(literal && literal->kind == LiteralKind::Number, ""); + literal->value = YulString{util::toCompactHexWithPrefix(reservedMemory)}; + } +} diff --git a/libyul/optimiser/StackLimitEvader.h b/libyul/optimiser/StackLimitEvader.h new file mode 100644 index 000000000000..4fc351f73a51 --- /dev/null +++ b/libyul/optimiser/StackLimitEvader.h @@ -0,0 +1,66 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimisation stage that assigns memory offsets to variables that would become unreachable if + * assigned a stack slot as usual and replaces references and assignments to them by mload and mstore calls. + */ + +#pragma once + +#include + +namespace solidity::yul +{ + +struct Object; + +/** + * Optimisation stage that assigns memory offsets to variables that would become unreachable if + * assigned a stack slot as usual. + * + * Uses CompilabilityChecker to determine which variables in which functions are unreachable. + * + * Only variables outside of functions contained in cycles in the call graph are considered. Thereby it is possible + * to assign globally fixed memory offsets to the variable. If a variable in a function contained in a cycle in the + * call graph is reported as unreachable, the process is aborted. + * + * Offsets are assigned to the variables, s.t. on every path through the call graph each variable gets a unique offset + * in memory. However, distinct paths through the call graph can use the same memory offsets for their variables. + * + * The current arguments to the ``memoryguard`` calls are used as base memory offset and then replaced by the offset past + * the last memory offset used for a variable on any path through the call graph. + * + * Finally, the StackToMemoryMover is called to actually move the variables to their offsets in memory. + * + * Prerequisite: Disambiguator + */ +class StackLimitEvader +{ +public: + /// @a _unreachableVariables can be determined by the CompilabilityChecker. + /// Can only be run on the EVM dialect with objects. + /// Abort and do nothing, if no ``memoryguard`` call or several ``memoryguard`` calls + /// with non-matching arguments are found, or if any of the @a _unreachableVariables + /// are contained in a recursive function. + static void run( + OptimiserStepContext& _context, + Object& _object, + std::map> const& _unreachableVariables + ); +}; + +} diff --git a/libyul/optimiser/StackToMemoryMover.cpp b/libyul/optimiser/StackToMemoryMover.cpp new file mode 100644 index 000000000000..25eb25cd5204 --- /dev/null +++ b/libyul/optimiser/StackToMemoryMover.cpp @@ -0,0 +1,207 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#include +#include +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; + +namespace +{ +vector generateMemoryStore( + Dialect const& _dialect, + langutil::SourceLocation const& _loc, + YulString _mpos, + Expression _value +) +{ + BuiltinFunction const* memoryStoreFunction = _dialect.memoryStoreFunction(_dialect.defaultType); + yulAssert(memoryStoreFunction, ""); + vector result; + result.emplace_back(ExpressionStatement{_loc, FunctionCall{ + _loc, + Identifier{_loc, memoryStoreFunction->name}, + { + Literal{_loc, LiteralKind::Number, _mpos, {}}, + std::move(_value) + } + }}); + return result; +} + +FunctionCall generateMemoryLoad(Dialect const& _dialect, langutil::SourceLocation const& _loc, YulString _mpos) +{ + BuiltinFunction const* memoryLoadFunction = _dialect.memoryLoadFunction(_dialect.defaultType); + yulAssert(memoryLoadFunction, ""); + return FunctionCall{ + _loc, + Identifier{_loc, memoryLoadFunction->name}, { + Literal{ + _loc, + LiteralKind::Number, + _mpos, + {} + } + } + }; +} +} + +void StackToMemoryMover::run( + OptimiserStepContext& _context, + u256 _reservedMemory, + map const& _memorySlots, + uint64_t _numRequiredSlots, + Block& _block +) +{ + VariableMemoryOffsetTracker memoryOffsetTracker(_reservedMemory, _memorySlots, _numRequiredSlots); + StackToMemoryMover stackToMemoryMover(_context, memoryOffsetTracker); + stackToMemoryMover(_block); +} + +StackToMemoryMover::StackToMemoryMover( + OptimiserStepContext& _context, + VariableMemoryOffsetTracker const& _memoryOffsetTracker +): +m_context(_context), +m_memoryOffsetTracker(_memoryOffsetTracker), +m_nameDispenser(_context.dispenser) +{ + auto const* evmDialect = dynamic_cast(&_context.dialect); + yulAssert( + evmDialect && evmDialect->providesObjectAccess(), + "StackToMemoryMover can only be run on objects using the EVMDialect with object access." + ); +} + +void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition) +{ + for (TypedName const& param: _functionDefinition.parameters + _functionDefinition.returnVariables) + if (m_memoryOffsetTracker(param.name)) + { + // TODO: we cannot handle function parameters yet. + return; + } + ASTModifier::operator()(_functionDefinition); +} + +void StackToMemoryMover::operator()(Block& _block) +{ + using OptionalStatements = std::optional>; + auto rewriteAssignmentOrVariableDeclaration = [&]( + auto& _stmt, + auto const& _variables + ) -> OptionalStatements { + using StatementType = decay_t; + if (_stmt.value) + visit(*_stmt.value); + bool leftHandSideNeedsMoving = util::contains_if(_variables, [&](auto const& var) { + return m_memoryOffsetTracker(var.name); + }); + if (!leftHandSideNeedsMoving) + return {}; + + langutil::SourceLocation loc = _stmt.location; + + if (_variables.size() == 1) + { + optional offset = m_memoryOffsetTracker(_variables.front().name); + yulAssert(offset, ""); + return generateMemoryStore( + m_context.dialect, + loc, + *offset, + _stmt.value ? *std::move(_stmt.value) : Literal{loc, LiteralKind::Number, "0"_yulstring, {}} + ); + } + + VariableDeclaration tempDecl{loc, {}, std::move(_stmt.value)}; + vector memoryAssignments; + vector variableAssignments; + for (auto& var: _variables) + { + YulString tempVarName = m_nameDispenser.newName(var.name); + tempDecl.variables.emplace_back(TypedName{var.location, tempVarName, {}}); + + if (optional offset = m_memoryOffsetTracker(var.name)) + memoryAssignments += generateMemoryStore( + m_context.dialect, + loc, + *offset, + Identifier{loc, tempVarName} + ); + else + variableAssignments.emplace_back(StatementType{ + loc, {move(var)}, + make_unique(Identifier{loc, tempVarName}) + }); + } + std::vector result; + result.emplace_back(std::move(tempDecl)); + std::reverse(memoryAssignments.begin(), memoryAssignments.end()); + result += std::move(memoryAssignments); + std::reverse(variableAssignments.begin(), variableAssignments.end()); + result += std::move(variableAssignments); + return OptionalStatements{move(result)}; + }; + + util::iterateReplacing( + _block.statements, + [&](Statement& _statement) + { + return std::visit(util::GenericVisitor{ + [&](Assignment& _assignment) -> OptionalStatements + { + return rewriteAssignmentOrVariableDeclaration(_assignment, _assignment.variableNames); + }, + [&](VariableDeclaration& _varDecl) -> OptionalStatements + { + return rewriteAssignmentOrVariableDeclaration(_varDecl, _varDecl.variables); + }, + [&](auto& _stmt) -> OptionalStatements { (*this)(_stmt); return {}; } + }, _statement); + } + ); +} + +void StackToMemoryMover::visit(Expression& _expression) +{ + ASTModifier::visit(_expression); + if (Identifier* identifier = std::get_if(&_expression)) + if (optional offset = m_memoryOffsetTracker(identifier->name)) + _expression = generateMemoryLoad(m_context.dialect, identifier->location, *offset); +} + +optional StackToMemoryMover::VariableMemoryOffsetTracker::operator()(YulString _variable) const +{ + if (m_memorySlots.count(_variable)) + { + uint64_t slot = m_memorySlots.at(_variable); + yulAssert(slot < m_numRequiredSlots, ""); + return YulString{util::toCompactHexWithPrefix(m_reservedMemory + 32 * (m_numRequiredSlots - slot - 1))}; + } + else + return std::nullopt; +} diff --git a/libyul/optimiser/StackToMemoryMover.h b/libyul/optimiser/StackToMemoryMover.h new file mode 100644 index 000000000000..2ec831946a87 --- /dev/null +++ b/libyul/optimiser/StackToMemoryMover.h @@ -0,0 +1,132 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimisation stage that moves Yul variables from stack to memory. + */ + +#pragma once + +#include +#include +#include + +namespace solidity::yul +{ + +/** + * Optimisation stage that moves Yul variables from stack to memory. + * It takes a map from functions names and variable names to memory offsets. + * It then transforms the AST as follows: + * + * Single variable declarations are replaced by mstore's as follows: + * If a is in the map, replace + * let a + * by + * mstore(, 0) + * respectively, replace + * let a := expr + * by + * mstore(, expr) + * + * In a multi-variable declaration, variables to be moved are replaced by fresh variables and then moved to memory: + * If b and d are in the map, replace + * let a, b, c, d := f() + * by + * let _1, _2, _3, _4 := f() + * mstore(, _4) + * mstore(, _2) + * let c := _3 + * let a := _1 + * + * Assignments to single variables are replaced by mstore's: + * If a is in the map, replace + * a := expr + * by + * mstore(, expr) + * + * Assignments to multiple variables are split up similarly to multi-variable declarations: + * If b and d are in the map, replace + * a, b, c, d := f() + * by + * let _1, _2, _3, _4 := f() + * mstore(, _4) + * mstore(, _2) + * c := _3 + * a := _1 + * + * Replace all references to a variable ``a`` in the map by ``mload()``. + * + * If a visited function has arguments or return parameters that are contained in the map, + * the entire function is skipped (no local variables in the function will be moved at all). + * + * Prerequisite: Disambiguator, ForLoopInitRewriter, FunctionHoister. + */ +class StackToMemoryMover: ASTModifier +{ +public: + /** + * Runs the stack to memory mover. + * @param _reservedMemory Is the amount of previously reserved memory, + * i.e. the lowest memory offset to which variables can be moved. + * @param _memorySlots A map from variables to a slot in memory. Based on the slot a unique offset in the memory range + * between _reservedMemory and _reservedMemory + 32 * _numRequiredSlots is calculated for each + * variable. + * @param _numRequiredSlots The number of slots required in total. The maximum value that may occur in @a _memorySlots. + */ + static void run( + OptimiserStepContext& _context, + u256 _reservedMemory, + std::map const& _memorySlots, + uint64_t _numRequiredSlots, + Block& _block + ); + using ASTModifier::operator(); + + void operator()(FunctionDefinition& _functionDefinition) override; + void operator()(Block& _block) override; + void visit(Expression& _expression) override; +private: + class VariableMemoryOffsetTracker + { + public: + VariableMemoryOffsetTracker( + u256 _reservedMemory, + std::map const& _memorySlots, + uint64_t _numRequiredSlots + ): m_reservedMemory(_reservedMemory), m_memorySlots(_memorySlots), m_numRequiredSlots(_numRequiredSlots) + {} + + /// @returns a YulString containing the memory offset to be assigned to @a _variable as number literal + /// or std::nullopt if the variable should not be moved. + std::optional operator()(YulString _variable) const; + private: + u256 m_reservedMemory; + std::map const& m_memorySlots; + uint64_t m_numRequiredSlots = 0; + }; + + StackToMemoryMover( + OptimiserStepContext& _context, + VariableMemoryOffsetTracker const& _memoryOffsetTracker + ); + + OptimiserStepContext& m_context; + VariableMemoryOffsetTracker const& m_memoryOffsetTracker; + NameDispenser& m_nameDispenser; +}; + +} diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 09fe9ca1fa16..4cfa0b3f1778 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -17,7 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include #include -#include +#include #include #include #include diff --git a/libyul/optimiser/Substitution.cpp b/libyul/optimiser/Substitution.cpp index ee2323577954..e0557ad74ddf 100644 --- a/libyul/optimiser/Substitution.cpp +++ b/libyul/optimiser/Substitution.cpp @@ -21,7 +21,7 @@ #include -#include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index a12ecd194d96..3f59e89e6862 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -41,7 +41,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -49,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -56,11 +59,12 @@ #include #include #include +#include #include #include #include -#include #include +#include #include #include @@ -70,6 +74,7 @@ #include #include +#include using namespace std; using namespace solidity; @@ -96,10 +101,11 @@ void OptimiserSuite::run( OptimiserSuite suite(_dialect, reservedIdentifiers, Debug::None, ast); - // Some steps depend on properties ensured by FunctionHoister, FunctionGrouper and + // Some steps depend on properties ensured by FunctionHoister, BlockFlattener, FunctionGrouper and // ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely. - suite.runSequence("fgo", ast); + suite.runSequence("hfgo", ast); + NameSimplifier::run(suite.m_context, ast); // Now the user-supplied part suite.runSequence(_optimisationSequence, ast); @@ -121,6 +127,12 @@ void OptimiserSuite::run( { yulAssert(_meter, ""); ConstantOptimiser{*dialect, *_meter}(ast); + if (dialect->providesObjectAccess() && _optimizeStackAllocation) + StackLimitEvader::run(suite.m_context, _object, CompilabilityChecker{ + _dialect, + _object, + _optimizeStackAllocation + }.unreachableVariables); } else if (dynamic_cast(&_dialect)) { @@ -129,6 +141,9 @@ void OptimiserSuite::run( if (ast.statements.size() > 1 && std::get(ast.statements.front()).statements.empty()) ast.statements.erase(ast.statements.begin()); } + + suite.m_dispenser.reset(ast); + NameSimplifier::run(suite.m_context, ast); VarNameCleaner::run(suite.m_context, ast); *_object.analysisInfo = AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object); @@ -181,14 +196,17 @@ map> const& OptimiserSuite::allSteps() LoadResolver, LoopInvariantCodeMotion, RedundantAssignEliminator, + ReasoningBasedSimplifier, Rematerialiser, SSAReverser, SSATransform, StructuralSimplifier, + UnusedFunctionParameterPruner, UnusedPruner, VarDeclInitializer >(); // Does not include VarNameCleaner because it destroys the property of unique names. + // Does not include NameSimplifier. return instance; } @@ -216,11 +234,13 @@ map const& OptimiserSuite::stepNameToAbbreviationMap() {LiteralRematerialiser::name, 'T'}, {LoadResolver::name, 'L'}, {LoopInvariantCodeMotion::name, 'M'}, + {ReasoningBasedSimplifier::name, 'R'}, {RedundantAssignEliminator::name, 'r'}, {Rematerialiser::name, 'm'}, {SSAReverser::name, 'V'}, {SSATransform::name, 'a'}, {StructuralSimplifier::name, 't'}, + {UnusedFunctionParameterPruner::name, 'p'}, {UnusedPruner::name, 'u'}, {VarDeclInitializer::name, 'd'}, }; @@ -260,6 +280,7 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations) insideLoop = false; break; default: + { yulAssert( string(NonStepAbbreviations).find(abbreviation) == string::npos, "Unhandled syntactic element in the abbreviation sequence" @@ -269,6 +290,14 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations) OptimizerException, "'"s + abbreviation + "' is not a valid step abbreviation" ); + optional invalid = allSteps().at(stepAbbreviationToNameMap().at(abbreviation))->invalidInCurrentEnvironment(); + assertThrow( + !invalid.has_value(), + OptimizerException, + "'"s + abbreviation + "' is invalid in the current environment: " + *invalid + ); + + } } assertThrow(!insideLoop, OptimizerException, "Unbalanced brackets"); } @@ -338,6 +367,9 @@ void OptimiserSuite::runSequenceUntilStable( size_t maxRounds ) { + if (_steps.empty()) + return; + size_t codeSize = 0; for (size_t rounds = 0; rounds < maxRounds; ++rounds) { diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index affbd37c0fd8..9ae0ada586e6 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include #include diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index 1ce5da7d15cd..490e1c9e7bcf 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index 774a223cc3d3..02a6d4b397f8 100644 --- a/libyul/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/TypeInfo.cpp b/libyul/optimiser/TypeInfo.cpp index 740856c72815..81bc7ac0b0ef 100644 --- a/libyul/optimiser/TypeInfo.cpp +++ b/libyul/optimiser/TypeInfo.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include diff --git a/libyul/optimiser/TypeInfo.h b/libyul/optimiser/TypeInfo.h index f458cbe443e0..5ed0a0d55c5f 100644 --- a/libyul/optimiser/TypeInfo.h +++ b/libyul/optimiser/TypeInfo.h @@ -20,7 +20,7 @@ */ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/UnusedFunctionParameterPruner.cpp b/libyul/optimiser/UnusedFunctionParameterPruner.cpp new file mode 100644 index 000000000000..75e8325a3e1d --- /dev/null +++ b/libyul/optimiser/UnusedFunctionParameterPruner.cpp @@ -0,0 +1,127 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * UnusedFunctionParameterPruner: Optimiser step that removes unused parameters from function + * definition. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::yul; +using namespace solidity::yul::unusedFunctionsCommon; + +void UnusedFunctionParameterPruner::run(OptimiserStepContext& _context, Block& _ast) +{ + map references = ReferencesCounter::countReferences(_ast); + auto used = [&](auto v) -> bool { return references.count(v.name); }; + + // Function name and a pair of boolean masks, the first corresponds to parameters and the second + // corresponds to returnVariables. + // + // For the first vector in the pair, a value `false` at index `i` indicates that the function + // argument at index `i` in `FunctionDefinition::parameters` is unused inside the function body. + // + // Similarly for the second vector in the pair, a value `false` at index `i` indicates that the + // return parameter at index `i` in `FunctionDefinition::returnVariables` is unused inside + // function body. + map, vector>> usedParametersAndReturnVariables; + + // Step 1 of UnusedFunctionParameterPruner: Find functions whose parameters (both arguments and + // return-parameters) are not used in its body. + for (auto const& statement: _ast.statements) + if (holds_alternative(statement)) + { + FunctionDefinition const& f = std::get(statement); + + if (tooSimpleToBePruned(f) || boost::algorithm::all_of(f.parameters + f.returnVariables, used)) + continue; + + usedParametersAndReturnVariables[f.name] = { + applyMap(f.parameters, used), + applyMap(f.returnVariables, used) + }; + } + + set functionNamesToFree = util::keys(usedParametersAndReturnVariables); + + // Step 2 of UnusedFunctionParameterPruner: Renames the function and replaces all references to + // the function, say `f`, by its new name, say `f_1`. + NameDisplacer replace{_context.dispenser, functionNamesToFree}; + replace(_ast); + + // Inverse-Map of the above translations. In the above example, this will store an element with + // key `f_1` and value `f`. + std::map newToOriginalNames = invertMap(replace.translations()); + + // Step 3 of UnusedFunctionParameterPruner: introduce a new function in the block with body of + // the old one. Replace the body of the old one with a function call to the new one with reduced + // parameters. + // + // For example: introduce a new 'linking' function `f` with the same the body as `f_1`, but with + // reduced parameters, i.e., `function f() -> y { y := 1 }`. Now replace the body of `f_1` with + // a call to `f`, i.e., `f_1(x) -> y { y := f() }`. + iterateReplacing(_ast.statements, [&](Statement& _s) -> optional> { + if (holds_alternative(_s)) + { + // The original function except that it has a new name (e.g., `f_1`) + FunctionDefinition& originalFunction = get(_s); + if (newToOriginalNames.count(originalFunction.name)) + { + + YulString linkingFunctionName = originalFunction.name; + YulString originalFunctionName = newToOriginalNames.at(linkingFunctionName); + pair, vector> used = + usedParametersAndReturnVariables.at(originalFunctionName); + + FunctionDefinition linkingFunction = createLinkingFunction( + originalFunction, + used, + originalFunctionName, + linkingFunctionName, + _context.dispenser + ); + + originalFunction.name = originalFunctionName; + originalFunction.parameters = + filter(originalFunction.parameters, used.first); + originalFunction.returnVariables = + filter(originalFunction.returnVariables, used.second); + + return make_vector(move(originalFunction), move(linkingFunction)); + } + } + + return nullopt; + }); +} diff --git a/libyul/optimiser/UnusedFunctionParameterPruner.h b/libyul/optimiser/UnusedFunctionParameterPruner.h new file mode 100644 index 000000000000..1f62a8b081b6 --- /dev/null +++ b/libyul/optimiser/UnusedFunctionParameterPruner.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +namespace solidity::yul +{ + +/** + * UnusedFunctionParameterPruner: Optimiser step that removes unused parameters in a function. + * + * If a parameter is unused, like `c` and `y` in, `function f(a,b,c) -> x, y { x := div(a,b) }` + * + * We remove the parameter and create a new "linking" function as follows: + * + * `function f(a,b) -> x { x := div(a,b) }` + * `function f2(a,b,c) -> x, y { x := f(a,b) }` + * + * and replace all references to `f` by `f2`. + * The inliner should be run afterwards to make sure that all references to `f2` are replaced by + * `f`. + * + * Prerequisites: Disambiguator, FunctionHoister, LiteralRematerialiser + * + * The step LiteralRematerialiser is not required for correctness. It helps deal with cases such as: + * `function f(x) -> y { revert(y, y} }` where the literal `y` will be replaced by its value `0`, + * allowing us to rewrite the function. + */ +struct UnusedFunctionParameterPruner +{ + static constexpr char const* name{"UnusedFunctionParameterPruner"}; + static void run(OptimiserStepContext& _context, Block& _ast); +}; + +} diff --git a/libyul/optimiser/UnusedFunctionsCommon.cpp b/libyul/optimiser/UnusedFunctionsCommon.cpp new file mode 100644 index 000000000000..db2ca5dee46d --- /dev/null +++ b/libyul/optimiser/UnusedFunctionsCommon.cpp @@ -0,0 +1,77 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +#include + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::yul; +using namespace solidity::yul::unusedFunctionsCommon; + +FunctionDefinition unusedFunctionsCommon::createLinkingFunction( + FunctionDefinition const& _original, + std::pair, std::vector> const& _usedParametersAndReturns, + YulString const& _originalFunctionName, + YulString const& _linkingFunctionName, + NameDispenser& _nameDispenser +) +{ + auto generateTypedName = [&](TypedName t) + { + return TypedName{ + t.location, + _nameDispenser.newName(t.name), + t.type + }; + }; + + langutil::SourceLocation loc = _original.location; + + FunctionDefinition linkingFunction{ + loc, + _linkingFunctionName, + util::applyMap(_original.parameters, generateTypedName), + util::applyMap(_original.returnVariables, generateTypedName), + {loc, {}} // body + }; + + FunctionCall call{loc, Identifier{loc, _originalFunctionName}, {}}; + for (auto const& p: filter(linkingFunction.parameters, _usedParametersAndReturns.first)) + call.arguments.emplace_back(Identifier{loc, p.name}); + + Assignment assignment{loc, {}, nullptr}; + + for (auto const& r: filter(linkingFunction.returnVariables, _usedParametersAndReturns.second)) + assignment.variableNames.emplace_back(Identifier{loc, r.name}); + + if (assignment.variableNames.empty()) + linkingFunction.body.statements.emplace_back(ExpressionStatement{loc, std::move(call)}); + else + { + assignment.value = std::make_unique(std::move(call)); + linkingFunction.body.statements.emplace_back(std::move(assignment)); + } + + return linkingFunction; +} diff --git a/libyul/optimiser/UnusedFunctionsCommon.h b/libyul/optimiser/UnusedFunctionsCommon.h new file mode 100644 index 000000000000..2b0f3000dfda --- /dev/null +++ b/libyul/optimiser/UnusedFunctionsCommon.h @@ -0,0 +1,60 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +#include + +namespace solidity::yul::unusedFunctionsCommon +{ + +/// Returns true if applying UnusedFunctionParameterPruner is not helpful or redundant because the +/// inliner will be able to handle it anyway. +inline bool tooSimpleToBePruned(FunctionDefinition const& _f) +{ + return _f.body.statements.size() <= 1 && CodeSize::codeSize(_f.body) <= 1; +} + +/// Given a function definition `_original`, this function returns a 'linking' function that calls +/// `_originalFunctionName` (with reduced parameters and return values). +/// +/// The parameter `_usedParametersAndReturnVariables` is a pair of boolean-vectors. Its `.first` +/// corresponds to function parameters and its `.second` corresponds to function return-variables. A +/// false value at index `i` means that the corresponding function parameter / return-variable at +/// index `i` is unused. +/// +/// Example: +/// +/// Let `_original` be the function `function f_1() -> y { }`. (In practice, this function usually cannot +/// be inlined and has parameters / return-variables that are unused.) +/// Let `_usedParametersAndReturnVariables` be `({}, {false})` +/// Let `_originalFunctionName` be `f`. +/// Let `_linkingFunctionName` be `f_1`. +/// +/// Then the returned linking function would be `function f_1() -> y_1 { f() }` +FunctionDefinition createLinkingFunction( + FunctionDefinition const& _original, + std::pair, std::vector> const& _usedParametersAndReturns, + YulString const& _originalFunctionName, + YulString const& _linkingFunctionName, + NameDispenser& _nameDispenser +); + +} diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index a0e0f535dd8b..b0b4b794cd80 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include @@ -93,7 +93,7 @@ void UnusedPruner::operator()(Block& _block) statement = Block{std::move(varDecl.location), {}}; else if ( SideEffectsCollector(m_dialect, *varDecl.value, m_functionSideEffects). - sideEffectFree(m_allowMSizeOptimization) + canBeRemoved(m_allowMSizeOptimization) ) { subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); @@ -112,7 +112,7 @@ void UnusedPruner::operator()(Block& _block) ExpressionStatement& exprStmt = std::get(statement); if ( SideEffectsCollector(m_dialect, exprStmt.expression, m_functionSideEffects). - sideEffectFree(m_allowMSizeOptimization) + canBeRemoved(m_allowMSizeOptimization) ) { subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index ca6c89feb7da..dc3e27c2a48d 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -17,7 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include -#include +#include #include #include diff --git a/libyul/optimiser/VarDeclInitializer.h b/libyul/optimiser/VarDeclInitializer.h index 3d2d51c34df6..4060878b15e6 100644 --- a/libyul/optimiser/VarDeclInitializer.h +++ b/libyul/optimiser/VarDeclInitializer.h @@ -18,7 +18,7 @@ #pragma once -#include +#include #include #include diff --git a/libyul/optimiser/VarNameCleaner.cpp b/libyul/optimiser/VarNameCleaner.cpp index 15d215f2cf23..55fc71fd64b5 100644 --- a/libyul/optimiser/VarNameCleaner.cpp +++ b/libyul/optimiser/VarNameCleaner.cpp @@ -17,10 +17,10 @@ // SPDX-License-Identifier: GPL-3.0 #include -#include +#include +#include #include -#include -#include + #include #include #include @@ -111,11 +111,7 @@ YulString VarNameCleaner::findCleanName(YulString const& _name) const bool VarNameCleaner::isUsedName(YulString const& _name) const { - if (_name.empty() || m_dialect.builtin(_name) || m_usedNames.count(_name)) - return true; - if (dynamic_cast(&m_dialect)) - return Parser::instructions().count(_name.str()); - return false; + return isRestrictedIdentifier(m_dialect, _name) || m_usedNames.count(_name); } YulString VarNameCleaner::stripSuffix(YulString const& _name) const diff --git a/libyul/optimiser/VarNameCleaner.h b/libyul/optimiser/VarNameCleaner.h index 2c26cc7a67bf..7823e5716c28 100644 --- a/libyul/optimiser/VarNameCleaner.h +++ b/libyul/optimiser/VarNameCleaner.h @@ -19,7 +19,7 @@ #pragma once -#include +#include #include #include #include diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 6f8d8b06e1ca..350b8251c896 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -1,10 +1,15 @@ #!/usr/bin/env bash +set -e + # Bash script to test the ast-import option of the compiler by # first exporting a .sol file to JSON, then loading it into the compiler # and exporting it again. The second JSON should be identical to the first - -REPO_ROOT=$(readlink -f "$(dirname "$0")"/..) +READLINK=readlink +if [[ "$OSTYPE" == "darwin"* ]]; then + READLINK=greadlink +fi +REPO_ROOT=$(${READLINK} -f "$(dirname "$0")"/..) SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build} SOLC=${SOLIDITY_BUILD_DIR}/solc/solc SPLITSOURCES=${REPO_ROOT}/scripts/splitSources.py @@ -83,19 +88,39 @@ do FILETMP=$(mktemp -d) cd $FILETMP + set +e OUTPUT=$($SPLITSOURCES $solfile) - if [ $? != 1 ] + SPLITSOURCES_RC=$? + set -e + if [ ${SPLITSOURCES_RC} == 0 ] then # echo $OUTPUT NSOURCES=$((NSOURCES - 1)) for i in $OUTPUT; do - testImportExportEquivalence $i $OUTPUT + testImportExportEquivalence "$i" "$OUTPUT" NSOURCES=$((NSOURCES + 1)) done - - else + elif [ ${SPLITSOURCES_RC} == 1 ] + then testImportExportEquivalence $solfile + elif [ ${SPLITSOURCES_RC} == 2 ] + then + # The script will exit with return code 2, if an UnicodeDecodeError occurred. + # This is the case if e.g. some tests are using invalid utf-8 sequences. We will ignore + # these errors, but print the actual output of the script. + echo -e "\n${OUTPUT}\n" + testImportExportEquivalence $solfile + else + # All other return codes will be treated as critical errors. The script will exit. + echo -e "\nGot unexpected return code ${SPLITSOURCES_RC} from ${SPLITSOURCES}. Aborting." + echo -e "\n${OUTPUT}\n" + + cd $WORKINGDIR + # Delete temporary files + rm -rf $FILETMP + + exit 1 fi cd $WORKINGDIR diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index f4b0fe647a7c..8f8208dd0be2 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -35,5 +35,5 @@ else fi docker run -v $(pwd):/root/project -w /root/project \ - solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc \ - ./scripts/travis-emscripten/build_emscripten.sh $BUILD_DIR + solbuildpackpusher/solidity-buildpack-deps@sha256:23dad3b34deae8107c8551804ef299f6a89c23ed506e8118fac151e2bdc9018c\ + ./scripts/ci/build_emscripten.sh $BUILD_DIR diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index cd5ee7491932..2697b940bed9 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -8,13 +8,10 @@ SOLC_BIN = sys.argv[1] REPORT_FILE = open("report.txt", mode="w", encoding='utf8', newline='\n') -def removeSMT(source): - return source.replace('pragma experimental SMTChecker;', '') - for optimize in [False, True]: for f in sorted(glob.glob("*.sol")): sources = {} - sources[f] = {'content': removeSMT(open(f, mode='r', encoding='utf8').read())} + sources[f] = {'content': open(f, mode='r', encoding='utf8').read()} input_json = { 'language': 'Solidity', 'sources': sources, @@ -22,7 +19,8 @@ def removeSMT(source): 'optimizer': { 'enabled': optimize }, - 'outputSelection': {'*': {'*': ['evm.bytecode.object', 'metadata']}} + 'outputSelection': {'*': {'*': ['evm.bytecode.object', 'metadata']}}, + 'modelChecker': { "engine": 'none' } } } args = [SOLC_BIN, '--standard-json'] diff --git a/scripts/bytecodecompare/storebytecode.bat b/scripts/bytecodecompare/storebytecode.bat deleted file mode 100644 index 5aad801c4280..000000000000 --- a/scripts/bytecodecompare/storebytecode.bat +++ /dev/null @@ -1,43 +0,0 @@ -@ECHO OFF - -REM --------------------------------------------------------------------------- -REM This file is part of solidity. -REM -REM solidity is free software: you can redistribute it and/or modify -REM it under the terms of the GNU General Public License as published by -REM the Free Software Foundation, either version 3 of the License, or -REM (at your option) any later version. -REM -REM solidity is distributed in the hope that it will be useful, -REM but WITHOUT ANY WARRANTY; without even the implied warranty of -REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -REM GNU General Public License for more details. -REM -REM You should have received a copy of the GNU General Public License -REM along with solidity. If not, see -REM -REM Copyright (c) 2017 solidity contributors. -REM --------------------------------------------------------------------------- - -set CONFIGURATION=%1 -set DIRECTORY=%2 - -mkdir bytecode -cd bytecode -..\scripts\isolate_tests.py ..\test\ -..\scripts\bytecodecompare\prepare_report.py ..\build\solc\%CONFIGURATION%\solc.exe - -REM Send to stdout instead of stderr to not confuse powershell -git clone --depth 2 git@github.com:ethereum/solidity-test-bytecode.git 2>&1 -cd solidity-test-bytecode -git config user.name "travis" -git config user.email "chris@ethereum.org" -git clean -f -d -x 2>&1 - -if not exist %DIRECTORY% mkdir %DIRECTORY% -set REPORT=%DIRECTORY%/windows.txt -cp ../report.txt %REPORT% -git add %REPORT% 2>$1 -git commit -a -m "Added report." -git pull --rebase 2>&1 -git push origin 2>&1 diff --git a/scripts/bytecodecompare/storebytecode.sh b/scripts/bytecodecompare/storebytecode.sh index 02530db03749..bddc6aa56590 100755 --- a/scripts/bytecodecompare/storebytecode.sh +++ b/scripts/bytecodecompare/storebytecode.sh @@ -27,26 +27,15 @@ set -e -if [[ "${TRAVIS_PULL_REQUEST_BRANCH}" != "" ]]; then - # Variable is set to the branch's name iff current job is a pull request, - # or is set to empty string if it is a push build. - echo "Skipping bytecode comparison." - exit 0 -fi - REPO_ROOT="$(dirname "$0")"/../.. +cd "$REPO_ROOT" +REPO_ROOT=$(pwd) # make it absolute -if test -z "$1"; then - BUILD_DIR="build" -else - BUILD_DIR="$1" -fi +BUILD_DIR="${1:-${REPO_ROOT}/build}" echo "Compiling all test contracts into bytecode..." TMPDIR=$(mktemp -d) ( - cd "$REPO_ROOT" - REPO_ROOT=$(pwd) # make it absolute cd "$TMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ @@ -64,11 +53,6 @@ var fs = require('fs') var compiler = require('./solc-js/wrapper.js')(require('./solc-js/soljson.js')) -function removeSMT(source) -{ - return source.replace('pragma experimental SMTChecker;', ''); -} - for (var optimize of [false, true]) { for (var filename of process.argv.slice(2)) @@ -76,13 +60,14 @@ for (var optimize of [false, true]) if (filename !== undefined) { var inputs = {} - inputs[filename] = { content: removeSMT(fs.readFileSync(filename).toString()) } + inputs[filename] = { content: fs.readFileSync(filename).toString() } var input = { language: 'Solidity', sources: inputs, settings: { optimizer: { enabled: optimize }, - outputSelection: { '*': { '*': ['evm.bytecode.object', 'metadata'] } } + outputSelection: { '*': { '*': ['evm.bytecode.object', 'metadata'] } }, + "modelChecker": { "engine": "none" } } } var result = JSON.parse(compiler.compile(JSON.stringify(input))) @@ -117,37 +102,10 @@ EOF ./solc *.sol > report.txt echo "Finished running the compiler." else - $REPO_ROOT/scripts/bytecodecompare/prepare_report.py $REPO_ROOT/$BUILD_DIR/solc/solc + "$REPO_ROOT/scripts/bytecodecompare/prepare_report.py" "$BUILD_DIR/solc/solc" fi - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ] - then - openssl aes-256-cbc -K $encrypted_60701c962b9c_key -iv $encrypted_60701c962b9c_iv -in "$REPO_ROOT"/scripts/bytecodecompare/deploy_key.enc -out deploy_key -d - chmod 600 deploy_key - eval `ssh-agent -s` - ssh-add deploy_key - - git clone --depth 2 git@github.com:ethereum/solidity-test-bytecode.git - cd solidity-test-bytecode - git config user.name "travis" - git config user.email "chris@ethereum.org" - git clean -f -d -x - - DIRNAME=$(cd "$REPO_ROOT" && git show -s --format="%cd-%H" --date="format:%Y-%m-%d-%H-%M") - mkdir -p "$DIRNAME" - REPORT="$DIRNAME/$ZIP_SUFFIX.txt" - cp ../report.txt "$REPORT" - # Only push if adding actually worked, i.e. there were changes. - if git add "$REPORT" && git commit -a -m "Added report $REPORT" - then - git pull --rebase - git push origin - else - echo "Adding report failed, it might already exist in the repository." - fi - else - echo "Not storing bytecode because the keys are not available." - fi + cp report.txt "$REPO_ROOT" ) rm -rf "$TMPDIR" -echo "Storebytecode finished." \ No newline at end of file +echo "Storebytecode finished." diff --git a/scripts/check_style.sh b/scripts/check_style.sh index f8f6e4a0e7a0..7d73b8470e39 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -6,7 +6,10 @@ REPO_ROOT="$(dirname "$0")"/.. cd $REPO_ROOT -WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" | grep -v "test/libsolidity/ASTJSON\|test/libsolidity/ASTRecoveryTests\|test/compilationTests/zeppelin/LICENSE") +WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" | + grep -v "test/libsolidity/ASTJSON\|test/libsolidity/ASTRecoveryTests\|test/compilationTests/zeppelin/LICENSE" | + grep -v -E "test/libsolidity/syntaxTests/comments/unicode_direction_override_1.sol" +) if [[ "$WHITESPACE" != "" ]] then diff --git a/scripts/check_symlinks.sh b/scripts/check_symlinks.sh new file mode 100755 index 000000000000..a02200cea04d --- /dev/null +++ b/scripts/check_symlinks.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e + +REPO_ROOT="$(dirname "$0")"/.. +REPO_ROOT=$(realpath "${REPO_ROOT}") + +BROKEN_LINKS=$(find -L "${REPO_ROOT}" -type l -ls) +if [ -z "${BROKEN_LINKS}" ] +then + exit 0 +else + echo "broken symbolic link(s) found:" + echo "${BROKEN_LINKS}" + + exit 1 +fi diff --git a/scripts/chk_shellscripts/ignore.txt b/scripts/chk_shellscripts/ignore.txt index 0986077641a8..188efb0ab5ff 100644 --- a/scripts/chk_shellscripts/ignore.txt +++ b/scripts/chk_shellscripts/ignore.txt @@ -1,12 +1,5 @@ ./test/docsCodeStyle.sh ./test/cmdlineTests.sh -./test/externalTests.sh -./test/externalTests/common.sh -./test/externalTests/gnosis.sh -./test/externalTests/zeppelin.sh -./test/externalTests/colony.sh -./test/externalTests/solc-js/solc-js.sh -./scripts/common.sh ./scripts/isoltest.sh ./scripts/get_version.sh ./scripts/soltest.sh @@ -17,9 +10,7 @@ ./scripts/wasm-rebuild/docker-scripts/patch.sh ./scripts/wasm-rebuild/rebuild.sh ./scripts/build_emscripten.sh -./scripts/travis-emscripten/build_emscripten.sh -./scripts/travis-emscripten/install_deps.sh -./scripts/travis-emscripten/publish_binary.sh +./scripts/ci/build_emscripten.sh ./scripts/docker_build.sh ./scripts/docs_version_pragma_check.sh ./scripts/uniqueErrors.sh diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index a939412f6dc5..b1f2f2cf3343 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -5,7 +5,7 @@ ROOTDIR="$(dirname "$0")/../.." cd "${ROOTDIR}" # shellcheck disable=SC2166 -if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" -o -n "$FORCE_RELEASE" ]; then echo -n >prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" >prerelease.txt; fi +if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" -o -n "$FORCE_RELEASE" ]; then echo -n >prerelease.txt; fi if [ -n "$CIRCLE_SHA1" ] then echo -n "$CIRCLE_SHA1" >commit_hash.txt diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/ci/build_emscripten.sh similarity index 87% rename from scripts/travis-emscripten/build_emscripten.sh rename to scripts/ci/build_emscripten.sh index 3162e13b661a..0ebbe9e226f6 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -11,7 +11,7 @@ # # The documentation for solidity is hosted at: # -# http://solidity.readthedocs.io/ +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -42,8 +42,18 @@ fi WORKSPACE=/root/project -echo -en 'travis_fold:start:compiling_solidity\\r' cd $WORKSPACE + +# shellcheck disable=SC2166 +if [[ "$CIRCLE_BRANCH" = release || -n "$CIRCLE_TAG" || -n "$FORCE_RELEASE" || "$(git tag --points-at HEAD 2>/dev/null)" == v* ]] +then + echo -n >prerelease.txt +fi +if [ -n "$CIRCLE_SHA1" ] +then + echo -n "$CIRCLE_SHA1" >commit_hash.txt +fi + mkdir -p $BUILD_DIR cd $BUILD_DIR cmake \ @@ -67,5 +77,3 @@ cp $BUILD_DIR/libsolc/soljson.js ./ OUTPUT_SIZE=`ls -la soljson.js` echo "Emscripten output size: $OUTPUT_SIZE" - -echo -en 'travis_fold:end:compiling_solidity\\r' diff --git a/scripts/ci/build_ossfuzz.sh b/scripts/ci/build_ossfuzz.sh index 42f7b3a3959c..f208c9b7fe3d 100755 --- a/scripts/ci/build_ossfuzz.sh +++ b/scripts/ci/build_ossfuzz.sh @@ -1,15 +1,27 @@ #!/usr/bin/env bash -set -e +set -ex -ROOTDIR="$(dirname "$0")/../.." +ROOTDIR="/root/project" BUILDDIR="${ROOTDIR}/build" +mkdir -p "${BUILDDIR}" && mkdir -p "$BUILDDIR/deps" -mkdir -p "${BUILDDIR}" -cd "${BUILDDIR}" +generate_protobuf_bindings() +{ + cd "${ROOTDIR}"/test/tools/ossfuzz + # Generate protobuf C++ bindings + for protoName in yul abiV2 sol; + do + protoc "${protoName}"Proto.proto --cpp_out . + done +} -protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz -protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz -protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz -cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/libfuzzer.cmake +build_fuzzers() +{ + cd "${BUILDDIR}" + cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \ + -DCMAKE_TOOLCHAIN_FILE="${ROOTDIR}"/cmake/toolchains/libfuzzer.cmake + make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 +} -make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 +generate_protobuf_bindings +build_fuzzers diff --git a/scripts/ci/buildpack-deps_test_emscripten.sh b/scripts/ci/buildpack-deps_test_emscripten.sh index 6e838fba7fb3..4e901d9ccb70 120000 --- a/scripts/ci/buildpack-deps_test_emscripten.sh +++ b/scripts/ci/buildpack-deps_test_emscripten.sh @@ -1 +1 @@ -../../scripts/travis-emscripten/build_emscripten.sh \ No newline at end of file +build_emscripten.sh \ No newline at end of file diff --git a/scripts/ci/docker_upgrade.sh b/scripts/ci/docker_upgrade.sh index 16d6222305bf..29e80354a192 100755 --- a/scripts/ci/docker_upgrade.sh +++ b/scripts/ci/docker_upgrade.sh @@ -61,5 +61,5 @@ docker push "${DOCKER_IMAGE_ID}-${VERSION}" REPO_DIGEST=$(docker inspect --format='{{.RepoDigests}}' "${DOCKER_IMAGE_ID}-${VERSION}") -echo "::set-env name=DOCKER_IMAGE::${DOCKER_IMAGE_ID}-${VERSION}" -echo "::set-env name=DOCKER_REPO_DIGEST::${REPO_DIGEST}" +echo "DOCKER_IMAGE=${DOCKER_IMAGE_ID}-${VERSION}" >> "$GITHUB_ENV" +echo "DOCKER_REPO_DIGEST=${REPO_DIGEST}" >> "$GITHUB_ENV" diff --git a/scripts/codespell_whitelist.txt b/scripts/codespell_whitelist.txt index f1111719f138..ad3c5d2100d5 100644 --- a/scripts/codespell_whitelist.txt +++ b/scripts/codespell_whitelist.txt @@ -12,3 +12,4 @@ errorstring hist otion keypair +ether diff --git a/scripts/common.sh b/scripts/common.sh index fbb730241357..1cca52f4ef3c 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # ------------------------------------------------------------------------------ # vim:ts=4:et # This file is part of solidity. @@ -22,11 +23,11 @@ if [ "$CIRCLECI" ] then export TERM="${TERM:-xterm}" function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput setaf 7)"; } - function printError() { echo "$(tput setaf 1)$1$(tput setaf 7)"; } + function printError() { >&2 echo "$(tput setaf 1)$1$(tput setaf 7)"; } function printLog() { echo "$(tput setaf 3)$1$(tput setaf 7)"; } else function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput sgr0)"; } - function printError() { echo "$(tput setaf 1)$1$(tput sgr0)"; } + function printError() { >&2 echo "$(tput setaf 1)$1$(tput sgr0)"; } function printLog() { echo "$(tput setaf 3)$1$(tput sgr0)"; } fi @@ -37,21 +38,21 @@ safe_kill() local n=1 # only proceed if $PID does exist - kill -0 $PID 2>/dev/null || return + kill -0 "$PID" 2>/dev/null || return echo "Sending SIGTERM to ${NAME} (${PID}) ..." - kill $PID + kill "$PID" # wait until process terminated gracefully - while kill -0 $PID 2>/dev/null && [[ $n -le 4 ]]; do + while kill -0 "$PID" 2>/dev/null && [[ $n -le 4 ]]; do echo "Waiting ($n) ..." sleep 1 - n=$[n + 1] + n=$((n + 1)) done # process still alive? then hard-kill - if kill -0 $PID 2>/dev/null; then + if kill -0 "$PID" 2>/dev/null; then echo "Sending SIGKILL to ${NAME} (${PID}) ..." - kill -9 $PID + kill -9 "$PID" fi } diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh index 1d21fe75c415..1467ab8f26f4 100644 --- a/scripts/common_cmdline.sh +++ b/scripts/common_cmdline.sh @@ -49,7 +49,6 @@ function compileFull() fi local files="$*" - local output local stderr_path=$(mktemp) @@ -71,7 +70,7 @@ function compileFull() printError "Was failure: $exit_code" echo "$errors" printError "While calling:" - echo "\"$SOLC\" $ARGS $files" + echo "\"$SOLC\" $args $files" printError "Inside directory:" pwd false diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index c5f6d6ea4438..ca0795815de2 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -25,9 +25,9 @@ set -ev keyid=70D110489D66E2F6 email=builds@ethereum.org packagename=libz3-static-dev -version=4.8.8 +version=4.8.9 -DISTRIBUTIONS="bionic eoan focal" +DISTRIBUTIONS="focal groovy" for distribution in $DISTRIBUTIONS do @@ -80,7 +80,9 @@ Vcs-Browser: https://github.com/Z3Prover/z3 Package: libz3-static-dev Section: libdevel -Architecture: any-i386 any-amd64 +Architecture: any-amd64 +Breaks: libz3-dev +Replaces: libz3-dev Multi-Arch: same Depends: \${shlibs:Depends}, \${misc:Depends} Description: theorem prover from Microsoft Research - development files (static library) diff --git a/scripts/docker/buildpack-deps/Dockerfile.emscripten b/scripts/docker/buildpack-deps/Dockerfile.emscripten index 2d9ce401ddc1..65dcd1fc3566 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.emscripten +++ b/scripts/docker/buildpack-deps/Dockerfile.emscripten @@ -29,12 +29,12 @@ # make version=1.39.15 build # FROM emscripten/emsdk:1.39.15 AS base -LABEL version="1" +LABEL version="2" ADD emscripten.jam /usr/src RUN set -ex; \ cd /usr/src; \ - git clone https://github.com/Z3Prover/z3.git -b z3-4.8.8 --depth 1 ; \ + git clone https://github.com/Z3Prover/z3.git -b z3-4.8.9 --depth 1 ; \ cd z3; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index 32f84573cf39..2b1bef899028 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="2" +LABEL version="7" ARG DEBIAN_FRONTEND=noninteractive @@ -31,7 +31,8 @@ RUN apt-get update; \ build-essential \ software-properties-common \ ninja-build git wget \ - libbz2-dev zlib1g-dev git curl; \ + libbz2-dev zlib1g-dev git curl uuid-dev \ + pkg-config openjdk-8-jdk liblzma-dev unzip mlton m4; \ apt-get install -qy python-pip python-sphinx; # Install cmake 3.14 (minimum requirement is cmake 3.10) @@ -42,20 +43,24 @@ RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5 FROM base AS libraries # Boost -RUN git clone -b boost-1.69.0 https://github.com/boostorg/boost.git \ - /usr/src/boost; \ - cd /usr/src/boost; \ - git submodule update --init --recursive; \ - ./bootstrap.sh --with-toolset=clang --prefix=/usr; \ - ./b2 toolset=clang cxxflags="-stdlib=libc++" linkflags="-stdlib=libc++" headers; \ - ./b2 toolset=clang cxxflags="-stdlib=libc++" linkflags="-stdlib=libc++" \ +RUN set -ex; \ + cd /usr/src; \ + wget -q 'https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2' -O boost.tar.bz2; \ + test "$(sha256sum boost.tar.bz2)" = "4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402 boost.tar.bz2"; \ + tar -xf boost.tar.bz2; \ + rm boost.tar.bz2; \ + cd boost_1_73_0; \ + CXXFLAGS="-stdlib=libc++ -pthread" LDFLAGS="-stdlib=libc++" ./bootstrap.sh --with-toolset=clang --prefix=/usr; \ + ./b2 toolset=clang cxxflags="-stdlib=libc++ -pthread" linkflags="-stdlib=libc++ -pthread" headers; \ + ./b2 toolset=clang cxxflags="-stdlib=libc++ -pthread" linkflags="-stdlib=libc++ -pthread" \ link=static variant=release runtime-link=static \ system filesystem unit_test_framework program_options \ install -j $(($(nproc)/2)); \ - rm -rf /usr/src/boost + rm -rf /usr/src/boost_1_73_0 # Z3 -RUN git clone --depth 1 -b z3-4.8.7 https://github.com/Z3Prover/z3.git \ +RUN set -ex; \ + git clone --depth 1 -b z3-4.8.9 https://github.com/Z3Prover/z3.git \ /usr/src/z3; \ cd /usr/src/z3; \ mkdir build; \ @@ -99,7 +104,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.0" --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.3.2" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ @@ -108,8 +113,37 @@ RUN set -ex; \ ninja install/strip; \ rm -rf /usr/src/hera +# gmp +RUN set -ex; \ + # Replace system installed libgmp static library + # with sanitized version. Do not perform apt + # remove because it removes mlton as well that + # we need for building libabicoder + rm -f /usr/lib/x86_64-linux-gnu/libgmp.*; \ + rm -f /usr/include/x86_64-linux-gnu/gmp.h; \ + cd /usr/src; \ + wget -q 'https://gmplib.org/download/gmp/gmp-6.2.1.tar.xz' -O gmp.tar.xz; \ + test "$(sha256sum gmp.tar.xz)" = "fd4829912cddd12f84181c3451cc752be224643e87fac497b69edddadc49b4f2 gmp.tar.xz"; \ + tar -xf gmp.tar.xz; \ + cd gmp-6.2.1; \ + ./configure --prefix=/usr --enable-static=yes; \ + make -j; \ + make install; \ + rm -rf /usr/src/gmp-6.2.1; \ + rm -f /usr/src/gmp.tar.xz + +# libabicoder +RUN set -ex; \ + cd /usr/src; \ + git clone https://github.com/ekpyron/Yul-Isabelle; \ + cd Yul-Isabelle; \ + cd libabicoder; \ + CXX=clang++ CXXFLAGS="-stdlib=libc++ -pthread" make; \ + cp libabicoder.a /usr/lib; \ + cp abicoder.hpp /usr/include; \ + rm -rf /usr/src/Yul-Isabelle + FROM base COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include - +COPY --from=libraries /usr/include /usr/include \ No newline at end of file diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1804 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1804 deleted file mode 100644 index b8ce7c50646d..000000000000 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1804 +++ /dev/null @@ -1,108 +0,0 @@ -# vim:syntax=dockerfile -#------------------------------------------------------------------------------ -# Dockerfile for building and testing Solidity Compiler on CI -# Target: Ubuntu 18.04 (Bionic Beaver) -# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps -# -# This file is part of solidity. -# -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2016-2019 solidity contributors. -#------------------------------------------------------------------------------ -FROM buildpack-deps:bionic AS base -LABEL version="2" - -ARG DEBIAN_FRONTEND=noninteractive - -RUN set -ex; \ - dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \ - echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \ - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \ - apt-get update; \ - apt-get install -qqy --no-install-recommends \ - build-essential \ - software-properties-common \ - cmake ninja-build clang++-8 \ - libboost-filesystem-dev libboost-test-dev libboost-system-dev \ - libboost-program-options-dev \ - libjsoncpp-dev \ - llvm-8-dev libz3-static-dev \ - ; \ - apt-get install -qy python-pip python-sphinx; \ - update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \ - pip install codecov; \ - rm -rf /var/lib/apt/lists/* - -FROM base AS libraries - -# OSSFUZZ: libprotobuf-mutator -RUN set -ex; \ - git clone https://github.com/google/libprotobuf-mutator.git \ - /usr/src/libprotobuf-mutator; \ - cd /usr/src/libprotobuf-mutator; \ - git checkout d1fe8a7d8ae18f3d454f055eba5213c291986f21; \ - mkdir build; \ - cd build; \ - cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \ - -DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX="/usr"; \ - ninja; \ - cp -vpr external.protobuf/bin/* /usr/bin/; \ - cp -vpr external.protobuf/include/* /usr/include/; \ - cp -vpr external.protobuf/lib/* /usr/lib/; \ - ninja install/strip; \ - rm -rf /usr/src/libprotobuf-mutator - -# OSSFUZZ: libfuzzer -RUN set -ex; \ - cd /var/tmp; \ - svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer libfuzzer; \ - mkdir -p build-libfuzzer; \ - cd build-libfuzzer; \ - clang++-8 -O1 -stdlib=libstdc++ -std=c++11 -O2 -fPIC -c ../libfuzzer/*.cpp -I../libfuzzer; \ - ar r /usr/lib/libFuzzingEngine.a *.o; \ - rm -rf /var/lib/libfuzzer - -# EVMONE -ARG EVMONE_HASH="e9f8df89c52d9c60c9a38dd00687b1ec9e9ae9650b400a87c4c0cf7468e35307" -ARG EVMONE_MAJOR="0" -ARG EVMONE_MINOR="4" -ARG EVMONE_MICRO="0" -RUN set -ex; \ - EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \ - TGZFILE="evmone-$EVMONE_VERSION-linux-x86_64.tar.gz"; \ - wget https://github.com/ethereum/evmone/releases/download/v$EVMONE_VERSION/$TGZFILE; \ - sha256sum $TGZFILE; \ - tar xzpf $TGZFILE -C /usr; \ - rm -f $TGZFILE; - -# HERA -ARG HERA_HASH="622663cb3bc1fa07213c6e1fe8070c5c259096e75039a46d014b2a3448b5cf44" -ARG HERA_MAJOR="0" -ARG HERA_MINOR="3" -ARG HERA_MICRO="0" -RUN set -ex; \ - HERA_VERSION="$HERA_MAJOR.$HERA_MINOR.$HERA_MICRO"; \ - TGZFILE="hera-$HERA_VERSION-linux-x86_64.tar.gz"; \ - wget https://github.com/ewasm/hera/releases/download/v$HERA_VERSION/$TGZFILE; \ - sha256sum $TGZFILE; \ - tar xzpf $TGZFILE -C /usr; \ - rm -f $TGZFILE; - -FROM base -COPY --from=libraries /usr/lib /usr/lib -COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include - diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 index 78a14651d220..505f4b58568d 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="2" +LABEL version="4" ARG DEBIAN_FRONTEND=noninteractive @@ -60,7 +60,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.0" --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.3.2" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang index 05429eb3902c..4968c148c189 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="2" +LABEL version="4" ARG DEBIAN_FRONTEND=noninteractive @@ -62,7 +62,7 @@ RUN set -ex; \ # HERA RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.3.0" --recurse-submodules https://github.com/ewasm/hera.git; \ + git clone --branch="v0.3.2" --depth 1 --recurse-submodules https://github.com/ewasm/hera.git; \ cd hera; \ mkdir build; \ cd build; \ diff --git a/scripts/docker_build.sh b/scripts/docker_build.sh deleted file mode 100755 index 9eedec344653..000000000000 --- a/scripts/docker_build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env sh - -set -e - -# Scratch image -docker build -t ethereum/solc:build -f scripts/Dockerfile . -tmp_container=$(docker create ethereum/solc:build sh) -mkdir -p upload -docker cp ${tmp_container}:/usr/bin/solc upload/solc-static-linux - -# Alpine image -docker build -t ethereum/solc:build-alpine -f scripts/Dockerfile_alpine . diff --git a/scripts/docker_deploy.sh b/scripts/docker_deploy.sh deleted file mode 100755 index 4672accce8e3..000000000000 --- a/scripts/docker_deploy.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env sh - -set -e - -image="ethereum/solc" - -tag_and_push() -{ - docker tag "$image:$1" "$image:$2" - docker push "$image:$2" -} - -echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin -version=$($(dirname "$0")/get_version.sh) -if [ "$TRAVIS_BRANCH" = "develop" ] -then - tag_and_push build nightly - tag_and_push build nightly-"$version"-"$TRAVIS_COMMIT" - tag_and_push build-alpine nightly-alpine - tag_and_push build-alpine nightly-alpine-"$version"-"$TRAVIS_COMMIT" -elif [ "$TRAVIS_TAG" = v"$version" ] -then - tag_and_push build stable - tag_and_push build "$version" - tag_and_push build-alpine stable-alpine - tag_and_push build-alpine "$version"-alpine -else - echo "Not publishing docker image from branch $TRAVIS_BRANCH or tag $TRAVIS_TAG" -fi diff --git a/scripts/docs.sh b/scripts/docs.sh index 91f619d89ff7..4d31277daab5 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh index 520be3e5e53c..ecb47377b6ba 100755 --- a/scripts/docs_version_pragma_check.sh +++ b/scripts/docs_version_pragma_check.sh @@ -65,7 +65,7 @@ function getAllAvailableVersions() { allVersions=() local allListedVersions=( $( - wget -q -O- https://ethereum.github.io/solc-bin/bin/list.txt | + wget -q -O- https://binaries.soliditylang.org/bin/list.txt | grep -Po '(?<=soljson-v)\d+.\d+.\d+(?=\+commit)' | sort -V ) ) @@ -108,7 +108,7 @@ function findMinimalVersion() do if versionGreater "$ver" "$pragmaVersion" then - minVersion="$ver" + version="$ver" break elif ([ $greater == false ]) && versionEqual "$ver" "$pragmaVersion" then @@ -117,9 +117,15 @@ function findMinimalVersion() fi done - if [ -z version ] + if [ -z "$version" ] then - printError "No release $sign$pragmaVersion was listed in available releases!" + if [[ "$greater" = true && "$pragmaVersion" =~ 99 ]] + then + printError "Skipping version check for pragma: $pragmaVersion" + else + printError "No release $sign$pragmaVersion was listed in available releases!" + exit 1 + fi fi } diff --git a/scripts/error_codes.py b/scripts/error_codes.py index eaf401cb0749..bf4079b323ff 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -12,8 +12,10 @@ def read_file(file_name): content = None + _, tail = path.split(file_name) + is_latin = tail == "invalid_utf8_sequence.sol" try: - with open(file_name, "r", encoding=ENCODING) as f: + with open(file_name, "r", encoding="latin-1" if is_latin else ENCODING) as f: content = f.read() finally: if content == None: @@ -167,16 +169,17 @@ def print_ids_per_file(ids, id_to_file_names, top_dir): print() -def examine_id_coverage(top_dir, source_id_to_file_names): +def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): test_sub_dirs = [ path.join("test", "libsolidity", "errorRecoveryTests"), path.join("test", "libsolidity", "smtCheckerTests"), - path.join("test", "libsolidity", "syntaxTests") + path.join("test", "libsolidity", "syntaxTests"), + path.join("test", "libyul", "yulSyntaxTests") ] test_file_names = find_files( top_dir, test_sub_dirs, - [".sol"] + [".sol", ".yul"] ) source_ids = source_id_to_file_names.keys() test_ids = find_ids_in_test_files(test_file_names) @@ -185,6 +188,7 @@ def examine_id_coverage(top_dir, source_id_to_file_names): # Warning (1878): SPDX license identifier not provided in source file. .... # Warning (3420): Source file does not specify required compiler version! test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "error_codes", "err")) + test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "yul_unimplemented", "err")) # white list of ids which are not covered by tests white_ids = { @@ -196,24 +200,49 @@ def examine_id_coverage(top_dir, source_id_to_file_names): assert len(test_ids & white_ids) == 0, "The sets are not supposed to intersect" test_ids |= white_ids - print(f"IDs in source files: {len(source_ids)}") - print(f"IDs in test files : {len(test_ids)} ({len(test_ids) - len(source_ids)})") - print() - test_only_ids = test_ids - source_ids - if len(test_only_ids) != 0: - print("Error. The following error codes found in tests, but not in sources:") - print_ids(test_only_ids) - return 1 - source_only_ids = source_ids - test_ids - if len(source_only_ids) != 0: - print("The following error codes found in sources, but not in tests:") - print_ids_per_file(source_only_ids, source_id_to_file_names, top_dir) - print("\n\nPlease make sure to add appropriate tests.") - return 1 - return 0 + if not new_ids_only: + print(f"IDs in source files: {len(source_ids)}") + print(f"IDs in test files : {len(test_ids)} ({len(test_ids) - len(source_ids)})") + print() + + if len(test_only_ids) != 0: + print("Error. The following error codes found in tests, but not in sources:") + print_ids(test_only_ids) + return False + + if len(source_only_ids) != 0: + print("The following error codes found in sources, but not in tests:") + print_ids_per_file(source_only_ids, source_id_to_file_names, top_dir) + print("\n\nPlease make sure to add appropriate tests.") + return False + + old_source_only_ids = { + "1123", "1220", "1584", "1823", + "1988", "2066", "2657", "2800", "3356", + "3893", "3996", "4010", "4802", + "5073", "5272", "5622", "7128", + "7589", "7593", "7653", "8065", "8084", "8140", + "8312", "8592", "9085", "9390" + } + + new_source_only_ids = source_only_ids - old_source_only_ids + if len(new_source_only_ids) != 0: + print("The following new error code(s), not covered by tests, found:") + print_ids(new_source_only_ids) + print( + "\nYou can:\n" + "- create appropriate test(s);\n" + "- add the error code(s) to old_source_only_ids in error_codes.py\n" + " (to silence the checking script, with a promise to add a test later);\n" + "- add the error code(s) to white_ids in error_codes.py\n" + " (for rare cases when the error is not supposed to be tested)" + ) + return False + + return True def main(argv): @@ -267,9 +296,11 @@ def main(argv): if not ok: print("Incorrect IDs have to be fixed before applying --examine-coverage") exit(1) - res = examine_id_coverage(cwd, source_id_to_file_names) + res = 0 if examine_id_coverage(cwd, source_id_to_file_names) else 1 exit(res) + ok &= examine_id_coverage(cwd, source_id_to_file_names, new_ids_only=True) + random.seed() if next_id: diff --git a/scripts/get_nightly_version.sh b/scripts/get_nightly_version.sh index bbac846381c3..48e953966d08 100755 --- a/scripts/get_nightly_version.sh +++ b/scripts/get_nightly_version.sh @@ -6,7 +6,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/get_version.sh b/scripts/get_version.sh index 180ff3cd6691..a47cda619c49 100755 --- a/scripts/get_version.sh +++ b/scripts/get_version.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -28,4 +28,4 @@ set -e -grep -oP "PROJECT_VERSION \"?\K[0-9.]+(?=\")"? $(dirname "$0")/../CMakeLists.txt +grep -oP "PROJECT_VERSION \"?\K[0-9.]+(?=\")?" $(dirname "$0")/../CMakeLists.txt diff --git a/scripts/install_cmake.sh b/scripts/install_cmake.sh index 134b86f0995e..64647645e515 100755 --- a/scripts/install_cmake.sh +++ b/scripts/install_cmake.sh @@ -27,7 +27,6 @@ if test -f $BIN/cmake && ($BIN/cmake --version | grep -q "$VERSION"); then else FILE=cmake-$VERSION-$OS-x86_64.tar.gz URL=https://cmake.org/files/v$VERSION_MAJOR.$VERSION_MINOR/$FILE - ERROR=0 TMPFILE=$(mktemp --tmpdir cmake-$VERSION-$OS-x86_64.XXXXXXXX.tar.gz) echo "Downloading CMake ($URL)..." wget "$URL" -O "$TMPFILE" -nv diff --git a/scripts/install_deps.bat b/scripts/install_deps.bat index d02005ccd0f8..61f7022a5e14 100644 --- a/scripts/install_deps.bat +++ b/scripts/install_deps.bat @@ -37,7 +37,7 @@ REM for those packages. REM REM The documentation for solidity is hosted at: REM -REM http://solidity.readthedocs.org +REM https://docs.soliditylang.org REM REM --------------------------------------------------------------------------- REM This file is part of solidity. diff --git a/scripts/install_deps.ps1 b/scripts/install_deps.ps1 new file mode 100644 index 000000000000..ca204729d6c4 --- /dev/null +++ b/scripts/install_deps.ps1 @@ -0,0 +1,20 @@ +$ErrorActionPreference = "Stop" + +# Needed for Invoke-WebRequest to work via CI. +$progressPreference = "silentlyContinue" + +if ( -not (Test-Path "$PSScriptRoot\..\deps\boost") ) { + New-Item -ItemType Directory -Force -Path "$PSScriptRoot\..\deps" + + Invoke-WebRequest -URI "https://github.com/Kitware/CMake/releases/download/v3.18.2/cmake-3.18.2-win64-x64.zip" -OutFile cmake.zip + tar -xf cmake.zip + mv cmake-3.18.2-win64-x64 "$PSScriptRoot\..\deps\cmake" + + Invoke-WebRequest -URI "https://dl.bintray.com/boostorg/release/1.74.0/source/boost_1_74_0.zip" -OutFile boost.zip + tar -xf boost.zip + cd boost_1_74_0 + .\bootstrap.bat + .\b2 -j4 -d0 link=static runtime-link=static variant=release threading=multi address-model=64 --with-filesystem --with-system --with-program_options --with-test --prefix="$PSScriptRoot\..\deps\boost" install + if ( -not $? ) { throw "Error building boost." } + cd .. +} diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index a825a6d84f70..0312db4b3684 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -6,7 +6,7 @@ # # This is an "infrastucture-as-code" alternative to the manual build # instructions pages which we previously maintained at: -# http://solidity.readthedocs.io/en/latest/installing-solidity.html +# https://docs.soliditylang.org/en/latest/installing-solidity.html # # The aim of this script is to simplify things down to the following basic # flow for all supported operating systems: @@ -23,7 +23,7 @@ # # The documentation for solidity is hosted at: # -# http://solidity.readthedocs.io/ +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -101,7 +101,7 @@ case $(uname -s) in esac # Check for Homebrew install and abort if it is not installed. - brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See http://brew.sh."; exit 1; } + brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See https://brew.sh."; exit 1; } brew update brew install boost brew install cmake @@ -179,7 +179,7 @@ case $(uname -s) in #wheezy echo "Installing solidity dependencies on Debian Wheezy (7.x)." echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get 'install_deps.sh' working for Debian Wheezy, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." @@ -254,7 +254,7 @@ case $(uname -s) in #openSUSE echo "Installing solidity dependencies on openSUSE." echo "ERROR - 'install_deps.sh' doesn't have openSUSE support yet." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get 'install_deps.sh' working for openSUSE, that would be fantastic." echo "See https://github.com/ethereum/webthree-umbrella/issues/552." exit 1 @@ -264,7 +264,7 @@ case $(uname -s) in # #------------------------------------------------------------------------------ - Ubuntu|LinuxMint) + Ubuntu|LinuxMint|Pop) #LinuxMint is a distro on top of Ubuntu. #Ubuntu install_z3="" @@ -311,7 +311,7 @@ case $(uname -s) in #do not try anything for betsy. echo "Linux Mint Betsy is not supported at the moment as it runs off of Debian." echo "We only support Sylvia, Sonya, Serena, Sarah, Rosa, Rafaela, Rebecca, and Qiana." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your distro working, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." exit 1 @@ -396,7 +396,7 @@ case $(uname -s) in #other Linux echo "ERROR - Unsupported or unidentified Linux distro." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your distro working, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." exit 1 @@ -413,7 +413,7 @@ case $(uname -s) in *) #other echo "ERROR - Unsupported or unidentified operating system." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." + echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions." echo "If you would like to get your operating system working, that would be fantastic." echo "Drop us a message at https://gitter.im/ethereum/solidity-dev." ;; diff --git a/scripts/install_evmone.ps1 b/scripts/install_evmone.ps1 new file mode 100644 index 000000000000..ba69dab34ffa --- /dev/null +++ b/scripts/install_evmone.ps1 @@ -0,0 +1,9 @@ +$ErrorActionPreference = "Stop" + +# Needed for Invoke-WebRequest to work via CI. +$progressPreference = "silentlyContinue" + +Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.5.0/evmone-0.5.0-windows-amd64.zip" -OutFile "evmone.zip" +tar -xf evmone.zip "bin/evmone.dll" +mkdir deps +mv bin/evmone.dll deps diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 0f2bc1d79c44..33fdc0165ff5 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -10,7 +10,7 @@ import re import os import hashlib -from os.path import join, isfile +from os.path import join, isfile, split def extract_test_cases(path): lines = open(path, encoding="utf8", errors='ignore', mode='r').read().splitlines() @@ -43,7 +43,7 @@ def extract_docs_cases(path): tests = [] # Collect all snippets of indented blocks - for l in open(path, mode='r', encoding='utf8').read().splitlines(): + for l in open(path, mode='r', errors='ignore', encoding='utf8').read().splitlines(): if l != '': if not inside and l.startswith(' '): # start new test @@ -99,5 +99,8 @@ def extract_and_write(f, path): if 'compilationTests' in subdirs: subdirs.remove('compilationTests') for f in files: + _, tail = split(f) + if tail == "invalid_utf8_sequence.sol": + continue # ignore the test with broken utf-8 encoding path = join(root, f) extract_and_write(f, path) diff --git a/scripts/release.bat b/scripts/release.bat index caa56fc9a561..a1a909ae006e 100644 --- a/scripts/release.bat +++ b/scripts/release.bat @@ -5,7 +5,7 @@ REM Batch file for implementing release flow for solidity for Windows. REM REM The documentation for solidity is hosted at: REM -REM https://solidity.readthedocs.org +REM https://docs.soliditylang.org REM REM --------------------------------------------------------------------------- REM This file is part of solidity. diff --git a/scripts/release.sh b/scripts/release.sh index 76d57e2ffdc9..42e062fde060 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -8,7 +8,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 3017692cce63..0a1ef62c4fe8 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -57,7 +57,7 @@ packagename=solc static_build_distribution=focal -DISTRIBUTIONS="bionic eoan focal" +DISTRIBUTIONS="focal groovy" if is_release then @@ -113,7 +113,6 @@ wget -O ./solc/deps/downloads/jsoncpp-1.9.3.tar.gz https://github.com/open-sourc cd solc version=$($(dirname "$0")/get_version.sh) commithash=$(git rev-parse --short=8 HEAD) -committimestamp=$(git show --format=%ci HEAD | head -n 1) commitdate=$(git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/-0?/./' | sed -e 's/-0?/./') echo "$commithash" > commit_hash.txt @@ -158,7 +157,7 @@ Vcs-Git: git://github.com/ethereum/solidity.git Vcs-Browser: https://github.com/ethereum/solidity Package: solc -Architecture: any-i386 any-amd64 +Architecture: any-amd64 Multi-Arch: same Depends: \${shlibs:Depends}, \${misc:Depends} Conflicts: libethereum (<= 1.2.9) diff --git a/scripts/splitSources.py b/scripts/splitSources.py index 01a9f9278b3e..54ddb2f38a17 100755 --- a/scripts/splitSources.py +++ b/scripts/splitSources.py @@ -11,10 +11,20 @@ import sys import os +import traceback hasMultipleSources = False createdSources = [] + +def uncaught_exception_hook(exc_type, exc_value, exc_traceback): + # The script `scripts/ASTImportTest.sh` will interpret return code 3 + # as a critical error (because of the uncaught exception) and will + # terminate further execution. + print("Unhandled exception: %s", "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))) + sys.exit(3) + + def extractSourceName(line): if line.find("/") > -1: filePath = line[13: line.rindex("/")] @@ -23,6 +33,7 @@ def extractSourceName(line): return filePath, srcName return False, line[line.find(":")+2 : line.find(" ====")] + # expects the first line of lines to be "==== Source: sourceName ====" # writes the following source into a file named sourceName def writeSourceToFile(lines): @@ -45,18 +56,29 @@ def writeSourceToFile(lines): writeSourceToFile(lines[1+idx:]) break + if __name__ == '__main__': filePath = sys.argv[1] - # decide if file has multiple sources - lines = open(filePath, mode='r', encoding='utf8').read().splitlines() - if lines[0][:12] == "==== Source:": - hasMultipleSources = True - writeSourceToFile(lines) - - if hasMultipleSources: - srcString = "" - for src in createdSources: - srcString += src + ' ' - print(srcString) - else: - sys.exit(1) + sys.excepthook = uncaught_exception_hook + + try: + # decide if file has multiple sources + lines = open(filePath, mode='r', encoding='utf8').read().splitlines() + if lines[0][:12] == "==== Source:": + hasMultipleSources = True + writeSourceToFile(lines) + + if hasMultipleSources: + srcString = "" + for src in createdSources: + srcString += src + ' ' + print(srcString) + sys.exit(0) + else: + sys.exit(1) + + except UnicodeDecodeError as ude: + print("UnicodeDecodeError in '" + filePath + "': " + str(ude)) + print("This is expected for some tests containing invalid utf8 sequences. " + "Exception will be ignored.") + sys.exit(2) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 97cbe4ab415d..39c433028789 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -2,11 +2,14 @@ set -e -ROOT_DIR="$(dirname "$0")"/.. +READLINK=readlink +if [[ "$OSTYPE" == "darwin"* ]]; then + READLINK=greadlink +fi +ROOT_DIR=$(${READLINK} -f "$(dirname "$0")"/..) WORKDIR="${ROOT_DIR}/build/antlr" ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar" -ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.7.2-complete.jar" -GRAMMAR_FILE="$(readlink -f "${ROOT_DIR}/docs/Solidity.g4")" +ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" SGR_RESET="\033[0m" SGR_BOLD="\033[1m" @@ -40,11 +43,14 @@ if [[ ! -f "${WORKDIR}/target/SolidityParser.class" ]] || \ [ "${GRAMMAR_FILE}" -nt "${WORKDIR}/target/SolidityParser.class" ] then echo "Creating parser" + ( + cd "${ROOT_DIR}"/docs/grammar # Create lexer/parser from grammar - java -jar "${ANTLR_JAR}" "${GRAMMAR_FILE}" -o "${WORKDIR}/src/" + java -jar "${ANTLR_JAR}" Solidity.g4 SolidityLexer.g4 -o "${WORKDIR}/src/" # Compile lexer/parser sources javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/" + ) fi # Run tests @@ -52,50 +58,93 @@ failed_count=0 test_file() { local SOL_FILE - SOL_FILE="$(readlink -m "${1}")" + SOL_FILE="$(${READLINK} -m "${1}")" local cur=${2} local max=${3} + local solOrYul=${4} echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..." local output - output=$( - java \ - -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ - "org.antlr.v4.gui.TestRig" \ - Solidity \ - sourceUnit <"${SOL_FILE}" 2>&1 - ) + if [[ "${solOrYul}" == "sol" ]]; then + output=$( + java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + sourceUnit <"${SOL_FILE}" 2>&1 + ) + else + output=$( + echo "assembly $(cat "${SOL_FILE}")" | java \ + -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ + "org.antlr.v4.gui.TestRig" \ + Solidity \ + assemblyStatement 2>&1 + ) + fi vt_cursor_up vt_cursor_begin_of_line - if [[ "${output}" == "" ]] - then - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + if grep -qE "^\/\/ ParserError" "${SOL_FILE}"; then + if [[ "${output}" != "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}SUCCEEDED DESPITE PARSER ERROR${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi else - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" - echo "${output}" - failed_count=$((failed_count + 1)) - exit 1 + if [[ "${output}" == "" ]] + then + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" + echo "${output}" + failed_count=$((failed_count + 1)) + exit 1 + fi fi } -# we only want to use files that do not contain errors or multi-source files. +# we only want to use files that do not contain excluded parser errors, analysis errors or multi-source files. SOL_FILES=() while IFS='' read -r line do SOL_FILES+=("$line") done < <( grep -riL -E \ - "^\/\/ (Syntax|Type|Parser|Declaration)Error|^==== Source:" \ + "^\/\/ (Syntax|Type|Declaration)Error|^\/\/ ParserError (2837|3716|3997|5333|6275|6281|6933|7319)|^==== Source:" \ "${ROOT_DIR}/test/libsolidity/syntaxTests" \ - "${ROOT_DIR}/test/libsolidity/semanticTests" \ + "${ROOT_DIR}/test/libsolidity/semanticTests" | + grep -v -E 'comments/.*_direction_override.*.sol' | + grep -v -E 'literals/.*_direction_override.*.sol' + # Skipping the unicode tests as I couldn't adapt the lexical grammar to recursively counting RLO/LRO/PDF's. ) +YUL_FILES=() +# Add all yul optimizer tests without objects and types. +while IFS='' read -r line +do + YUL_FILES+=("$line") +done < <( + grep -riL -E \ + "object|\:[ ]*[uib]" \ + "${ROOT_DIR}/test/libyul/yulOptimizerTests" +) + +num_tests=$((${#SOL_FILES[*]} + ${#YUL_FILES[*]})) test_count=0 for SOL_FILE in "${SOL_FILES[@]}" do test_count=$((test_count + 1)) - test_file "${SOL_FILE}" ${test_count} ${#SOL_FILES[*]} + test_file "${SOL_FILE}" ${test_count} $num_tests "sol" +done +for YUL_FILE in "${YUL_FILES[@]}" +do + test_count=$((test_count + 1)) + test_file "${YUL_FILE}" ${test_count} $num_tests "yul" done -echo "Summary: ${failed_count} of ${#SOL_FILES[*]} sources failed." +echo "Summary: ${failed_count} of $num_tests sources failed." exit ${failed_count} diff --git a/scripts/test_emscripten.sh b/scripts/test_emscripten.sh index 980cee196d53..44045540a8eb 100755 --- a/scripts/test_emscripten.sh +++ b/scripts/test_emscripten.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/scripts/tests.sh b/scripts/tests.sh index 00b4a1641742..2882af067ba7 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -90,33 +90,36 @@ for optimize in "" "--optimize" do for vm in $EVM_VERSIONS do - FORCE_ABIV2_RUNS="no" + FORCE_ABIV1_RUNS="no" if [[ "$vm" == "istanbul" ]] then - FORCE_ABIV2_RUNS="no yes" # run both in istanbul + FORCE_ABIV1_RUNS="no yes" # run both in istanbul fi - for abiv2 in $FORCE_ABIV2_RUNS + for abiv1 in $FORCE_ABIV1_RUNS do - force_abiv2_flag="" - if [[ "$abiv2" == "yes" ]] + force_abiv1_flag="" + if [[ "$abiv1" == "yes" ]] then - force_abiv2_flag="--abiencoderv2" + force_abiv1_flag="--abiencoderv1" fi - printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv2_flag..." + printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv1_flag..." log="" if [ -n "$log_directory" ] then if [ -n "$optimize" ] then - log=--logger=JUNIT,error,$log_directory/opt_$vm.xml $testargs + log=--logger=JUNIT,error,$log_directory/opt_$vm.xml else - log=--logger=JUNIT,error,$log_directory/noopt_$vm.xml $testargs_no_opt + log=--logger=JUNIT,error,$log_directory/noopt_$vm.xml fi fi + EWASM_ARGS="" + [ "${vm}" = "byzantium" ] && [ "${optimize}" = "" ] && EWASM_ARGS="--ewasm" + set +e - "${SOLIDITY_BUILD_DIR}"/test/soltest --show-progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" $SMT_FLAGS $force_abiv2_flag + "${SOLIDITY_BUILD_DIR}"/test/soltest --show-progress $log -- ${EWASM_ARGS} --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" $SMT_FLAGS $force_abiv1_flag if test "0" -ne "$?"; then exit 1 diff --git a/scripts/travis-emscripten/deploy_key.enc b/scripts/travis-emscripten/deploy_key.enc deleted file mode 100644 index e6e9e0e6972d..000000000000 Binary files a/scripts/travis-emscripten/deploy_key.enc and /dev/null differ diff --git a/scripts/travis-emscripten/publish_binary.sh b/scripts/travis-emscripten/publish_binary.sh deleted file mode 100755 index eba3cdac5514..000000000000 --- a/scripts/travis-emscripten/publish_binary.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env bash - -#------------------------------------------------------------------------------ -# Bash script for publishing Solidity Emscripten binaries to Github. -# -# The results are committed to https://github.com/ethereum/solc-bin. -# -# The documentation for solidity is hosted at: -# -# http://solidity.readthedocs.io/ -# -# ------------------------------------------------------------------------------ -# This file is part of solidity. -# -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2016 solidity contributors. -#------------------------------------------------------------------------------ - -set -e - -VER=$(cat CMakeLists.txt | grep 'set(PROJECT_VERSION' | sed -e 's/.*set(PROJECT_VERSION "\(.*\)".*/\1/') -test -n "$VER" -VER="v$VER" -COMMIT=$(git rev-parse --short=8 HEAD) -DATE=$(date --date="$(git log -1 --date=iso --format=%ad HEAD)" --utc +%Y.%-m.%-d) - -ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" -ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" -ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} -ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} -openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in scripts/travis-emscripten/deploy_key.enc -out deploy_key -d -chmod 600 deploy_key -eval `ssh-agent -s` -ssh-add deploy_key - -git clone --depth 2 git@github.com:ethereum/solc-bin.git -cd solc-bin -git config user.name "travis" -git config user.email "chris@ethereum.org" -git checkout -B gh-pages origin/gh-pages -git clean -f -d -x - - -FULLVERSION=INVALID -if [ "$TRAVIS_BRANCH" = release ] -then - # We only want one file with this version - if ls ./bin/soljson-"$VER+"*.js - then - echo "Not publishing, we already published this version." - exit 0 - fi - FULLVERSION="$VER+commit.$COMMIT" -elif [ "$TRAVIS_BRANCH" = develop ] -then - # We only want one release per day and we do not want to push the same commit twice. - if ls ./bin/soljson-"$VER-nightly.$DATE"*.js || ls ./bin/soljson-*"commit.$COMMIT.js" - then - echo "Not publishing, we already published this version today." - exit 0 - fi - FULLVERSION="$VER-nightly.$DATE+commit.$COMMIT" -else - echo "Not publishing, wrong branch." - exit 0 -fi - - -NEWFILE="soljson-$FULLVERSION.js" - -# Prepare for update script -npm install - -# This file is assumed to be the product of the build_emscripten.sh script. -cp ../soljson.js ./bin/"$NEWFILE" - -# For releases, add a symlink to the wasm directory. -[ "$TRAVIS_BRANCH" = release ] && ln -sf ../bin/"$NEWFILE" ./wasm/"$NEWFILE" - -# Run update script -npm run update - -# Publish updates -git add ./bin/"$NEWFILE" -[ "$TRAVIS_BRANCH" = release ] && git add ./wasm/"$NEWFILE" -git commit -a -m "Added compiler version $FULLVERSION" -git push origin gh-pages diff --git a/scripts/wasm-rebuild/docker-scripts/patch.sh b/scripts/wasm-rebuild/docker-scripts/patch.sh index a03fc73d0efe..0c536b46c3c6 100755 --- a/scripts/wasm-rebuild/docker-scripts/patch.sh +++ b/scripts/wasm-rebuild/docker-scripts/patch.sh @@ -1,7 +1,8 @@ #!/bin/bash -TAG="$1" -SOLJSON_JS="$2" - # If we ever want to patch the binaries e.g. for compatibility with older solc-js versions, # we can do that here. +# +# This script gets the following parameters: +# - TAG +# - SOLJSON_JS diff --git a/scripts/wasm-rebuild/docker-scripts/rebuild_current.sh b/scripts/wasm-rebuild/docker-scripts/rebuild_current.sh index b48b37fc589e..9a28ce30c157 100755 --- a/scripts/wasm-rebuild/docker-scripts/rebuild_current.sh +++ b/scripts/wasm-rebuild/docker-scripts/rebuild_current.sh @@ -41,7 +41,7 @@ if [ -d jsoncpp ]; then fi set +e -scripts/travis-emscripten/build_emscripten.sh +scripts/*/build_emscripten.sh set -e mkdir -p upload diff --git a/scripts/yul_coverage.sh b/scripts/yul_coverage.sh index c32dc9472d6d..02b9a0476454 100755 --- a/scripts/yul_coverage.sh +++ b/scripts/yul_coverage.sh @@ -30,7 +30,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 3fbdefe6a9c5..a94638090e99 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -20,4 +20,9 @@ if(SOLC_LINK_STATIC AND UNIX AND NOT APPLE) LINK_SEARCH_START_STATIC ON LINK_SEARCH_END_STATIC ON ) -endif() \ No newline at end of file +elseif(SOLC_STATIC_STDLIBS AND UNIX AND NOT APPLE) + set_target_properties( + solc PROPERTIES + LINK_FLAGS "-static-libgcc -static-libstdc++" + ) +endif() diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 27e801c82240..4142a27be73a 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include @@ -55,6 +54,7 @@ #include #include +#include #include #include @@ -91,6 +91,9 @@ namespace solidity::frontend bool g_hasOutput = false; +namespace +{ + std::ostream& sout() { g_hasOutput = true; @@ -104,6 +107,8 @@ std::ostream& serr(bool _used = true) return cerr; } +} + #define cout #define cerr @@ -127,6 +132,9 @@ static string const g_strEVM = "evm"; static string const g_strEVM15 = "evm15"; static string const g_strEVMVersion = "evm-version"; static string const g_strEwasm = "ewasm"; +static string const g_strExperimentalViaIR = "experimental-via-ir"; +static string const g_strGeneratedSources = "generated-sources"; +static string const g_strGeneratedSourcesRuntime = "generated-sources-runtime"; static string const g_strGas = "gas"; static string const g_strHelp = "help"; static string const g_strImportAst = "import-ast"; @@ -144,6 +152,8 @@ static string const g_strMachine = "machine"; static string const g_strMetadata = "metadata"; static string const g_strMetadataHash = "metadata-hash"; static string const g_strMetadataLiteral = "metadata-literal"; +static string const g_strModelCheckerEngine = "model-checker-engine"; +static string const g_strModelCheckerTimeout = "model-checker-timeout"; static string const g_strNatspecDev = "devdoc"; static string const g_strNatspecUser = "userdoc"; static string const g_strNone = "none"; @@ -157,6 +167,8 @@ static string const g_strOutputDir = "output-dir"; static string const g_strOverwrite = "overwrite"; static string const g_strRevertStrings = "revert-strings"; static string const g_strStorageLayout = "storage-layout"; +static string const g_strStopAfter = "stop-after"; +static string const g_strParsing = "parsing"; /// Possible arguments to for --revert-strings static set const g_revertStringsArgs @@ -181,7 +193,6 @@ static string const g_strIgnoreMissingFiles = "ignore-missing"; static string const g_strColor = "color"; static string const g_strNoColor = "no-color"; static string const g_strErrorIds = "error-codes"; -static string const g_strOldReporter = "old-reporter"; static string const g_argAbi = g_strAbi; static string const g_argPrettyJson = g_strPrettyJson; @@ -205,12 +216,15 @@ static string const g_argYul = g_strYul; static string const g_argIR = g_strIR; static string const g_argIROptimized = g_strIROptimized; static string const g_argEwasm = g_strEwasm; +static string const g_argExperimentalViaIR = g_strExperimentalViaIR; static string const g_argLibraries = g_strLibraries; static string const g_argLink = g_strLink; static string const g_argMachine = g_strMachine; static string const g_argMetadata = g_strMetadata; static string const g_argMetadataHash = g_strMetadataHash; static string const g_argMetadataLiteral = g_strMetadataLiteral; +static string const g_argModelCheckerEngine = g_strModelCheckerEngine; +static string const g_argModelCheckerTimeout = g_strModelCheckerTimeout; static string const g_argNatspecDev = g_strNatspecDev; static string const g_argNatspecUser = g_strNatspecUser; static string const g_argOpcodes = g_strOpcodes; @@ -227,7 +241,6 @@ static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; static string const g_argColor = g_strColor; static string const g_argNoColor = g_strNoColor; static string const g_argErrorIds = g_strErrorIds; -static string const g_argOldReporter = g_strOldReporter; /// Possible arguments to for --combined-json static set const g_combinedJsonArgs @@ -238,6 +251,8 @@ static set const g_combinedJsonArgs g_strBinary, g_strBinaryRuntime, g_strCompactJSON, + g_strGeneratedSources, + g_strGeneratedSourcesRuntime, g_strInterface, g_strMetadata, g_strNatspecUser, @@ -316,6 +331,22 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) return false; } +namespace +{ + +bool checkMutuallyExclusive(boost::program_options::variables_map const& args, std::string const& _optionA, std::string const& _optionB) +{ + if (args.count(_optionA) && args.count(_optionB)) + { + serr() << "Option " << _optionA << " and " << _optionB << " are mutually exclusive." << endl; + return false; + } + + return true; +} + +} + void CommandLineInterface::handleBinary(string const& _contract) { if (m_args.count(g_argBinary)) @@ -593,6 +624,7 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() continue; } + // NOTE: we ignore the FileNotFound exception as we manually check above m_sourceCodes[infile.generic_string()] = readFileAsString(infile.string()); path = boost::filesystem::canonical(infile).string(); } @@ -622,6 +654,10 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) { // Thrown e.g. if path is too long. } + catch (FileNotFound const&) + { + // Should not happen if `fs::is_regular_file` is correct. + } vector libraries; boost::split(libraries, data, boost::is_space() || boost::is_any_of(","), boost::token_compress_on); @@ -636,9 +672,16 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) serr() << "Colon separator missing in library address specifier \"" << lib << "\"" << endl; return false; } + string libName(lib.begin(), lib.begin() + static_cast(colon)); - string addrString(lib.begin() + static_cast(colon) + 1, lib.end()); boost::trim(libName); + if (m_libraries.count(libName)) + { + serr() << "Address specified more than once for library \"" << libName << "\"." << endl; + return false; + } + + string addrString(lib.begin() + static_cast(colon) + 1, lib.end()); boost::trim(addrString); if (addrString.substr(0, 2) == "0x") addrString = addrString.substr(2); @@ -702,12 +745,15 @@ map CommandLineInterface::parseAstFromInput() void CommandLineInterface::createFile(string const& _fileName, string const& _data) { namespace fs = boost::filesystem; - // create directory if not existent - fs::path p(m_args.at(g_argOutputDir).as()); - // Do not try creating the directory if the first item is . or .. - if (p.filename() != "." && p.filename() != "..") - fs::create_directories(p); - string pathName = (p / _fileName).string(); + + fs::path outputDir(m_args.at(g_argOutputDir).as()); + + // NOTE: create_directories() raises an exception if the path consists solely of '.' or '..' + // (or equivalent such as './././.'). Paths like 'a/b/.' and 'a/b/..' are fine though. + // The simplest workaround is to use an absolute path. + fs::create_directories(fs::absolute(outputDir)); + + string pathName = (outputDir / _fileName).string(); if (fs::exists(pathName) && !m_args.count(g_strOverwrite)) { serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl; @@ -717,7 +763,11 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da ofstream outFile(pathName); outFile << _data; if (!outFile) - BOOST_THROW_EXCEPTION(FileError() << errinfo_comment("Could not write to file: " + pathName)); + { + serr() << "Could not write to file \"" << pathName << "\"." << endl; + m_error = true; + return; + } } void CommandLineInterface::createJson(string const& _fileName, string const& _json) @@ -795,11 +845,20 @@ General Information)").c_str(), "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " "byzantium, constantinople, petersburg, istanbul (default) or berlin." ) + ( + g_strExperimentalViaIR.c_str(), + "Turn on experimental compilation mode via the IR (EXPERIMENTAL)." + ) ( g_strRevertStrings.c_str(), po::value()->value_name(boost::join(g_revertStringsArgs, ",")), "Strip revert (and require) reason strings or add additional debugging information." ) + ( + g_strStopAfter.c_str(), + po::value()->value_name("stage"), + "Stop execution after the given compiler stage. Valid options: \"parsing\"." + ) ; desc.add(outputOptions); @@ -887,10 +946,6 @@ General Information)").c_str(), g_argErrorIds.c_str(), "Output error codes." ) - ( - g_argOldReporter.c_str(), - "Enables old diagnostics reporter (legacy option, will be removed)." - ) ; desc.add(outputFormatting); @@ -971,6 +1026,23 @@ General Information)").c_str(), ; desc.add(optimizerOptions); + po::options_description smtCheckerOptions("Model Checker Options"); + smtCheckerOptions.add_options() + ( + g_strModelCheckerEngine.c_str(), + po::value()->value_name("all,bmc,chc,none")->default_value("all"), + "Select model checker engine." + ) + ( + g_strModelCheckerTimeout.c_str(), + po::value()->value_name("ms"), + "Set model checker timeout per query in milliseconds. " + "The default is a deterministic resource limit. " + "A timeout of 0 means no resource/time restrictions for any query." + ) + ; + desc.add(smtCheckerOptions); + po::options_description allOptions = desc; allOptions.add_options()(g_argInputFile.c_str(), po::value>(), "input file"); @@ -992,11 +1064,23 @@ General Information)").c_str(), return false; } - if (m_args.count(g_argColor) && m_args.count(g_argNoColor)) - { - serr() << "Option " << g_argColor << " and " << g_argNoColor << " are mutualy exclusive." << endl; + if (!checkMutuallyExclusive(m_args, g_argColor, g_argNoColor)) return false; - } + + static vector const conflictingWithStopAfter{ + g_argBinary, + g_argIR, + g_argIROptimized, + g_argEwasm, + g_argGas, + g_argAsm, + g_argAsmJson, + g_argOpcodes + }; + + for (auto& option: conflictingWithStopAfter) + if (!checkMutuallyExclusive(m_args, g_strStopAfter, option)) + return false; m_coloredOutput = !m_args.count(g_argNoColor) && (isatty(STDERR_FILENO) || m_args.count(g_argColor)); @@ -1091,6 +1175,7 @@ bool CommandLineInterface::processInput() if (!boost::filesystem::is_regular_file(canonicalPath)) return ReadCallback::Result{false, "Not a valid file."}; + // NOTE: we ignore the FileNotFound exception as we manually check above auto contents = readFileAsString(canonicalPath.string()); m_sourceCodes[path.generic_string()] = contents; return ReadCallback::Result{true, contents}; @@ -1134,6 +1219,17 @@ bool CommandLineInterface::processInput() } } + if (m_args.count(g_strStopAfter)) + { + if (m_args[g_strStopAfter].as() != "parsing") + { + serr() << "Valid options for --" << g_strStopAfter << " are: \"parsing\".\n"; + return false; + } + else + m_stopAfter = CompilerStack::State::Parsed; + } + vector const exclusiveModes = { g_argStandardJSON, g_argLink, @@ -1166,7 +1262,17 @@ bool CommandLineInterface::processInput() if (jsonFile.empty()) input = readStandardInput(); else - input = readFileAsString(jsonFile); + { + try + { + input = readFileAsString(jsonFile); + } + catch (FileNotFound const&) + { + serr() << "File not found: " << jsonFile << endl; + return false; + } + } StandardCompiler compiler(fileReader); sout() << compiler.compile(std::move(input)) << endl; return true; @@ -1334,13 +1440,24 @@ bool CommandLineInterface::processInput() } } + if (m_args.count(g_argModelCheckerEngine)) + { + string engineStr = m_args[g_argModelCheckerEngine].as(); + optional engine = ModelCheckerEngine::fromString(engineStr); + if (!engine) + { + serr() << "Invalid option for --" << g_argModelCheckerEngine << ": " << engineStr << endl; + return false; + } + m_modelCheckerSettings.engine = *engine; + } + + if (m_args.count(g_argModelCheckerTimeout)) + m_modelCheckerSettings.timeout = m_args[g_argModelCheckerTimeout].as(); + m_compiler = make_unique(fileReader); - unique_ptr formatter; - if (m_args.count(g_argOldReporter)) - formatter = make_unique(serr(false)); - else - formatter = make_unique(serr(false), m_coloredOutput, m_withErrorIds); + SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); try { @@ -1348,11 +1465,15 @@ bool CommandLineInterface::processInput() m_compiler->useMetadataLiteralSources(true); if (m_args.count(g_argMetadataHash)) m_compiler->setMetadataHash(m_metadataHash); + if (m_args.count(g_argModelCheckerEngine) || m_args.count(g_argModelCheckerTimeout)) + m_compiler->setModelCheckerSettings(m_modelCheckerSettings); if (m_args.count(g_argInputFile)) m_compiler->setRemappings(m_remappings); if (m_args.count(g_argLibraries)) m_compiler->setLibraries(m_libraries); + if (m_args.count(g_argExperimentalViaIR)) + m_compiler->setViaIR(true); m_compiler->setEVMVersion(m_evmVersion); m_compiler->setRevertStringBehaviour(m_revertStrings); // TODO: Perhaps we should not compile unless requested @@ -1396,7 +1517,7 @@ bool CommandLineInterface::processInput() if (!m_compiler->analyze()) { for (auto const& error: m_compiler->errors()) - formatter->printErrorInformation(*error); + formatter.printErrorInformation(*error); astAssert(false, "Analysis of the AST failed"); } } @@ -1413,12 +1534,12 @@ bool CommandLineInterface::processInput() m_compiler->setParserErrorRecovery(true); } - bool successful = m_compiler->compile(); + bool successful = m_compiler->compile(m_stopAfter); for (auto const& error: m_compiler->errors()) { g_hasOutput = true; - formatter->printErrorInformation(*error); + formatter.printErrorInformation(*error); } if (!successful) @@ -1432,7 +1553,7 @@ bool CommandLineInterface::processInput() catch (CompilerError const& _exception) { g_hasOutput = true; - formatter->printExceptionInformation(_exception, "Compiler error"); + formatter.printExceptionInformation(_exception, "Compiler error"); return false; } catch (InternalCompilerError const& _exception) @@ -1466,7 +1587,7 @@ bool CommandLineInterface::processInput() else { g_hasOutput = true; - formatter->printExceptionInformation(_error, _error.typeName()); + formatter.printExceptionInformation(_error, _error.typeName()); } return false; @@ -1510,7 +1631,7 @@ void CommandLineInterface::handleCombinedJSON() { Json::Value& contractData = output[g_strContracts][contractName] = Json::objectValue; if (requests.count(g_strAbi)) - contractData[g_strAbi] = jsonCompactPrint(m_compiler->contractABI(contractName)); + contractData[g_strAbi] = m_compiler->contractABI(contractName); if (requests.count("metadata")) contractData["metadata"] = m_compiler->metadata(contractName); if (requests.count(g_strBinary) && m_compiler->compilationSuccessful()) @@ -1522,7 +1643,11 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) - contractData[g_strStorageLayout] = jsonCompactPrint(m_compiler->storageLayout(contractName)); + contractData[g_strStorageLayout] = m_compiler->storageLayout(contractName); + if (requests.count(g_strGeneratedSources) && m_compiler->compilationSuccessful()) + contractData[g_strGeneratedSources] = m_compiler->generatedSources(contractName, false); + if (requests.count(g_strGeneratedSourcesRuntime) && m_compiler->compilationSuccessful()) + contractData[g_strGeneratedSourcesRuntime] = m_compiler->generatedSources(contractName, true); if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) { auto map = m_compiler->sourceMapping(contractName); @@ -1536,9 +1661,9 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strSignatureHashes)) contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); if (requests.count(g_strNatspecDev)) - contractData[g_strNatspecDev] = jsonCompactPrint(m_compiler->natspecDev(contractName)); + contractData[g_strNatspecDev] = m_compiler->natspecDev(contractName); if (requests.count(g_strNatspecUser)) - contractData[g_strNatspecUser] = jsonCompactPrint(m_compiler->natspecUser(contractName)); + contractData[g_strNatspecUser] = m_compiler->natspecUser(contractName); } bool needsSourceList = requests.count(g_strAst) || requests.count(g_strSrcMap) || requests.count(g_strSrcMapRuntime); @@ -1553,11 +1678,10 @@ void CommandLineInterface::handleCombinedJSON() if (requests.count(g_strAst)) { - bool legacyFormat = !requests.count(g_strCompactJSON); output[g_strSources] = Json::Value(Json::objectValue); for (auto const& sourceCode: m_sourceCodes) { - ASTJsonConverter converter(legacyFormat, m_compiler->sourceIndices()); + ASTJsonConverter converter(m_compiler->state(), m_compiler->sourceIndices()); output[g_strSources][sourceCode.first] = Json::Value(Json::objectValue); output[g_strSources][sourceCode.first]["AST"] = converter.toJson(m_compiler->ast(sourceCode.first)); } @@ -1572,57 +1696,34 @@ void CommandLineInterface::handleCombinedJSON() sout() << json << endl; } -void CommandLineInterface::handleAst(string const& _argStr) +void CommandLineInterface::handleAst() { - string title; + if (!m_args.count(g_argAstCompactJson)) + return; - if (_argStr == g_argAstJson) - title = "JSON AST:"; - else if (_argStr == g_argAstCompactJson) - title = "JSON AST (compact format):"; - else - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal argStr for AST")); + vector asts; + for (auto const& sourceCode: m_sourceCodes) + asts.push_back(&m_compiler->ast(sourceCode.first)); - // do we need AST output? - if (m_args.count(_argStr)) + if (m_args.count(g_argOutputDir)) { - vector asts; for (auto const& sourceCode: m_sourceCodes) - asts.push_back(&m_compiler->ast(sourceCode.first)); - map gasCosts; - for (auto const& contract: m_compiler->contractNames()) - if (m_compiler->compilationSuccessful()) - if (auto const* assemblyItems = m_compiler->runtimeAssemblyItems(contract)) - { - auto ret = GasEstimator::breakToStatementLevel( - GasEstimator(m_evmVersion).structuralEstimation(*assemblyItems, asts), - asts - ); - for (auto const& it: ret) - gasCosts[it.first] += it.second; - } - - bool legacyFormat = !m_args.count(g_argAstCompactJson); - if (m_args.count(g_argOutputDir)) { - for (auto const& sourceCode: m_sourceCodes) - { - stringstream data; - string postfix = ""; - ASTJsonConverter(legacyFormat, m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first)); - postfix += "_json"; - boost::filesystem::path path(sourceCode.first); - createFile(path.filename().string() + postfix + ".ast", data.str()); - } + stringstream data; + string postfix = ""; + ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(data, m_compiler->ast(sourceCode.first)); + postfix += "_json"; + boost::filesystem::path path(sourceCode.first); + createFile(path.filename().string() + postfix + ".ast", data.str()); } - else + } + else + { + sout() << "JSON AST (compact format):" << endl << endl; + for (auto const& sourceCode: m_sourceCodes) { - sout() << title << endl << endl; - for (auto const& sourceCode: m_sourceCodes) - { - sout() << endl << "======= " << sourceCode.first << " =======" << endl; - ASTJsonConverter(legacyFormat, m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first)); - } + sout() << endl << "======= " << sourceCode.first << " =======" << endl; + ASTJsonConverter(m_compiler->state(), m_compiler->sourceIndices()).print(sout(), m_compiler->ast(sourceCode.first)); } } } @@ -1667,20 +1768,26 @@ bool CommandLineInterface::link() { while (it != end && *it != '_') ++it; if (it == end) break; - if (end - it < placeholderSize) + if ( + end - it < placeholderSize || + *(it + 1) != '_' || + *(it + placeholderSize - 2) != '_' || + *(it + placeholderSize - 1) != '_' + ) { - serr() << "Error in binary object file " << src.first << " at position " << (end - src.second.begin()) << endl; + serr() << "Error in binary object file " << src.first << " at position " << (it - src.second.begin()) << endl; + serr() << '"' << string(it, it + min(placeholderSize, static_cast(end - it))) << "\" is not a valid link reference." << endl; return false; } - string name(it, it + placeholderSize); - if (librariesReplacements.count(name)) + string foundPlaceholder(it, it + placeholderSize); + if (librariesReplacements.count(foundPlaceholder)) { - string hexStr(toHex(librariesReplacements.at(name).asBytes())); + string hexStr(toHex(librariesReplacements.at(foundPlaceholder).asBytes())); copy(hexStr.begin(), hexStr.end(), it); } else - serr() << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl; + serr() << "Reference \"" << foundPlaceholder << "\" in file \"" << src.first << "\" still unresolved." << endl; it += placeholderSize; } // Remove hints for resolved libraries. @@ -1775,16 +1882,12 @@ bool CommandLineInterface::assemble( for (auto const& sourceAndStack: assemblyStacks) { auto const& stack = sourceAndStack.second; - unique_ptr formatter; - if (m_args.count(g_argOldReporter)) - formatter = make_unique(serr(false)); - else - formatter = make_unique(serr(false), m_coloredOutput, m_withErrorIds); + SourceReferenceFormatter formatter(serr(false), m_coloredOutput, m_withErrorIds); for (auto const& error: stack.errors()) { g_hasOutput = true; - formatter->printErrorInformation(*error); + formatter.printErrorInformation(*error); } if (!Error::containsOnlyWarnings(stack.errors())) successful = false; @@ -1808,8 +1911,29 @@ bool CommandLineInterface::assemble( if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm) { - stack.translate(yul::AssemblyStack::Language::Ewasm); - stack.optimize(); + try + { + stack.translate(yul::AssemblyStack::Language::Ewasm); + stack.optimize(); + } + catch (Exception const& _exception) + { + serr() << "Exception in assembler: " << boost::diagnostic_information(_exception) << endl; + return false; + } + catch (std::exception const& _e) + { + serr() << + "Unknown exception during compilation" << + (_e.what() ? ": " + string(_e.what()) : ".") << + endl; + return false; + } + catch (...) + { + serr() << "Unknown exception in assembler." << endl; + return false; + } sout() << endl << "==========================" << endl; sout() << endl << "Translated source:" << endl; @@ -1820,6 +1944,7 @@ bool CommandLineInterface::assemble( try { object = stack.assemble(_targetMachine); + object.bytecode->link(m_libraries); } catch (Exception const& _exception) { @@ -1860,10 +1985,12 @@ void CommandLineInterface::outputCompilationResults() handleCombinedJSON(); // do we need AST output? - handleAst(g_argAstJson); - handleAst(g_argAstCompactJson); + handleAst(); - if (!m_compiler->compilationSuccessful()) + if ( + !m_compiler->compilationSuccessful() && + m_stopAfter == CompilerStack::State::CompilationSuccessful + ) { serr() << endl << "Compilation halted after AST generation due to errors." << endl; return; diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index e79d8140688f..d68fc587f416 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -67,7 +67,7 @@ class CommandLineInterface void outputCompilationResults(); void handleCombinedJSON(); - void handleAst(std::string const& _argStr); + void handleAst(); void handleBinary(std::string const& _contract); void handleOpcode(std::string const& _contract); void handleIR(std::string const& _contract); @@ -79,7 +79,6 @@ class CommandLineInterface void handleABI(std::string const& _contract); void handleNatspec(bool _natspecDev, std::string const& _contract); void handleGasEstimation(std::string const& _contract); - void handleFormal(); void handleStorageLayout(std::string const& _contract); /// Fills @a m_sourceCodes initially and @a m_redirects. @@ -127,12 +126,15 @@ class CommandLineInterface std::map m_libraries; /// Solidity compiler stack std::unique_ptr m_compiler; + CompilerStack::State m_stopAfter = CompilerStack::State::CompilationSuccessful; /// EVM version to use langutil::EVMVersion m_evmVersion; /// How to handle revert strings RevertStrings m_revertStrings = RevertStrings::Default; /// Chosen hash method for the bytecode metadata. CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS; + /// Model checker settings. + ModelCheckerSettings m_modelCheckerSettings; /// Whether or not to colorize diagnostics output. bool m_coloredOutput = true; /// Whether or not to output error IDs. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 06a082e66e60..f7adf16a9fdf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,12 +29,14 @@ detect_stray_source_files("${contracts_sources}" "contracts/") set(libsolutil_sources libsolutil/Checksum.cpp libsolutil/CommonData.cpp + libsolutil/FixedHash.cpp libsolutil/IndentedWriter.cpp libsolutil/IpfsHash.cpp libsolutil/IterateReplacing.cpp libsolutil/JSON.cpp libsolutil/Keccak256.cpp libsolutil/LazyInit.cpp + libsolutil/LEB128.cpp libsolutil/StringUtils.cpp libsolutil/SwarmHash.cpp libsolutil/UTF8.cpp @@ -50,6 +52,7 @@ detect_stray_source_files("${libevmasm_sources}" "libevmasm/") set(liblangutil_sources liblangutil/CharStream.cpp + liblangutil/Scanner.cpp liblangutil/SourceLocation.cpp ) detect_stray_source_files("${liblangutil_sources}" "liblangutil/") @@ -78,9 +81,6 @@ set(libsolidity_sources libsolidity/SemanticTest.cpp libsolidity/SemanticTest.h libsolidity/SemVerMatcher.cpp - libsolidity/SMTChecker.cpp - libsolidity/SMTCheckerJSONTest.cpp - libsolidity/SMTCheckerJSONTest.h libsolidity/SMTCheckerTest.cpp libsolidity/SMTCheckerTest.h libsolidity/SolidityCompiler.cpp @@ -92,7 +92,6 @@ set(libsolidity_sources libsolidity/SolidityNatspecJSON.cpp libsolidity/SolidityOptimizer.cpp libsolidity/SolidityParser.cpp - libsolidity/SolidityScanner.cpp libsolidity/SolidityTypes.cpp libsolidity/StandardCompiler.cpp libsolidity/SyntaxTest.cpp diff --git a/test/Common.cpp b/test/Common.cpp index b56cb3257734..8c2be05ca89c 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include +#include #include #include @@ -29,6 +30,9 @@ namespace po = boost::program_options; namespace solidity::test { +namespace +{ + /// If non-empty returns the value of the env. variable ETH_TEST_PATH, otherwise /// it tries to find a path that contains the directories "libsolidity/syntaxTests" /// and returns it if found. @@ -57,9 +61,9 @@ boost::filesystem::path testPath() return {}; } -std::string EVMOneEnvOrDefaultPath() +std::string envOrDefaultPath(std::string const& env_name, std::string const& lib_name) { - if (auto path = getenv("ETH_EVMONE")) + if (auto path = getenv(env_name.c_str())) return path; auto const searchPath = @@ -76,13 +80,15 @@ std::string EVMOneEnvOrDefaultPath() }; for (auto const& basePath: searchPath) { - fs::path p = basePath / evmoneFilename; + fs::path p = basePath / lib_name; if (fs::exists(p)) return p.string(); } return {}; } +} + CommonOptions::CommonOptions(std::string _caption): options(_caption, po::options_description::m_default_line_length, @@ -92,11 +98,12 @@ CommonOptions::CommonOptions(std::string _caption): options.add_options() ("evm-version", po::value(&evmVersionString), "which evm version to use") ("testpath", po::value(&this->testPath)->default_value(solidity::test::testPath()), "path to test files") - ("evmonepath", po::value(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library") + ("vm", po::value>(&vmPaths), "path to evmc library, can be supplied multiple times.") + ("ewasm", po::bool_switch(&ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.") ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") ("optimize", po::bool_switch(&optimize), "enables optimization") ("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.") - ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2") + ("abiencoderv1", po::bool_switch(&useABIEncoderV1), "enables abi encoder v1") ("show-messages", po::bool_switch(&showMessages), "enables message output") ("show-metadata", po::bool_switch(&showMetadata), "enables metadata output"); } @@ -141,6 +148,33 @@ bool CommonOptions::parse(int argc, char const* const* argv) throw std::runtime_error(errorMessage.str()); } + if (vmPaths.empty()) + { + std::string evmone = envOrDefaultPath("ETH_EVMONE", evmoneFilename); + if (!evmone.empty()) + vmPaths.emplace_back(evmone); + else + { + std::cout << "Unable to find " << solidity::test::evmoneFilename + << ". Please provide the path using --vm ." << std::endl; + std::cout << "You can download it at" << std::endl; + std::cout << solidity::test::evmoneDownloadLink << std::endl; + } + } + + if (ewasm) { + std::string hera = envOrDefaultPath("ETH_HERA", heraFilename); + if (!hera.empty()) + vmPaths.emplace_back(hera); + else { + std::cout << "Unable to find " << solidity::test::heraFilename + << ". Please provide the path using --vm ." << std::endl; + std::cout << "You can download it at" << std::endl; + std::cout << solidity::test::heraDownloadLink << std::endl; + std::cout << "Ewasm tests disabled." << std::endl; + } + } + return true; } diff --git a/test/Common.h b/test/Common.h index eaad7f0bac64..57a7c773d5f1 100644 --- a/test/Common.h +++ b/test/Common.h @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include @@ -31,25 +33,31 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-windows-amd64.zip"; +static constexpr auto heraFilename = "hera.dll"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.2.tar.gz"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-darwin-x86_64.tar.gz"; +static constexpr auto heraFilename = "libhera.dylib"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2/hera-0.3.2-darwin-x86_64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-linux-x86_64.tar.gz"; +static constexpr auto heraFilename = "libhera.so"; +static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.2/hera-0.3.2-linux-x86_64.tar.gz"; #endif - struct ConfigException : public util::Exception {}; struct CommonOptions: boost::noncopyable { - boost::filesystem::path evmonePath; + std::vector vmPaths; boost::filesystem::path testPath; + bool ewasm = false; bool optimize = false; bool enforceViaYul = false; bool disableSMT = false; - bool useABIEncoderV2 = false; + bool useABIEncoderV1 = false; bool showMessages = false; bool showMetadata = false; @@ -63,9 +71,9 @@ struct CommonOptions: boost::noncopyable static void setSingleton(std::unique_ptr&& _instance); CommonOptions(std::string caption = ""); - virtual ~CommonOptions() {}; -protected: + virtual ~CommonOptions() {} +protected: boost::program_options::options_description options; private: diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 51dc833521c9..c39540a7584a 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -39,17 +39,18 @@ using namespace evmc::literals; evmc::VM& EVMHost::getVM(string const& _path) { - static evmc::VM theVM; - if (!theVM && !_path.empty()) + static evmc::VM NullVM{nullptr}; + static map> vms; + if (vms.count(_path) == 0) { evmc_loader_error_code errorCode = {}; auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)}; if (vm && errorCode == EVMC_LOADER_SUCCESS) { - if (vm.get_capabilities() & EVMC_CAPABILITY_EVM1) - theVM = std::move(vm); + if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM)) + vms[_path] = make_unique(evmc::VM(move(vm))); else - cerr << "VM loaded does not support EVM1" << endl; + cerr << "VM loaded neither supports EVM1 nor EWASM" << endl; } else { @@ -59,7 +60,38 @@ evmc::VM& EVMHost::getVM(string const& _path) cerr << endl; } } - return theVM; + + if (vms.count(_path) > 0) + return *vms[_path]; + + return NullVM; +} + +bool EVMHost::checkVmPaths(vector const& _vmPaths) +{ + bool evmVmFound = false; + bool ewasmVmFound = false; + for (auto const& path: _vmPaths) + { + evmc::VM& vm = EVMHost::getVM(path.string()); + if (!vm) + return false; + + if (vm.has_capability(EVMC_CAPABILITY_EVM1)) + { + if (evmVmFound) + throw runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm."); + evmVmFound = true; + } + + if (vm.has_capability(EVMC_CAPABILITY_EWASM)) + { + if (ewasmVmFound) + throw runtime_error("Multiple ewasm evmc vms where defined. Please only define one ewasm evmc vm."); + ewasmVmFound = true; + } + } + return evmVmFound; } EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): @@ -91,6 +123,22 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): else assertThrow(false, Exception, "Unsupported EVM version"); + tx_context.block_difficulty = evmc::uint256be{200000000}; + tx_context.block_gas_limit = 20000000; + tx_context.block_coinbase = 0x7878787878787878787878787878787878787878_address; + tx_context.tx_gas_price = evmc::uint256be{3000000000}; + tx_context.tx_origin = 0x9292929292929292929292929292929292929292_address; + // Mainnet according to EIP-155 + tx_context.chain_id = evmc::uint256be{1}; + + reset(); +} + +void EVMHost::reset() +{ + accounts.clear(); + m_currentAddress = {}; + // Mark all precompiled contracts as existing. Existing here means to have a balance (as per EIP-161). // NOTE: keep this in sync with `EVMHost::call` below. // @@ -99,19 +147,13 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): // roughly 22 days before the update went live. for (unsigned precompiledAddress = 1; precompiledAddress <= 8; precompiledAddress++) { - evmc::address address{}; - address.bytes[19] = precompiledAddress; + evmc::address address{precompiledAddress}; // 1wei accounts[address].balance = evmc::uint256be{1}; + // Set according to EIP-1052. + if (precompiledAddress < 5 || m_evmVersion >= langutil::EVMVersion::byzantium()) + accounts[address].codehash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470_bytes32; } - - tx_context.block_difficulty = evmc::uint256be{200000000}; - tx_context.block_gas_limit = 20000000; - tx_context.block_coinbase = 0x7878787878787878787878787878787878787878_address; - tx_context.tx_gas_price = evmc::uint256be{3000000000}; - tx_context.tx_origin = 0x9292929292929292929292929292929292929292_address; - // Mainnet according to EIP-155 - tx_context.chain_id = evmc::uint256be{1}; } void EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _beneficiary) noexcept @@ -167,7 +209,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept { // TODO this is not the right formula // TODO is the nonce incremented on failure, too? - Address createAddress(keccak256( + h160 createAddress(keccak256( bytes(begin(message.sender.bytes), end(message.sender.bytes)) + asBytes(to_string(sender.nonce++)) )); @@ -176,7 +218,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept } else if (message.kind == EVMC_CREATE2) { - Address createAddress(keccak256( + h160 createAddress(keccak256( bytes(1, 0xff) + bytes(begin(message.sender.bytes), end(message.sender.bytes)) + bytes(begin(message.create2_salt.bytes), end(message.create2_salt.bytes)) + @@ -250,15 +292,15 @@ evmc::bytes32 EVMHost::get_block_hash(int64_t _number) const noexcept return convertToEVMC(u256("0x3737373737373737373737373737373737373737373737373737373737373737") + _number); } -Address EVMHost::convertFromEVMC(evmc::address const& _addr) +h160 EVMHost::convertFromEVMC(evmc::address const& _addr) { - return Address(bytes(begin(_addr.bytes), end(_addr.bytes))); + return h160(bytes(begin(_addr.bytes), end(_addr.bytes))); } -evmc::address EVMHost::convertToEVMC(Address const& _addr) +evmc::address EVMHost::convertToEVMC(h160 const& _addr) { evmc::address a; - for (size_t i = 0; i < 20; ++i) + for (unsigned i = 0; i < 20; ++i) a.bytes[i] = _addr[i]; return a; } @@ -271,7 +313,7 @@ h256 EVMHost::convertFromEVMC(evmc::bytes32 const& _data) evmc::bytes32 EVMHost::convertToEVMC(h256 const& _data) { evmc::bytes32 d; - for (size_t i = 0; i < 32; ++i) + for (unsigned i = 0; i < 32; ++i) d.bytes[i] = _data[i]; return d; } diff --git a/test/EVMHost.h b/test/EVMHost.h index af38426be19b..768c20659fc2 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -30,6 +30,8 @@ #include +#include + namespace solidity::test { using Address = util::h160; @@ -40,14 +42,19 @@ class EVMHost: public evmc::MockedHost using MockedHost::get_code_size; using MockedHost::get_balance; - /// Tries to dynamically load libevmone. @returns nullptr on failure. - /// The path has to be provided for the first successful run and will be ignored - /// afterwards. + /// Tries to dynamically load an evmc vm supporting evm1 or ewasm and caches the loaded VM. + /// @returns vmc::VM(nullptr) on failure. static evmc::VM& getVM(std::string const& _path = {}); - explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); + /// Tries to load all defined evmc vm shared libraries. + /// @param _vmPaths paths to multiple evmc shared libraries. + /// @throw Exception if multiple evm1 or multiple ewasm evmc vms where loaded. + /// @returns true, if an evmc vm was supporting evm1 loaded properly. + static bool checkVmPaths(std::vector const& _vmPaths); + + explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm); - void reset() { accounts.clear(); m_currentAddress = {}; } + void reset(); void newBlock() { tx_context.block_number++; @@ -71,6 +78,12 @@ class EVMHost: public evmc::MockedHost static util::h256 convertFromEVMC(evmc::bytes32 const& _data); static evmc::bytes32 convertToEVMC(util::h256 const& _data); + /// @returns true, if the evmc VM has the given capability. + bool hasCapability(evmc_capabilities capability) const noexcept + { + return m_vm.has_capability(capability); + } + private: evmc::address m_currentAddress = {}; diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index b3841f6b4d20..1f5bece9035c 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -28,6 +28,9 @@ #include #include +#include + +#include #include #include @@ -40,27 +43,47 @@ using namespace solidity::util; using namespace solidity::test; ExecutionFramework::ExecutionFramework(): - ExecutionFramework(solidity::test::CommonOptions::get().evmVersion()) + ExecutionFramework(solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths) { } -ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion): +ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion, vector const& _vmPaths): m_evmVersion(_evmVersion), m_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()), m_showMessages(solidity::test::CommonOptions::get().showMessages), - m_evmHost(make_shared(m_evmVersion)) + m_vmPaths(_vmPaths) { if (solidity::test::CommonOptions::get().optimize) m_optimiserSettings = solidity::frontend::OptimiserSettings::standard(); + for (auto const& path: m_vmPaths) + if (EVMHost::getVM(path.string()).has_capability(EVMC_CAPABILITY_EWASM)) + m_supportsEwasm = true; + + selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1); +} + +void ExecutionFramework::selectVM(evmc_capabilities _cap) +{ + m_evmcHost.reset(); + for (auto const& path: m_vmPaths) + { + evmc::VM& vm = EVMHost::getVM(path.string()); + if (vm.has_capability(_cap)) + { + m_evmcHost = make_unique(m_evmVersion, vm); + break; + } + } + solAssert(m_evmcHost != nullptr, ""); reset(); } void ExecutionFramework::reset() { - m_evmHost->reset(); + m_evmcHost->reset(); for (size_t i = 0; i < 10; i++) - m_evmHost->accounts[EVMHost::convertToEVMC(account(i))].balance = + m_evmcHost->accounts[EVMHost::convertToEVMC(account(i))].balance = EVMHost::convertToEVMC(u256(1) << 100); } @@ -90,31 +113,42 @@ std::pair ExecutionFramework::compareAndCreateMessage( return make_pair(false, message); } +bytes ExecutionFramework::panicData(util::PanicCode _code) +{ + return + m_evmVersion.supportsReturndata() ? + toCompactBigEndian(selectorFromSignature32("Panic(uint256)"), 4) + encode(u256(_code)) : + bytes(); +} + u256 ExecutionFramework::gasLimit() const { - return {m_evmHost->tx_context.block_gas_limit}; + return {m_evmcHost->tx_context.block_gas_limit}; } u256 ExecutionFramework::gasPrice() const { - return {EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; + // here and below we use "return u256{....}" instead of just "return {....}" + // to please MSVC and avoid unexpected + // warning C4927 : illegal conversion; more than one user - defined conversion has been implicitly applied + return u256{EVMHost::convertFromEVMC(m_evmcHost->tx_context.tx_gas_price)}; } u256 ExecutionFramework::blockHash(u256 const& _number) const { - return {EVMHost::convertFromEVMC( - m_evmHost->get_block_hash(static_cast(_number & numeric_limits::max())) + return u256{EVMHost::convertFromEVMC( + m_evmcHost->get_block_hash(static_cast(_number & numeric_limits::max())) )}; } u256 ExecutionFramework::blockNumber() const { - return m_evmHost->tx_context.block_number; + return m_evmcHost->tx_context.block_number; } void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value) { - m_evmHost->newBlock(); + m_evmcHost->newBlock(); if (m_showMessages) { @@ -135,7 +169,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 if (_isCreation) { message.kind = EVMC_CREATE; - message.destination = EVMHost::convertToEVMC(Address{}); + message.destination = EVMHost::convertToEVMC(h160{}); } else { @@ -144,7 +178,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 } message.gas = m_gas.convert_to(); - evmc::result result = m_evmHost->call(message); + evmc::result result = m_evmcHost->call(message); m_output = bytes(result.output_data, result.output_data + result.output_size); if (_isCreation) @@ -161,9 +195,9 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 } } -void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount) +void ExecutionFramework::sendEther(h160 const& _addr, u256 const& _amount) { - m_evmHost->newBlock(); + m_evmcHost->newBlock(); if (m_showMessages) { @@ -178,12 +212,12 @@ void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount) message.destination = EVMHost::convertToEVMC(_addr); message.gas = m_gas.convert_to(); - m_evmHost->call(message); + m_evmcHost->call(message); } size_t ExecutionFramework::currentTimestamp() { - return static_cast(m_evmHost->tx_context.block_timestamp); + return static_cast(m_evmcHost->tx_context.block_timestamp); } size_t ExecutionFramework::blockTimestamp(u256 _block) @@ -194,53 +228,53 @@ size_t ExecutionFramework::blockTimestamp(u256 _block) return static_cast((currentTimestamp() / blockNumber()) * _block); } -Address ExecutionFramework::account(size_t _idx) +h160 ExecutionFramework::account(size_t _idx) { - return Address(h256(u256{"0x1212121212121212121212121212120000000012"} + _idx * 0x1000), Address::AlignRight); + return h160(h256(u256{"0x1212121212121212121212121212120000000012"} + _idx * 0x1000), h160::AlignRight); } -bool ExecutionFramework::addressHasCode(Address const& _addr) +bool ExecutionFramework::addressHasCode(h160 const& _addr) { - return m_evmHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0; + return m_evmcHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0; } size_t ExecutionFramework::numLogs() const { - return m_evmHost->recorded_logs.size(); + return m_evmcHost->recorded_logs.size(); } size_t ExecutionFramework::numLogTopics(size_t _logIdx) const { - return m_evmHost->recorded_logs.at(_logIdx).topics.size(); + return m_evmcHost->recorded_logs.at(_logIdx).topics.size(); } h256 ExecutionFramework::logTopic(size_t _logIdx, size_t _topicIdx) const { - return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).topics.at(_topicIdx)); + return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).topics.at(_topicIdx)); } -Address ExecutionFramework::logAddress(size_t _logIdx) const +h160 ExecutionFramework::logAddress(size_t _logIdx) const { - return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).creator); + return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).creator); } bytes ExecutionFramework::logData(size_t _logIdx) const { - const auto& data = m_evmHost->recorded_logs.at(_logIdx).data; + const auto& data = m_evmcHost->recorded_logs.at(_logIdx).data; // TODO: Return a copy of log data, because this is expected from REQUIRE_LOG_DATA(), // but reference type like string_view would be preferable. return {data.begin(), data.end()}; } -u256 ExecutionFramework::balanceAt(Address const& _addr) +u256 ExecutionFramework::balanceAt(h160 const& _addr) { - return u256(EVMHost::convertFromEVMC(m_evmHost->get_balance(EVMHost::convertToEVMC(_addr)))); + return u256(EVMHost::convertFromEVMC(m_evmcHost->get_balance(EVMHost::convertToEVMC(_addr)))); } -bool ExecutionFramework::storageEmpty(Address const& _addr) +bool ExecutionFramework::storageEmpty(h160 const& _addr) { - const auto it = m_evmHost->accounts.find(EVMHost::convertToEVMC(_addr)); - if (it != m_evmHost->accounts.end()) + const auto it = m_evmcHost->accounts.find(EVMHost::convertToEVMC(_addr)); + if (it != m_evmcHost->accounts.end()) { for (auto const& entry: it->second.storage) if (!(entry.second.value == evmc::bytes32{})) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 425f0ee1a3a2..b4e3fd560439 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include @@ -32,6 +33,7 @@ #include #include +#include #include @@ -39,12 +41,7 @@ namespace solidity::test { -class EVMHost; - using rational = boost::rational; -/// An Ethereum address: 20 bytes. -/// @NOTE This is not endian-specific; it's just a bunch of bytes. -using Address = util::h160; // The various denominations; here for ease of use where needed within code. static const u256 sun = 1; @@ -55,7 +52,7 @@ class ExecutionFramework public: ExecutionFramework(); - explicit ExecutionFramework(langutil::EVMVersion _evmVersion); + ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector const& _vmPaths); virtual ~ExecutionFramework() = default; virtual bytes const& compileAndRunWithoutCheck( @@ -63,7 +60,7 @@ class ExecutionFramework u256 const& _value = 0, std::string const& _contractName = "", bytes const& _arguments = {}, - std::map const& _libraryAddresses = {} + std::map const& _libraryAddresses = {} ) = 0; bytes const& compileAndRun( @@ -71,7 +68,7 @@ class ExecutionFramework u256 const& _value = 0, std::string const& _contractName = "", bytes const& _arguments = {}, - std::map const& _libraryAddresses = {} + std::map const& _libraryAddresses = {} ) { compileAndRunWithoutCheck( @@ -177,6 +174,7 @@ class ExecutionFramework return encode(u256((value.numerator() << fractionalBits) / value.denominator())); } static bytes encode(util::h256 const& _value) { return _value.asBytes(); } + static bytes encode(util::h160 const& _value) { return encode(util::h256(_value, util::h256::AlignRight)); } static bytes encode(bytes const& _value, bool _padLeft = true) { bytes padding = bytes((32 - _value.size() % 32) % 32, 0); @@ -201,6 +199,9 @@ class ExecutionFramework { return bytes(); } + /// @returns error returndata corresponding to the Panic(uint256) error code, + /// if REVERT is supported by the current EVM version and the empty string otherwise. + bytes panicData(util::PanicCode _code); //@todo might be extended in the future template @@ -255,35 +256,39 @@ class ExecutionFramework } protected: + void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1); void reset(); void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0); - void sendEther(Address const& _to, u256 const& _value); + void sendEther(util::h160 const& _to, u256 const& _value); size_t currentTimestamp(); size_t blockTimestamp(u256 _number); /// @returns the (potentially newly created) _ith address. - Address account(size_t _i); + util::h160 account(size_t _i); - u256 balanceAt(Address const& _addr); - bool storageEmpty(Address const& _addr); - bool addressHasCode(Address const& _addr); + u256 balanceAt(util::h160 const& _addr); + bool storageEmpty(util::h160 const& _addr); + bool addressHasCode(util::h160 const& _addr); size_t numLogs() const; size_t numLogTopics(size_t _logIdx) const; util::h256 logTopic(size_t _logIdx, size_t _topicIdx) const; - Address logAddress(size_t _logIdx) const; + util::h160 logAddress(size_t _logIdx) const; bytes logData(size_t _logIdx) const; langutil::EVMVersion m_evmVersion; solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default; solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal(); bool m_showMessages = false; - std::shared_ptr m_evmHost; + bool m_supportsEwasm = false; + std::unique_ptr m_evmcHost; + + std::vector m_vmPaths; bool m_transactionSuccessful = true; - Address m_sender = account(0); - Address m_contractAddress; + util::h160 m_sender = account(0); + util::h160 m_contractAddress; u256 m_blockNumber; u256 const m_gasPrice = 10000 * sun; u256 const m_gas = 100000000; diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 7ecc8de5e667..2daf716aa772 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/test/Metadata.cpp b/test/Metadata.cpp index f70b8ea5f2dc..61654ae00611 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -34,7 +34,7 @@ namespace solidity::test bytes onlyMetadata(bytes const& _bytecode) { - unsigned size = _bytecode.size(); + size_t size = _bytecode.size(); if (size < 5) return bytes{}; size_t metadataSize = (static_cast(_bytecode[size - 2]) << 8ul) + static_cast(_bytecode[size - 1]); @@ -49,10 +49,10 @@ bytes onlyMetadata(bytes const& _bytecode) bytes bytecodeSansMetadata(bytes const& _bytecode) { - unsigned metadataSize = onlyMetadata(_bytecode).size(); + size_t metadataSize = onlyMetadata(_bytecode).size(); if (metadataSize == 0) return bytes{}; - return bytes(_bytecode.begin(), _bytecode.end() - metadataSize - 2); + return bytes(_bytecode.begin(), _bytecode.end() - static_cast(metadataSize) - 2); } string bytecodeSansMetadata(string const& _bytecode) diff --git a/test/TestCase.h b/test/TestCase.h index 1815409fe018..65cc4eeb6a1d 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -39,6 +39,7 @@ class TestCase { std::string filename; langutil::EVMVersion evmVersion; + std::vector vmPaths; bool enforceCompileViaYul; }; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 8f065a7bb5dd..36620337e51e 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -72,7 +71,7 @@ int registerTests( { int numTestsAdded = 0; fs::path fullpath = _basepath / _path; - TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul}; + TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths, _enforceViaYul}; if (fs::is_directory(fullpath)) { test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); @@ -149,6 +148,10 @@ void initializeOptions() } } +// TODO: Prototype -- why isn't this declared in the boost headers? +// TODO: replace this with a (global) fixture. +test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ); + test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); @@ -156,14 +159,19 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) initializeOptions(); - bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string()); - if (disableSemantics) + bool disableSemantics = true; + try { - cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath ." << endl; - cout << "You can download it at" << endl; - cout << solidity::test::evmoneDownloadLink << endl; - cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + disableSemantics = !solidity::test::EVMHost::checkVmPaths(solidity::test::CommonOptions::get().vmPaths); + } + catch (std::runtime_error const& _exception) + { + cerr << "Error: " << _exception.what() << endl; + exit(1); } + if (disableSemantics) + cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + // Include the interactive tests in the automatic tests as well for (auto const& ts: g_interactiveTestsuites) { @@ -201,9 +209,6 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) removeTestSuite(suite); } - if (solidity::test::CommonOptions::get().disableSMT) - removeTestSuite("SMTChecker"); - return nullptr; } diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 448ba5a6e5ef..2dedbafe7a1b 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -5,7 +5,7 @@ # # The documentation for solidity is hosted at: # -# https://solidity.readthedocs.org +# https://docs.soliditylang.org # # ------------------------------------------------------------------------------ # This file is part of solidity. @@ -112,18 +112,34 @@ function test_solc_behaviour() sed -i.bak -e 's/{[^{]*Warning: This is a pre-release compiler version[^}]*},\{0,1\}//' "$stdout_path" sed -i.bak -E -e 's/ Consider adding \\"pragma solidity \^[0-9.]*;\\"//g' "$stdout_path" sed -i.bak -e 's/"errors":\[\],\{0,1\}//' "$stdout_path" - # Remove explicit bytecode and references to bytecode offsets - sed -i.bak -E -e 's/\"object\":\"[a-f0-9]+\"/\"object\":\"bytecode removed\"/g' "$stdout_path" - sed -i.bak -E -e 's/\"opcodes\":\"[^"]+\"/\"opcodes\":\"opcodes removed\"/g' "$stdout_path" - sed -i.bak -E -e 's/\"sourceMap\":\"[0-9:;-]+\"/\"sourceMap\":\"sourceMap removed\"/g' "$stdout_path" + sed -i.bak -E -e 's/\"opcodes\":\"[^"]+\"/\"opcodes\":\"\"/g' "$stdout_path" + sed -i.bak -E -e 's/\"sourceMap\":\"[0-9:;-]+\"/\"sourceMap\":\"\"/g' "$stdout_path" + + # Remove bytecode (but not linker references). + sed -i.bak -E -e 's/(\"object\":\")[0-9a-f]+([^"]*\")/\1\2/g' "$stdout_path" + sed -i.bak -E -e 's/(\"object\":\"[^"]+\$__)[0-9a-f]+(\")/\1\2/g' "$stdout_path" + sed -i.bak -E -e 's/([0-9a-f]{34}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1\2/g' "$stdout_path" + # Replace escaped newlines by actual newlines for readability sed -i.bak -E -e 's/\\n/\'$'\n/g' "$stdout_path" rm "$stdout_path.bak" else sed -i.bak -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" sed -i.bak -e '/^Warning (3805): This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" - sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1AUXDATA REMOVED/' "$stdout_path" + sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1/' "$stdout_path" sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path" + sed -i.bak -e 's/\(Unimplemented feature error.* in \).*$/\1/' "$stderr_path" + sed -i.bak -e 's/"version": "[^"]*"/"version": ""/' "$stdout_path" + + # Remove bytecode (but not linker references). Since non-JSON output is unstructured, + # use metadata markers for detection to have some confidence that it's actually bytecode + # and not some random word. + # 64697066735822 = hex encoding of 0x64 'i' 'p' 'f' 's' 0x58 0x22 + # 64736f6c63 = hex encoding of 0x64 's' 'o' 'l' 'c' + sed -i.bak -E -e 's/[0-9a-f]*64697066735822[0-9a-f]+64736f6c63[0-9a-f]+//g' "$stdout_path" + sed -i.bak -E -e 's/([0-9a-f]{17}\$__)[0-9a-f]+(__\$[0-9a-f]{17})/\1\2/g' "$stdout_path" + sed -i.bak -E -e 's/[0-9a-f]+((__\$[0-9a-f]{34}\$__)*)/\1/g' "$stdout_path" + # Remove trailing empty lines. Needs a line break to make OSX sed happy. sed -i.bak -e '1{/^$/d }' "$stderr_path" @@ -190,7 +206,7 @@ function test_solc_assembly_output() local expected_object="object \"object\" { code "${expected}" }" output=$(echo "${input}" | "$SOLC" - ${solc_args} 2>/dev/null) - empty=$(echo $output | sed -ne '/'"${expected_object}"'/p') + empty=$(echo "$output" | tr '\n' ' ' | tr -s ' ' | sed -ne "/${expected_object}/p") if [ -z "$empty" ] then printError "Incorrect assembly output. Expected: " @@ -238,26 +254,36 @@ printTask "Running general commandline tests..." cd "$REPO_ROOT"/test/cmdlineTests/ for tdir in */ do - if [ -e "${tdir}/input.json" ] + printTask " - ${tdir}" + + # Strip trailing slash from $tdir. + tdir=$(basename "${tdir}") + + inputFiles="$(ls -1 ${tdir}/input.* 2> /dev/null || true)" + inputCount="$(echo ${inputFiles} | wc -w)" + if (( ${inputCount} > 1 )) + then + printError "Ambiguous input. Found input files in multiple formats:" + echo -e "${inputFiles}" + exit 1 + fi + + # Use printf to get rid of the trailing newline + inputFile=$(printf "%s" "${inputFiles}") + + # If no files specified, assume input.sol as the default + if [ -z "${inputFile}" ]; then + inputFile="${tdir}/input.sol" + fi + + if [ "${inputFile}" = "${tdir}/input.json" ] then + stdin="${inputFile}" inputFile="" - stdin="${tdir}/input.json" stdout="$(cat ${tdir}/output.json 2>/dev/null || true)" stdoutExpectationFile="${tdir}/output.json" args="--standard-json "$(cat ${tdir}/args 2>/dev/null || true) else - if [[ -e "${tdir}input.yul" && -e "${tdir}input.sol" ]] - then - printError "Ambiguous input. Found both input.sol and input.yul." - exit 1 - fi - - if [ -e "${tdir}input.yul" ] - then - inputFile="${tdir}input.yul" - else - inputFile="${tdir}input.sol" - fi stdin="" stdout="$(cat ${tdir}/output 2>/dev/null || true)" stdoutExpectationFile="${tdir}/output" @@ -266,7 +292,6 @@ printTask "Running general commandline tests..." exitCode=$(cat ${tdir}/exit 2>/dev/null || true) err="$(cat ${tdir}/err 2>/dev/null || true)" stderrExpectationFile="${tdir}/err" - printTask " - ${tdir}" test_solc_behaviour "$inputFile" \ "$args" \ "$stdin" \ @@ -411,15 +436,18 @@ SOLTMPDIR=$(mktemp -d) # The contract should be compiled if [[ "$result" != 0 ]] then + printError "Failed to compile a simple contract from standard input" exit 1 fi # This should not fail set +e - output=$(echo '' | "$SOLC" --ast - 2>/dev/null) + output=$(echo '' | "$SOLC" --ast-json - 2>/dev/null) + result=$? set -e - if [[ $? != 0 ]] + if [[ $result != 0 ]] then + printError "Incorrect response to --ast-json option with empty stdin" exit 1 fi ) @@ -437,6 +465,9 @@ SOLTMPDIR=$(mktemp -d) ) rm -rf "$SOLTMPDIR" +printTask "Testing AST export with stop-after=parsing..." +"$REPO_ROOT/test/stopAfterParseTests.sh" + printTask "Testing soljson via the fuzzer..." SOLTMPDIR=$(mktemp -d) ( diff --git a/test/cmdlineTests/abiencoderv2_no_warning/input.sol b/test/cmdlineTests/abiencoderv2_no_warning/input.sol index c2eb120f8304..5a1dc985b4bd 100644 --- a/test/cmdlineTests/abiencoderv2_no_warning/input.sol +++ b/test/cmdlineTests/abiencoderv2_no_warning/input.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.0; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint x; } diff --git a/test/cmdlineTests/combined_json_generated_sources/args b/test/cmdlineTests/combined_json_generated_sources/args new file mode 100644 index 000000000000..425ca0a0ba75 --- /dev/null +++ b/test/cmdlineTests/combined_json_generated_sources/args @@ -0,0 +1 @@ +--combined-json generated-sources,generated-sources-runtime --pretty-json diff --git a/test/cmdlineTests/combined_json_generated_sources/err b/test/cmdlineTests/combined_json_generated_sources/err new file mode 100644 index 000000000000..c6113508bf92 --- /dev/null +++ b/test/cmdlineTests/combined_json_generated_sources/err @@ -0,0 +1,5 @@ +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +--> combined_json_generated_sources/input.sol + +Warning: Source file does not specify required compiler version! +--> combined_json_generated_sources/input.sol diff --git a/test/cmdlineTests/combined_json_generated_sources/exit b/test/cmdlineTests/combined_json_generated_sources/exit new file mode 100644 index 000000000000..c227083464fb --- /dev/null +++ b/test/cmdlineTests/combined_json_generated_sources/exit @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/test/cmdlineTests/combined_json_generated_sources/input.sol b/test/cmdlineTests/combined_json_generated_sources/input.sol new file mode 100644 index 000000000000..b08878fb1cdd --- /dev/null +++ b/test/cmdlineTests/combined_json_generated_sources/input.sol @@ -0,0 +1,5 @@ +pragma abicoder v2; + +contract C { + function f(uint[] calldata) pure external {} +} diff --git a/test/cmdlineTests/combined_json_generated_sources/output b/test/cmdlineTests/combined_json_generated_sources/output new file mode 100644 index 000000000000..cd8272f8e143 --- /dev/null +++ b/test/cmdlineTests/combined_json_generated_sources/output @@ -0,0 +1,735 @@ +{ + "contracts": + { + "combined_json_generated_sources/input.sol:C": + { + "generated-sources": [], + "generated-sources-runtime": + [ + { + "ast": + { + "nodeType": "YulBlock", + "src": "0:825:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "114:277:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "163:16:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "172:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "175:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "165:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "165:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "165:12:1" + } + ] + }, + "condition": + { + "arguments": + [ + { + "arguments": + [ + { + "arguments": + [ + { + "name": "offset", + "nodeType": "YulIdentifier", + "src": "142:6:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "150:4:1", + "type": "", + "value": "0x1f" + } + ], + "functionName": + { + "name": "add", + "nodeType": "YulIdentifier", + "src": "138:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "138:17:1" + }, + { + "name": "end", + "nodeType": "YulIdentifier", + "src": "157:3:1" + } + ], + "functionName": + { + "name": "slt", + "nodeType": "YulIdentifier", + "src": "134:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "134:27:1" + } + ], + "functionName": + { + "name": "iszero", + "nodeType": "YulIdentifier", + "src": "127:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "127:35:1" + }, + "nodeType": "YulIf", + "src": "124:2:1" + }, + { + "nodeType": "YulAssignment", + "src": "188:30:1", + "value": + { + "arguments": + [ + { + "name": "offset", + "nodeType": "YulIdentifier", + "src": "211:6:1" + } + ], + "functionName": + { + "name": "calldataload", + "nodeType": "YulIdentifier", + "src": "198:12:1" + }, + "nodeType": "YulFunctionCall", + "src": "198:20:1" + }, + "variableNames": + [ + { + "name": "length", + "nodeType": "YulIdentifier", + "src": "188:6:1" + } + ] + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "261:16:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "270:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "273:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "263:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "263:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "263:12:1" + } + ] + }, + "condition": + { + "arguments": + [ + { + "name": "length", + "nodeType": "YulIdentifier", + "src": "233:6:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "241:18:1", + "type": "", + "value": "0xffffffffffffffff" + } + ], + "functionName": + { + "name": "gt", + "nodeType": "YulIdentifier", + "src": "230:2:1" + }, + "nodeType": "YulFunctionCall", + "src": "230:30:1" + }, + "nodeType": "YulIf", + "src": "227:2:1" + }, + { + "nodeType": "YulAssignment", + "src": "286:29:1", + "value": + { + "arguments": + [ + { + "name": "offset", + "nodeType": "YulIdentifier", + "src": "302:6:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "310:4:1", + "type": "", + "value": "0x20" + } + ], + "functionName": + { + "name": "add", + "nodeType": "YulIdentifier", + "src": "298:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "298:17:1" + }, + "variableNames": + [ + { + "name": "arrayPos", + "nodeType": "YulIdentifier", + "src": "286:8:1" + } + ] + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "369:16:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "378:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "381:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "371:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "371:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "371:12:1" + } + ] + }, + "condition": + { + "arguments": + [ + { + "arguments": + [ + { + "name": "arrayPos", + "nodeType": "YulIdentifier", + "src": "334:8:1" + }, + { + "arguments": + [ + { + "name": "length", + "nodeType": "YulIdentifier", + "src": "348:6:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "356:4:1", + "type": "", + "value": "0x20" + } + ], + "functionName": + { + "name": "mul", + "nodeType": "YulIdentifier", + "src": "344:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "344:17:1" + } + ], + "functionName": + { + "name": "add", + "nodeType": "YulIdentifier", + "src": "330:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "330:32:1" + }, + { + "name": "end", + "nodeType": "YulIdentifier", + "src": "364:3:1" + } + ], + "functionName": + { + "name": "gt", + "nodeType": "YulIdentifier", + "src": "327:2:1" + }, + "nodeType": "YulFunctionCall", + "src": "327:41:1" + }, + "nodeType": "YulIf", + "src": "324:2:1" + } + ] + }, + "name": "abi_decode_t_array$_t_uint256_$dyn_calldata_ptr", + "nodeType": "YulFunctionDefinition", + "parameters": + [ + { + "name": "offset", + "nodeType": "YulTypedName", + "src": "81:6:1", + "type": "" + }, + { + "name": "end", + "nodeType": "YulTypedName", + "src": "89:3:1", + "type": "" + } + ], + "returnVariables": + [ + { + "name": "arrayPos", + "nodeType": "YulTypedName", + "src": "97:8:1", + "type": "" + }, + { + "name": "length", + "nodeType": "YulTypedName", + "src": "107:6:1", + "type": "" + } + ], + "src": "24:367:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "498:324:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "544:16:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "553:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "556:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "546:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "546:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "546:12:1" + } + ] + }, + "condition": + { + "arguments": + [ + { + "arguments": + [ + { + "name": "dataEnd", + "nodeType": "YulIdentifier", + "src": "519:7:1" + }, + { + "name": "headStart", + "nodeType": "YulIdentifier", + "src": "528:9:1" + } + ], + "functionName": + { + "name": "sub", + "nodeType": "YulIdentifier", + "src": "515:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "515:23:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "540:2:1", + "type": "", + "value": "32" + } + ], + "functionName": + { + "name": "slt", + "nodeType": "YulIdentifier", + "src": "511:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "511:32:1" + }, + "nodeType": "YulIf", + "src": "508:2:1" + }, + { + "nodeType": "YulBlock", + "src": "570:245:1", + "statements": + [ + { + "nodeType": "YulVariableDeclaration", + "src": "585:45:1", + "value": + { + "arguments": + [ + { + "arguments": + [ + { + "name": "headStart", + "nodeType": "YulIdentifier", + "src": "616:9:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "627:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "add", + "nodeType": "YulIdentifier", + "src": "612:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "612:17:1" + } + ], + "functionName": + { + "name": "calldataload", + "nodeType": "YulIdentifier", + "src": "599:12:1" + }, + "nodeType": "YulFunctionCall", + "src": "599:31:1" + }, + "variables": + [ + { + "name": "offset", + "nodeType": "YulTypedName", + "src": "589:6:1", + "type": "" + } + ] + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "677:16:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "686:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "689:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "679:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "679:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "679:12:1" + } + ] + }, + "condition": + { + "arguments": + [ + { + "name": "offset", + "nodeType": "YulIdentifier", + "src": "649:6:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "657:18:1", + "type": "", + "value": "0xffffffffffffffff" + } + ], + "functionName": + { + "name": "gt", + "nodeType": "YulIdentifier", + "src": "646:2:1" + }, + "nodeType": "YulFunctionCall", + "src": "646:30:1" + }, + "nodeType": "YulIf", + "src": "643:2:1" + }, + { + "nodeType": "YulAssignment", + "src": "707:98:1", + "value": + { + "arguments": + [ + { + "arguments": + [ + { + "name": "headStart", + "nodeType": "YulIdentifier", + "src": "777:9:1" + }, + { + "name": "offset", + "nodeType": "YulIdentifier", + "src": "788:6:1" + } + ], + "functionName": + { + "name": "add", + "nodeType": "YulIdentifier", + "src": "773:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "773:22:1" + }, + { + "name": "dataEnd", + "nodeType": "YulIdentifier", + "src": "797:7:1" + } + ], + "functionName": + { + "name": "abi_decode_t_array$_t_uint256_$dyn_calldata_ptr", + "nodeType": "YulIdentifier", + "src": "725:47:1" + }, + "nodeType": "YulFunctionCall", + "src": "725:80:1" + }, + "variableNames": + [ + { + "name": "value0", + "nodeType": "YulIdentifier", + "src": "707:6:1" + }, + { + "name": "value1", + "nodeType": "YulIdentifier", + "src": "715:6:1" + } + ] + } + ] + } + ] + }, + "name": "abi_decode_tuple_t_array$_t_uint256_$dyn_calldata_ptr", + "nodeType": "YulFunctionDefinition", + "parameters": + [ + { + "name": "headStart", + "nodeType": "YulTypedName", + "src": "460:9:1", + "type": "" + }, + { + "name": "dataEnd", + "nodeType": "YulTypedName", + "src": "471:7:1", + "type": "" + } + ], + "returnVariables": + [ + { + "name": "value0", + "nodeType": "YulTypedName", + "src": "483:6:1", + "type": "" + }, + { + "name": "value1", + "nodeType": "YulTypedName", + "src": "491:6:1", + "type": "" + } + ], + "src": "397:425:1" + } + ] + }, + "contents": "{\n\n // uint256[]\n function abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(offset, end) -> arrayPos, length {\n if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }\n length := calldataload(offset)\n if gt(length, 0xffffffffffffffff) { revert(0, 0) }\n arrayPos := add(offset, 0x20)\n if gt(add(arrayPos, mul(length, 0x20)), end) { revert(0, 0) }\n }\n\n function abi_decode_tuple_t_array$_t_uint256_$dyn_calldata_ptr(headStart, dataEnd) -> value0, value1 {\n if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }\n\n {\n\n let offset := calldataload(add(headStart, 0))\n if gt(offset, 0xffffffffffffffff) { revert(0, 0) }\n\n value0, value1 := abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(add(headStart, offset), dataEnd)\n }\n\n }\n\n}\n", + "id": 1, + "language": "Yul", + "name": "#utility.yul" + } + ] + } + }, + "version": "" +} diff --git a/test/cmdlineTests/dup_opt_peephole/output b/test/cmdlineTests/dup_opt_peephole/output index 020260c2b39e..fec8624282c1 100644 --- a/test/cmdlineTests/dup_opt_peephole/output +++ b/test/cmdlineTests/dup_opt_peephole/output @@ -50,5 +50,5 @@ sub_0: assembly { /* "dup_opt_peephole/input.sol":0:111 contract C {... */ stop - auxdata: AUXDATA REMOVED + auxdata: } diff --git a/test/cmdlineTests/evm_to_wasm/input.sol b/test/cmdlineTests/evm_to_wasm/input.yul similarity index 100% rename from test/cmdlineTests/evm_to_wasm/input.sol rename to test/cmdlineTests/evm_to_wasm/input.yul diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index 11b63c6a2cfc..772149b0a3fa 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -1,5 +1,5 @@ -======= evm_to_wasm/input.sol (Ewasm) ======= +======= evm_to_wasm/input.yul (Ewasm) ======= Pretty printed source: object "object" { @@ -19,26 +19,26 @@ object "object" { mstore_internal(32:i32, _1, _1, _1, 1) eth.storageStore(0:i32, 32:i32) } - function endian_swap_16(x) -> y + function bswap16(x) -> y { y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) } - function endian_swap_32(x) -> y + function bswap32(x) -> y { - let hi := i64.shl(endian_swap_16(x), 16) - y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16))) + let hi := i64.shl(bswap16(x), 16) + y := i64.or(hi, bswap16(i64.shr_u(x, 16))) } - function endian_swap(x) -> y + function bswap64(x) -> y { - let hi := i64.shl(endian_swap_32(x), 32) - y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) + let hi := i64.shl(bswap32(x), 32) + y := i64.or(hi, bswap32(i64.shr_u(x, 32))) } function mstore_internal(pos:i32, y1, y2, y3, y4) { - i64.store(pos, endian_swap(y1)) - i64.store(i32.add(pos, 8:i32), endian_swap(y2)) - i64.store(i32.add(pos, 16:i32), endian_swap(y3)) - i64.store(i32.add(pos, 24:i32), endian_swap(y4)) + i64.store(pos, bswap64(y1)) + i64.store(i32.add(pos, 8:i32), bswap64(y2)) + i64.store(i32.add(pos, 16:i32), bswap64(y3)) + i64.store(i32.add(pos, 24:i32), bswap64(y4)) } } } @@ -63,7 +63,7 @@ Text representation: ) ) -(func $endian_swap_16 +(func $bswap16 (param $x i64) (result i64) (local $y i64) @@ -74,27 +74,27 @@ Text representation: (local.get $y) ) -(func $endian_swap_32 +(func $bswap32 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__2 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (local.set $hi (i64.shl (call $bswap16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $bswap16 (i64.shr_u (local.get $x) (i64.const 16))))) ) (local.get $y) ) -(func $endian_swap +(func $bswap64 (param $x i64) (result i64) (local $y i64) (local $hi i64) (block $label__3 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + (local.set $hi (i64.shl (call $bswap32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $bswap32 (i64.shr_u (local.get $x) (i64.const 32))))) ) (local.get $y) @@ -107,10 +107,10 @@ Text representation: (param $y3 i64) (param $y4 i64) (block $label__4 - (i64.store (local.get $pos) (call $endian_swap (local.get $y1))) - (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $endian_swap (local.get $y2))) - (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $endian_swap (local.get $y3))) - (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $endian_swap (local.get $y4))) + (i64.store (local.get $pos) (call $bswap64 (local.get $y1))) + (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $bswap64 (local.get $y2))) + (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $bswap64 (local.get $y3))) + (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $bswap64 (local.get $y4))) ) ) diff --git a/test/cmdlineTests/evm_to_wasm_break/input.sol b/test/cmdlineTests/evm_to_wasm_break/input.yul similarity index 100% rename from test/cmdlineTests/evm_to_wasm_break/input.sol rename to test/cmdlineTests/evm_to_wasm_break/input.yul diff --git a/test/cmdlineTests/evm_to_wasm_break/output b/test/cmdlineTests/evm_to_wasm_break/output index ddeb0452905b..88212dd94004 100644 --- a/test/cmdlineTests/evm_to_wasm_break/output +++ b/test/cmdlineTests/evm_to_wasm_break/output @@ -1,5 +1,5 @@ -======= evm_to_wasm_break/input.sol (Ewasm) ======= +======= evm_to_wasm_break/input.yul (Ewasm) ======= Pretty printed source: object "object" { @@ -42,13 +42,12 @@ object "object" { x_7 := x_11 } { - let _4, _5, _6, _7 := lt(x_4, x_5, x_6, x_7, _1, _1, _1, 10) - let _8, _9, _10, _11 := iszero(_4, _5, _6, _7) + let _4, _5, _6, _7 := iszero_200_872_1499(_1, _1, _1, lt_202(x_4, x_5, x_6, x_7, _1, _1, _1, 10)) + if i32.eqz(i64.eqz(i64.or(i64.or(_4, _5), i64.or(_6, _7)))) { break } + let _8, _9, _10, _11 := eq_201_873_1500(x_4, x_5, x_6, x_7, _1, _1, _1, 2) if i32.eqz(i64.eqz(i64.or(i64.or(_8, _9), i64.or(_10, _11)))) { break } - let _12, _13, _14, _15 := eq(x_4, x_5, x_6, x_7, _1, _1, _1, 2) - if i32.eqz(i64.eqz(i64.or(i64.or(_12, _13), i64.or(_14, _15)))) { break } - let _16, _17, _18, _19 := eq(x_4, x_5, x_6, x_7, _1, _1, _1, 4) - if i32.eqz(i64.eqz(i64.or(i64.or(_16, _17), i64.or(_18, _19)))) { continue } + let _12, _13, _14, _15 := eq_201_873_1500(x_4, x_5, x_6, x_7, _1, _1, _1, 4) + if i32.eqz(i64.eqz(i64.or(i64.or(_12, _13), i64.or(_14, _15)))) { continue } } sstore(_1, _1, _1, _1, x_4, x_5, x_6, x_7) } @@ -69,34 +68,23 @@ object "object" { let r1_1, carry_2 := add_carry(x1, y1, carry_1) r1 := r1_1 } - function iszero(x1, x2, x3, x4) -> r1, r2, r3, r4 + function iszero_200_872_1499(x1, x2, x3, x4) -> r1, r2, r3, r4 { r4 := i64.extend_i32_u(i64.eqz(i64.or(i64.or(x1, x2), i64.or(x3, x4)))) } - function eq(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 + function eq_201_873_1500(x1, x2, x3, x4, y1, y2, y3, y4) -> r1, r2, r3, r4 { - if i64.eq(x1, y1) - { - if i64.eq(x2, y2) - { - if i64.eq(x3, y3) { if i64.eq(x4, y4) { r4 := 1 } } - } - } + r4 := i64.extend_i32_u(i32.and(i64.eq(x1, y1), i32.and(i64.eq(x2, y2), i32.and(i64.eq(x3, y3), i64.eq(x4, y4))))) } - function cmp(a, b) -> r:i32 - { - switch i64.lt_u(a, b) - case 1:i32 { r := 0xffffffff:i32 } - default { r := i64.ne(a, b) } - } - function lt(x1, x2, x3, x4, y1, y2, y3, y4) -> z1, z2, z3, z4 + function lt_202(x1, x2, x3, x4, y1, y2, y3, y4) -> z4 { let z:i32 := false - switch cmp(x1, y1) + let _1:i32 := 0xffffffff:i32 + switch i32.select(_1, i64.ne(x1, y1), i64.lt_u(x1, y1)) case 0:i32 { - switch cmp(x2, y2) + switch i32.select(_1, i64.ne(x2, y2), i64.lt_u(x2, y2)) case 0:i32 { - switch cmp(x3, y3) + switch i32.select(_1, i64.ne(x3, y3), i64.lt_u(x3, y3)) case 0:i32 { z := i64.lt_u(x4, y4) } case 1:i32 { z := 0:i32 } default { z := 1:i32 } @@ -108,40 +96,66 @@ object "object" { default { z := 1:i32 } z4 := i64.extend_i32_u(z) } - function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 + function u256_to_i32(x1, x2, x3, x4) -> v:i32 { if i64.ne(0, i64.or(i64.or(x1, x2), x3)) { unreachable() } if i64.ne(0, i64.shr_u(x4, 32)) { unreachable() } - eth.callDataCopy(0:i32, i32.wrap_i64(x4), 32:i32) - let z1_1 := endian_swap(i64.load(0:i32)) - let z2_1 := endian_swap(i64.load(i32.add(0:i32, 8:i32))) - let z3_1 := endian_swap(i64.load(i32.add(0:i32, 16:i32))) - let z4_1 := endian_swap(i64.load(i32.add(0:i32, 24:i32))) - z1 := z1_1 - z2 := z2_1 - z3 := z3_1 - z4 := z4_1 + v := i32.wrap_i64(x4) } - function endian_swap_16(x) -> y + function bswap16(x) -> y { y := i64.or(i64.and(i64.shl(x, 8), 0xff00), i64.and(i64.shr_u(x, 8), 0xff)) } - function endian_swap_32(x) -> y + function bswap32(x) -> y { - let hi := i64.shl(endian_swap_16(x), 16) - y := i64.or(hi, endian_swap_16(i64.shr_u(x, 16))) + let hi := i64.shl(bswap16(x), 16) + y := i64.or(hi, bswap16(i64.shr_u(x, 16))) } - function endian_swap(x) -> y + function bswap64(x) -> y { - let hi := i64.shl(endian_swap_32(x), 32) - y := i64.or(hi, endian_swap_32(i64.shr_u(x, 32))) + let hi := i64.shl(bswap32(x), 32) + y := i64.or(hi, bswap32(i64.shr_u(x, 32))) + } + function calldataload(x1, x2, x3, x4) -> z1, z2, z3, z4 + { + let cds:i32 := eth.getCallDataSize() + let _1 := 0 + let destination:i32 := u256_to_i32(_1, _1, _1, _1) + let offset:i32 := u256_to_i32(x1, x2, x3, x4) + let requested_size:i32 := u256_to_i32(_1, _1, _1, 32) + if i32.gt_u(offset, i32.sub(0xffffffff:i32, requested_size)) { eth.revert(0:i32, 0:i32) } + let available_size:i32 := i32.sub(cds, offset) + if i32.gt_u(offset, cds) { available_size := 0:i32 } + let _2:i32 := 0:i32 + if i32.gt_u(available_size, _2) + { + eth.callDataCopy(destination, offset, available_size) + } + if i32.gt_u(requested_size, available_size) + { + let _3:i32 := i32.sub(requested_size, available_size) + let _4:i32 := i32.add(destination, available_size) + let i:i32 := _2 + for { } i32.lt_u(i, _3) { i := i32.add(i, 1:i32) } + { + i32.store8(i32.add(_4, i), _2) + } + } + let z1_1 := bswap64(i64.load(_2)) + let z2_1 := bswap64(i64.load(i32.add(_2, 8:i32))) + let z3_1 := bswap64(i64.load(i32.add(_2, 16:i32))) + let z4_1 := bswap64(i64.load(i32.add(_2, 24:i32))) + z1 := z1_1 + z2 := z2_1 + z3 := z3_1 + z4 := z4_1 } function mstore_internal(pos:i32, y1, y2, y3, y4) { - i64.store(pos, endian_swap(y1)) - i64.store(i32.add(pos, 8:i32), endian_swap(y2)) - i64.store(i32.add(pos, 16:i32), endian_swap(y3)) - i64.store(i32.add(pos, 24:i32), endian_swap(y4)) + i64.store(pos, bswap64(y1)) + i64.store(i32.add(pos, 8:i32), bswap64(y2)) + i64.store(i32.add(pos, 16:i32), bswap64(y3)) + i64.store(i32.add(pos, 24:i32), bswap64(y4)) } function sstore(x1, x2, x3, x4, y1, y2, y3, y4) { @@ -154,11 +168,13 @@ object "object" { Binary representation: -0061736d0100000001480a60000060017e017e60027e7e017f60037e7e7e017e60047e7e7e7e017e60087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60057f7e7e7e7e0060027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003060406020604010101070505030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020ac3080dde02030a7e017f147e02404200210002402000200020002000100921012300210223012103230221040b20012105200221062003210720042108420121092000200084200020098484504545210a02400340200a45450d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f201084201120128484504504400c030b024020052006200720082000200020004202100621132300211423012115230221160b2013201484201520168484504504400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a8484504504400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2901037e0240200020017c2105200520027c21032005200054200320055472ad21040b2004240020030b6c010b7e0240200320077c210c200c42007c210b024020022006200c200354200b200c5472ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2401047e0240200020018420022003848450ad21070b20052400200624012007240220040b3901047e0240200020045104402001200551044020022006510440200320075104404201210b0b0b0b0b0b20092400200a2401200b240220080b2701027f024002402000200154210320034101460440417f210205200020015221020b0b0b20020b960102047e047f02404100210c0240200020041007210d200d41004604400240200120051007210e200e41004604400240200220061007210f200f41004604402003200754210c05200f41014604404100210c054101210c0b0b0b05200e41014604404100210c054101210c0b0b0b05200d41014604404100210c054101210c0b0b0b200cad210b0b20092400200a2401200b240220080b7601087e024042002000200184200284520440000b42002003422088520440000b41002003a7412010014100290000100c2108410041086a290000100c2109410041106a290000100c210a410041186a290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3200024020002001100c370000200041086a2002100c370000200041106a2003100c370000200041186a2004100c3700000b0b2300024041002000200120022003100d41202004200520062007100d4100412010000b0b +0061736d01000000014e0b6000006000017f60017e017e60037e7e7e017e60047e7e7e7e017e60047e7e7e7e017f60087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60057f7e7e7e7e0060027f7f0060037f7f7f00025e0408657468657265756d0c73746f7261676553746f7265000908657468657265756d06726576657274000908657468657265756d0f67657443616c6c4461746153697a65000108657468657265756d0c63616c6c44617461436f7079000a030e0d0003070407070502020204080605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00040abc090dcb02030a7e017f107e02404200210002402000200020002000100e21012300210223012103230221040b20012105200221062003210720042108420121092000200084200020098484504545210a02400340200a45450d01024002402000200020002005200620072008200020002000420a10091007210b2300210c2301210d2302210e0b200b200c84200d200e8484504504400c030b0240200520062007200820002000200042021008210f2300211023012111230221120b200f201084201120128484504504400c030b024020052006200720082000200020004204100821132300211423012115230221160b2013201484201520168484504504400c010b0b0240200520062007200820002000200020091006211723002118230121192302211a0b201721052018210620192107201a21080c000b0b2000200020002000200520062007200810100b0b2901037e0240200020017c2105200520027c21032005200054200320055472ad21040b2004240020030b6c010b7e0240200320077c210c200c42007c210b024020022006200c200354200b200c5472ad1005210d2300210e0b200d210a024020012005200e1005210f230021100b200f2109024020002004201010052111230021120b201121080b20092400200a2401200b240220080b2401047e0240200020018420022003848450ad21070b20052400200624012007240220040b2f01047e02402000200451200120055120022006512003200751717171ad210b0b20092400200a2401200b240220080ba30102017e057f024041002109417f210a0240200a200020045220002004541b210b200b41004604400240200a200120055220012005541b210c200c41004604400240200a200220065220022006541b210d200d41004604402003200754210905200d41014604404100210905410121090b0b0b05200c41014604404100210905410121090b0b0b05200b41014604404100210905410121090b0b0b2009ad21080b20080b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100b421086210220022000421088100b8421010b20010b1e01027e02402000100c422086210220022000422088100c8421010b20010bfc0105047e017f017e087f047e024010022108420021092009200920092009100a210a2000200120022003100a210b2009200920094220100a210c200b417f200c6b4b04404100410010010b2008200b6b210d200b20084b04404100210d0b4100210e200d200e4b0440200a200b200d10030b200c200d4b0440200c200d6b210f200a200d6a2110200e2111024003402011200f49450d010240201020116a200e3a00000b201141016a21110c000b0b0b200e290000100d2112200e41086a290000100d2113200e41106a290000100d2114200e41186a290000100d2115201221042013210520142106201521070b20052400200624012007240220040b3200024020002001100d370000200041086a2002100d370000200041106a2003100d370000200041186a2004100d3700000b0b2300024041002000200120022003100f41202004200520062007100f4100412010000b0b Text representation: (module (import "ethereum" "storageStore" (func $eth.storageStore (param i32 i32))) + (import "ethereum" "revert" (func $eth.revert (param i32 i32))) + (import "ethereum" "getCallDataSize" (func $eth.getCallDataSize (result i32))) (import "ethereum" "callDataCopy" (func $eth.callDataCopy (param i32 i32 i32))) (memory $memory (export "memory") 1) (export "main" (func $main)) @@ -190,10 +206,6 @@ Text representation: (local $_13 i64) (local $_14 i64) (local $_15 i64) - (local $_16 i64) - (local $_17 i64) - (local $_18 i64) - (local $_19 i64) (local $x_8 i64) (local $x_9 i64) (local $x_10 i64) @@ -218,14 +230,17 @@ Text representation: (br_if $label__3 (i32.eqz (i32.eqz (local.get $_3)))) (block $label__4 (block - (local.set $_4 (call $lt (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 10))) + (local.set $_4 (call $iszero_200_872_1499 (local.get $_1) (local.get $_1) (local.get $_1) (call $lt_202 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 10)))) (local.set $_5 (global.get $global_)) (local.set $_6 (global.get $global__1)) (local.set $_7 (global.get $global__2)) ) + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_4) (local.get $_5)) (i64.or (local.get $_6) (local.get $_7))))) (then + (br $label__3) + )) (block - (local.set $_8 (call $iszero (local.get $_4) (local.get $_5) (local.get $_6) (local.get $_7))) + (local.set $_8 (call $eq_201_873_1500 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 2))) (local.set $_9 (global.get $global_)) (local.set $_10 (global.get $global__1)) (local.set $_11 (global.get $global__2)) @@ -235,23 +250,13 @@ Text representation: (br $label__3) )) (block - (local.set $_12 (call $eq (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 2))) + (local.set $_12 (call $eq_201_873_1500 (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 4))) (local.set $_13 (global.get $global_)) (local.set $_14 (global.get $global__1)) (local.set $_15 (global.get $global__2)) ) (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_12) (local.get $_13)) (i64.or (local.get $_14) (local.get $_15))))) (then - (br $label__3) - )) - (block - (local.set $_16 (call $eq (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 4))) - (local.set $_17 (global.get $global_)) - (local.set $_18 (global.get $global__1)) - (local.set $_19 (global.get $global__2)) - - ) - (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_16) (local.get $_17)) (i64.or (local.get $_18) (local.get $_19))))) (then (br $label__4) )) @@ -343,7 +348,7 @@ Text representation: (local.get $r1) ) -(func $iszero +(func $iszero_200_872_1499 (param $x1 i64) (param $x2 i64) (param $x3 i64) @@ -363,7 +368,7 @@ Text representation: (local.get $r1) ) -(func $eq +(func $eq_201_873_1500 (param $x1 i64) (param $x2 i64) (param $x3 i64) @@ -378,15 +383,7 @@ Text representation: (local $r3 i64) (local $r4 i64) (block $label__9 - (if (i64.eq (local.get $x1) (local.get $y1)) (then - (if (i64.eq (local.get $x2) (local.get $y2)) (then - (if (i64.eq (local.get $x3) (local.get $y3)) (then - (if (i64.eq (local.get $x4) (local.get $y4)) (then - (local.set $r4 (i64.const 1)) - )) - )) - )) - )) + (local.set $r4 (i64.extend_i32_u (i32.and (i64.eq (local.get $x1) (local.get $y1)) (i32.and (i64.eq (local.get $x2) (local.get $y2)) (i32.and (i64.eq (local.get $x3) (local.get $y3)) (i64.eq (local.get $x4) (local.get $y4))))))) ) (global.set $global_ (local.get $r2)) @@ -395,28 +392,7 @@ Text representation: (local.get $r1) ) -(func $cmp - (param $a i64) - (param $b i64) - (result i32) - (local $r i32) - (local $condition i32) - (block $label__10 - (block - (local.set $condition (i64.lt_u (local.get $a) (local.get $b))) - (if (i32.eq (local.get $condition) (i32.const 1)) (then - (local.set $r (i32.const 4294967295)) - )(else - (local.set $r (i64.ne (local.get $a) (local.get $b))) - )) - - ) - - ) - (local.get $r) -) - -(func $lt +(func $lt_202 (param $x1 i64) (param $x2 i64) (param $x3 i64) @@ -426,28 +402,27 @@ Text representation: (param $y3 i64) (param $y4 i64) (result i64) - (local $z1 i64) - (local $z2 i64) - (local $z3 i64) (local $z4 i64) (local $z i32) + (local $_1 i32) + (local $condition i32) + (local $condition_11 i32) (local $condition_12 i32) - (local $condition_13 i32) - (local $condition_14 i32) - (block $label__11 + (block $label__10 (local.set $z (i32.const 0)) + (local.set $_1 (i32.const 4294967295)) (block - (local.set $condition_12 (call $cmp (local.get $x1) (local.get $y1))) - (if (i32.eq (local.get $condition_12) (i32.const 0)) (then + (local.set $condition (select (local.get $_1) (i64.ne (local.get $x1) (local.get $y1)) (i64.lt_u (local.get $x1) (local.get $y1)))) + (if (i32.eq (local.get $condition) (i32.const 0)) (then (block - (local.set $condition_13 (call $cmp (local.get $x2) (local.get $y2))) - (if (i32.eq (local.get $condition_13) (i32.const 0)) (then + (local.set $condition_11 (select (local.get $_1) (i64.ne (local.get $x2) (local.get $y2)) (i64.lt_u (local.get $x2) (local.get $y2)))) + (if (i32.eq (local.get $condition_11) (i32.const 0)) (then (block - (local.set $condition_14 (call $cmp (local.get $x3) (local.get $y3))) - (if (i32.eq (local.get $condition_14) (i32.const 0)) (then + (local.set $condition_12 (select (local.get $_1) (i64.ne (local.get $x3) (local.get $y3)) (i64.lt_u (local.get $x3) (local.get $y3)))) + (if (i32.eq (local.get $condition_12) (i32.const 0)) (then (local.set $z (i64.lt_u (local.get $x4) (local.get $y4))) )(else - (if (i32.eq (local.get $condition_14) (i32.const 1)) (then + (if (i32.eq (local.get $condition_12) (i32.const 1)) (then (local.set $z (i32.const 0)) )(else (local.set $z (i32.const 1)) @@ -456,7 +431,7 @@ Text representation: ) )(else - (if (i32.eq (local.get $condition_13) (i32.const 1)) (then + (if (i32.eq (local.get $condition_11) (i32.const 1)) (then (local.set $z (i32.const 0)) )(else (local.set $z (i32.const 1)) @@ -465,7 +440,7 @@ Text representation: ) )(else - (if (i32.eq (local.get $condition_12) (i32.const 1)) (then + (if (i32.eq (local.get $condition) (i32.const 1)) (then (local.set $z (i32.const 0)) )(else (local.set $z (i32.const 1)) @@ -476,96 +451,146 @@ Text representation: (local.set $z4 (i64.extend_i32_u (local.get $z))) ) - (global.set $global_ (local.get $z2)) - (global.set $global__1 (local.get $z3)) - (global.set $global__2 (local.get $z4)) - (local.get $z1) + (local.get $z4) ) -(func $calldataload +(func $u256_to_i32 (param $x1 i64) (param $x2 i64) (param $x3 i64) (param $x4 i64) - (result i64) - (local $z1 i64) - (local $z2 i64) - (local $z3 i64) - (local $z4 i64) - (local $z1_1 i64) - (local $z2_1 i64) - (local $z3_1 i64) - (local $z4_1 i64) - (block $label__15 + (result i32) + (local $v i32) + (block $label__13 (if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then (unreachable))) (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then (unreachable))) - (call $eth.callDataCopy (i32.const 0) (i32.wrap_i64 (local.get $x4)) (i32.const 32)) - (local.set $z1_1 (call $endian_swap (i64.load (i32.const 0)))) - (local.set $z2_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8))))) - (local.set $z3_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16))))) - (local.set $z4_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24))))) - (local.set $z1 (local.get $z1_1)) - (local.set $z2 (local.get $z2_1)) - (local.set $z3 (local.get $z3_1)) - (local.set $z4 (local.get $z4_1)) + (local.set $v (i32.wrap_i64 (local.get $x4))) ) - (global.set $global_ (local.get $z2)) - (global.set $global__1 (local.get $z3)) - (global.set $global__2 (local.get $z4)) - (local.get $z1) + (local.get $v) ) -(func $endian_swap_16 +(func $bswap16 (param $x i64) (result i64) (local $y i64) - (block $label__16 + (block $label__14 (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) ) (local.get $y) ) -(func $endian_swap_32 +(func $bswap32 (param $x i64) (result i64) (local $y i64) (local $hi i64) - (block $label__17 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (block $label__15 + (local.set $hi (i64.shl (call $bswap16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $bswap16 (i64.shr_u (local.get $x) (i64.const 16))))) ) (local.get $y) ) -(func $endian_swap +(func $bswap64 (param $x i64) (result i64) (local $y i64) (local $hi i64) - (block $label__18 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + (block $label__16 + (local.set $hi (i64.shl (call $bswap32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $bswap32 (i64.shr_u (local.get $x) (i64.const 32))))) ) (local.get $y) ) +(func $calldataload + (param $x1 i64) + (param $x2 i64) + (param $x3 i64) + (param $x4 i64) + (result i64) + (local $z1 i64) + (local $z2 i64) + (local $z3 i64) + (local $z4 i64) + (local $cds i32) + (local $_1 i64) + (local $destination i32) + (local $offset i32) + (local $requested_size i32) + (local $available_size i32) + (local $_2 i32) + (local $_3 i32) + (local $_4 i32) + (local $i i32) + (local $z1_1 i64) + (local $z2_1 i64) + (local $z3_1 i64) + (local $z4_1 i64) + (block $label__17 + (local.set $cds (call $eth.getCallDataSize)) + (local.set $_1 (i64.const 0)) + (local.set $destination (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) + (local.set $offset (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) + (local.set $requested_size (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 32))) + (if (i32.gt_u (local.get $offset) (i32.sub (i32.const 4294967295) (local.get $requested_size))) (then + (call $eth.revert (i32.const 0) (i32.const 0)))) + (local.set $available_size (i32.sub (local.get $cds) (local.get $offset))) + (if (i32.gt_u (local.get $offset) (local.get $cds)) (then + (local.set $available_size (i32.const 0)) + )) + (local.set $_2 (i32.const 0)) + (if (i32.gt_u (local.get $available_size) (local.get $_2)) (then + (call $eth.callDataCopy (local.get $destination) (local.get $offset) (local.get $available_size)))) + (if (i32.gt_u (local.get $requested_size) (local.get $available_size)) (then + (local.set $_3 (i32.sub (local.get $requested_size) (local.get $available_size))) + (local.set $_4 (i32.add (local.get $destination) (local.get $available_size))) + (local.set $i (local.get $_2)) + (block $label__18 + (loop $label__20 + (br_if $label__18 (i32.eqz (i32.lt_u (local.get $i) (local.get $_3)))) + (block $label__19 + (i32.store8 (i32.add (local.get $_4) (local.get $i)) (local.get $_2)) + ) + (local.set $i (i32.add (local.get $i) (i32.const 1))) + (br $label__20) + ) + + ) + )) + (local.set $z1_1 (call $bswap64 (i64.load (local.get $_2)))) + (local.set $z2_1 (call $bswap64 (i64.load (i32.add (local.get $_2) (i32.const 8))))) + (local.set $z3_1 (call $bswap64 (i64.load (i32.add (local.get $_2) (i32.const 16))))) + (local.set $z4_1 (call $bswap64 (i64.load (i32.add (local.get $_2) (i32.const 24))))) + (local.set $z1 (local.get $z1_1)) + (local.set $z2 (local.get $z2_1)) + (local.set $z3 (local.get $z3_1)) + (local.set $z4 (local.get $z4_1)) + + ) + (global.set $global_ (local.get $z2)) + (global.set $global__1 (local.get $z3)) + (global.set $global__2 (local.get $z4)) + (local.get $z1) +) + (func $mstore_internal (param $pos i32) (param $y1 i64) (param $y2 i64) (param $y3 i64) (param $y4 i64) - (block $label__19 - (i64.store (local.get $pos) (call $endian_swap (local.get $y1))) - (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $endian_swap (local.get $y2))) - (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $endian_swap (local.get $y3))) - (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $endian_swap (local.get $y4))) + (block $label__21 + (i64.store (local.get $pos) (call $bswap64 (local.get $y1))) + (i64.store (i32.add (local.get $pos) (i32.const 8)) (call $bswap64 (local.get $y2))) + (i64.store (i32.add (local.get $pos) (i32.const 16)) (call $bswap64 (local.get $y3))) + (i64.store (i32.add (local.get $pos) (i32.const 24)) (call $bswap64 (local.get $y4))) ) ) @@ -578,7 +603,7 @@ Text representation: (param $y2 i64) (param $y3 i64) (param $y4 i64) - (block $label__20 + (block $label__22 (call $mstore_internal (i32.const 0) (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $mstore_internal (i32.const 32) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $eth.storageStore (i32.const 0) (i32.const 32)) diff --git a/test/cmdlineTests/exp_base_literal/args b/test/cmdlineTests/exp_base_literal/args new file mode 100644 index 000000000000..cdda9bc05f2d --- /dev/null +++ b/test/cmdlineTests/exp_base_literal/args @@ -0,0 +1 @@ +--ir diff --git a/test/cmdlineTests/exp_base_literal/err b/test/cmdlineTests/exp_base_literal/err new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/test/cmdlineTests/exp_base_literal/err @@ -0,0 +1 @@ + diff --git a/test/cmdlineTests/exp_base_literal/exit b/test/cmdlineTests/exp_base_literal/exit new file mode 100644 index 000000000000..573541ac9702 --- /dev/null +++ b/test/cmdlineTests/exp_base_literal/exit @@ -0,0 +1 @@ +0 diff --git a/test/cmdlineTests/exp_base_literal/input.sol b/test/cmdlineTests/exp_base_literal/input.sol new file mode 100644 index 000000000000..1c8408413e86 --- /dev/null +++ b/test/cmdlineTests/exp_base_literal/input.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity > 0.7.1; +pragma abicoder v2; + +contract C { + function f(uint a, uint b, uint c, uint d) public pure returns (uint, int, uint, uint) { + uint w = 2**a; + int x = (-2)**b; + uint y = 10**c; + uint z = (2**256 -1 )**d; + + // Special cases: 0, 1, and -1 + w = (0)**a; + x = (-1)**b; + y = 1**c; + + return (w, x, y, z); + } +} diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output new file mode 100644 index 000000000000..55c06f769c59 --- /dev/null +++ b/test/cmdlineTests/exp_base_literal/output @@ -0,0 +1,320 @@ +IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object "C_81" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_C_81() + + codecopy(0, dataoffset("C_81_deployed"), datasize("C_81_deployed")) + + return(0, datasize("C_81_deployed")) + + function constructor_C_81() { + + } + + } + object "C_81_deployed" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x70cb9605 + { + // f(uint256,uint256,uint256,uint256) + if callvalue() { revert(0, 0) } + let param_0, param_1, param_2, param_3 := abi_decode_tuple_t_uint256t_uint256t_uint256t_uint256(4, calldatasize()) + let ret_0, ret_1, ret_2, ret_3 := fun_f_80(param_0, param_1, param_2, param_3) + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple_t_uint256_t_int256_t_uint256_t_uint256__to_t_uint256_t_int256_t_uint256_t_uint256__fromStack(memPos , ret_0, ret_1, ret_2, ret_3) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function abi_decode_t_uint256(offset, end) -> value { + value := calldataload(offset) + validator_revert_t_uint256(value) + } + + function abi_decode_tuple_t_uint256t_uint256t_uint256t_uint256(headStart, dataEnd) -> value0, value1, value2, value3 { + if slt(sub(dataEnd, headStart), 128) { revert(0, 0) } + + { + + let offset := 0 + + value0 := abi_decode_t_uint256(add(headStart, offset), dataEnd) + } + + { + + let offset := 32 + + value1 := abi_decode_t_uint256(add(headStart, offset), dataEnd) + } + + { + + let offset := 64 + + value2 := abi_decode_t_uint256(add(headStart, offset), dataEnd) + } + + { + + let offset := 96 + + value3 := abi_decode_t_uint256(add(headStart, offset), dataEnd) + } + + } + + function abi_encode_t_int256_to_t_int256_fromStack(value, pos) { + mstore(pos, cleanup_t_int256(value)) + } + + function abi_encode_t_uint256_to_t_uint256_fromStack(value, pos) { + mstore(pos, cleanup_t_uint256(value)) + } + + function abi_encode_tuple_t_uint256_t_int256_t_uint256_t_uint256__to_t_uint256_t_int256_t_uint256_t_uint256__fromStack(headStart , value0, value1, value2, value3) -> tail { + tail := add(headStart, 128) + + abi_encode_t_uint256_to_t_uint256_fromStack(value0, add(headStart, 0)) + + abi_encode_t_int256_to_t_int256_fromStack(value1, add(headStart, 32)) + + abi_encode_t_uint256_to_t_uint256_fromStack(value2, add(headStart, 64)) + + abi_encode_t_uint256_to_t_uint256_fromStack(value3, add(headStart, 96)) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function checked_exp_t_rational_0_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + power := exp(0, exponent) + } + + function checked_exp_t_rational_10_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + if gt(exponent, 77) { panic_error_0x11() } + + power := exp(10, exponent) + } + + function checked_exp_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + if gt(exponent, 1) { panic_error_0x11() } + + power := exp(115792089237316195423570985008687907853269984665640564039457584007913129639935, exponent) + } + + function checked_exp_t_rational_1_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + power := exp(1, exponent) + } + + function checked_exp_t_rational_2_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + if gt(exponent, 255) { panic_error_0x11() } + + power := exp(2, exponent) + } + + function checked_exp_t_rational_minus_1_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + power := exp(115792089237316195423570985008687907853269984665640564039457584007913129639935, exponent) + } + + function checked_exp_t_rational_minus_2_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + if gt(exponent, 255) { panic_error_0x11() } + + power := exp(115792089237316195423570985008687907853269984665640564039457584007913129639934, exponent) + } + + function cleanup_t_int256(value) -> cleaned { + cleaned := value + } + + function cleanup_t_uint256(value) -> cleaned { + cleaned := value + } + + function convert_t_rational_0_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(value) + } + + function convert_t_rational_10_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(value) + } + + function convert_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(value) + } + + function convert_t_rational_1_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(value) + } + + function convert_t_rational_2_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(value) + } + + function convert_t_rational_minus_1_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(value) + } + + function convert_t_rational_minus_2_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(value) + } + + function fun_f_80(vloc_a_4, vloc_b_6, vloc_c_8, vloc_d_10) -> vloc__13, vloc__15, vloc__17, vloc__19 { + let zero_value_for_type_t_uint256_1 := zero_value_for_split_t_uint256() + vloc__13 := zero_value_for_type_t_uint256_1 + let zero_value_for_type_t_int256_2 := zero_value_for_split_t_int256() + vloc__15 := zero_value_for_type_t_int256_2 + let zero_value_for_type_t_uint256_3 := zero_value_for_split_t_uint256() + vloc__17 := zero_value_for_type_t_uint256_3 + let zero_value_for_type_t_uint256_4 := zero_value_for_split_t_uint256() + vloc__19 := zero_value_for_type_t_uint256_4 + + let expr_23 := 0x02 + let _5 := vloc_a_4 + let expr_24 := _5 + let _6 := convert_t_rational_2_by_1_to_t_uint256(expr_23) + let expr_25 := checked_exp_t_rational_2_by_1_t_uint256(expr_24) + let vloc_w_22 := expr_25 + let expr_29 := 0x02 + let expr_30 := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe + let expr_31 := expr_30 + let _7 := vloc_b_6 + let expr_32 := _7 + let _8 := convert_t_rational_minus_2_by_1_to_t_int256(expr_31) + let expr_33 := checked_exp_t_rational_minus_2_by_1_t_uint256(expr_32) + let vloc_x_28 := expr_33 + let expr_37 := 0x0a + let _9 := vloc_c_8 + let expr_38 := _9 + let _10 := convert_t_rational_10_by_1_to_t_uint256(expr_37) + let expr_39 := checked_exp_t_rational_10_by_1_t_uint256(expr_38) + let vloc_y_36 := expr_39 + let expr_47 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + let expr_48 := expr_47 + let _11 := vloc_d_10 + let expr_49 := _11 + let _12 := convert_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_to_t_uint256(expr_48) + let expr_50 := checked_exp_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_t_uint256(expr_49) + let vloc_z_42 := expr_50 + let expr_53 := 0x00 + let expr_54 := expr_53 + let _13 := vloc_a_4 + let expr_55 := _13 + let _14 := convert_t_rational_0_by_1_to_t_uint256(expr_54) + let expr_56 := checked_exp_t_rational_0_by_1_t_uint256(expr_55) + vloc_w_22 := expr_56 + let expr_57 := expr_56 + let expr_60 := 0x01 + let expr_61 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + let expr_62 := expr_61 + let _15 := vloc_b_6 + let expr_63 := _15 + let _16 := convert_t_rational_minus_1_by_1_to_t_int256(expr_62) + let expr_64 := checked_exp_t_rational_minus_1_by_1_t_uint256(expr_63) + vloc_x_28 := expr_64 + let expr_65 := expr_64 + let expr_68 := 0x01 + let _17 := vloc_c_8 + let expr_69 := _17 + let _18 := convert_t_rational_1_by_1_to_t_uint256(expr_68) + let expr_70 := checked_exp_t_rational_1_by_1_t_uint256(expr_69) + vloc_y_36 := expr_70 + let expr_71 := expr_70 + let _19 := vloc_w_22 + let expr_73 := _19 + let expr_77_component_1 := expr_73 + let _20 := vloc_x_28 + let expr_74 := _20 + let expr_77_component_2 := expr_74 + let _21 := vloc_y_36 + let expr_75 := _21 + let expr_77_component_3 := expr_75 + let _22 := vloc_z_42 + let expr_76 := _22 + let expr_77_component_4 := expr_76 + vloc__13 := expr_77_component_1 + vloc__15 := expr_77_component_2 + vloc__17 := expr_77_component_3 + vloc__19 := expr_77_component_4 + leave + + } + + function panic_error_0x11() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x11) + revert(0, 0x24) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function validator_revert_t_uint256(value) { + if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) } + } + + function zero_value_for_split_t_int256() -> ret { + ret := 0 + } + + function zero_value_for_split_t_uint256() -> ret { + ret := 0 + } + + } + + } + +} diff --git a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/input.sol b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/input.sol index 8d523bd4dec9..097a2fbe9687 100644 --- a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/input.sol +++ b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/input.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.0; +pragma abicoder v2; contract C { constructor() {} diff --git a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output index fd372367a055..418676634492 100644 --- a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output +++ b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output @@ -6,17 +6,17 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *******************************************************/ -object "C_6" { +object "C_7" { code { { mstore(64, 128) if callvalue() { revert(0, 0) } - let _1 := datasize("C_6_deployed") - codecopy(0, dataoffset("C_6_deployed"), _1) + let _1 := datasize("C_7_deployed") + codecopy(0, dataoffset("C_7_deployed"), _1) return(0, _1) } } - object "C_6_deployed" { + object "C_7_deployed" { code { { mstore(64, 128) @@ -34,17 +34,17 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *******************************************************/ -object "D_9" { +object "D_10" { code { { mstore(64, 128) if callvalue() { revert(0, 0) } - let _1 := datasize("D_9_deployed") - codecopy(0, dataoffset("D_9_deployed"), _1) + let _1 := datasize("D_10_deployed") + codecopy(0, dataoffset("D_10_deployed"), _1) return(0, _1) } } - object "D_9_deployed" { + object "D_10_deployed" { code { { mstore(64, 128) diff --git a/test/cmdlineTests/ir_compiler_subobjects/err b/test/cmdlineTests/ir_compiler_subobjects/err index d4394af57256..b300ec124361 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/err +++ b/test/cmdlineTests/ir_compiler_subobjects/err @@ -1,5 +1,5 @@ Warning: Unused local variable. - --> ir_compiler_subobjects/input.sol:7:9: + --> ir_compiler_subobjects/input.sol:8:9: | -7 | C c = new C(); +8 | C c = new C(); | ^^^ diff --git a/test/cmdlineTests/ir_compiler_subobjects/input.sol b/test/cmdlineTests/ir_compiler_subobjects/input.sol index 13f9f3c60efd..bad0fe74f8f0 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/input.sol +++ b/test/cmdlineTests/ir_compiler_subobjects/input.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.6.0; +pragma abicoder v2; contract C {} contract D { diff --git a/test/cmdlineTests/ir_compiler_subobjects/output b/test/cmdlineTests/ir_compiler_subobjects/output index 6b5f8e6778bd..f331a82c606a 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/output +++ b/test/cmdlineTests/ir_compiler_subobjects/output @@ -6,17 +6,17 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *******************************************************/ -object "C_2" { +object "C_3" { code { { mstore(64, 128) if callvalue() { revert(0, 0) } - let _1 := datasize("C_2_deployed") - codecopy(0, dataoffset("C_2_deployed"), _1) + let _1 := datasize("C_3_deployed") + codecopy(0, dataoffset("C_3_deployed"), _1) return(0, _1) } } - object "C_2_deployed" { + object "C_3_deployed" { code { { mstore(64, 128) @@ -34,17 +34,17 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *******************************************************/ -object "D_13" { +object "D_16" { code { { mstore(64, 128) if callvalue() { revert(0, 0) } - let _1 := datasize("D_13_deployed") - codecopy(0, dataoffset("D_13_deployed"), _1) + let _1 := datasize("D_16_deployed") + codecopy(0, dataoffset("D_16_deployed"), _1) return(0, _1) } } - object "D_13_deployed" { + object "D_16_deployed" { code { { mstore(64, 128) @@ -55,11 +55,15 @@ object "D_13" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } - let _2 := datasize("C_2") + let _2 := datasize("C_3") let _3 := add(128, _2) - if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) { revert(_1, _1) } - datacopy(128, dataoffset("C_2"), _2) - pop(create(_1, 128, _2)) + if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) { panic_error_0x41() } + datacopy(128, dataoffset("C_3"), _2) + if iszero(create(_1, 128, _2)) + { + returndatacopy(_1, _1, returndatasize()) + revert(_1, returndatasize()) + } return(allocateMemory(_1), _1) } } @@ -69,21 +73,27 @@ object "D_13" { { memPtr := mload(64) let newFreePtr := add(memPtr, size) - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } + function panic_error_0x41() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } } - object "C_2" { + object "C_3" { code { { mstore(64, 128) if callvalue() { revert(0, 0) } - let _1 := datasize("C_2_deployed") - codecopy(0, dataoffset("C_2_deployed"), _1) + let _1 := datasize("C_3_deployed") + codecopy(0, dataoffset("C_3_deployed"), _1) return(0, _1) } } - object "C_2_deployed" { + object "C_3_deployed" { code { { mstore(64, 128) diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/args b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/args new file mode 100644 index 000000000000..cae21e7207e0 --- /dev/null +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/args @@ -0,0 +1 @@ +--ir-optimized --optimize \ No newline at end of file diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/input.sol b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/input.sol new file mode 100644 index 000000000000..aca9b3befe81 --- /dev/null +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; +pragma abicoder v2; + +contract D { + constructor() { assembly {}} + function f() public pure {} +} diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output new file mode 100644 index 000000000000..e4ad6b29860a --- /dev/null +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output @@ -0,0 +1,38 @@ +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "D_12" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("D_12_deployed") + codecopy(0, dataoffset("D_12_deployed"), _1) + return(0, _1) + } + } + object "D_12_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + if eq(0x26121ff0, shr(224, calldataload(_1))) + { + if callvalue() { revert(_1, _1) } + if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + mstore(64, 128) + return(128, _1) + } + } + revert(0, 0) + } + } + } +} diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/args b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/args new file mode 100644 index 000000000000..cae21e7207e0 --- /dev/null +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/args @@ -0,0 +1 @@ +--ir-optimized --optimize \ No newline at end of file diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/input.sol b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/input.sol new file mode 100644 index 000000000000..e217f9cbb9d0 --- /dev/null +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; +pragma abicoder v2; + +contract D { + function f() public pure { + assembly {} + } +} diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output new file mode 100644 index 000000000000..cf559e4bdcd1 --- /dev/null +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output @@ -0,0 +1,38 @@ +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "D_8" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("D_8_deployed") + codecopy(0, dataoffset("D_8_deployed"), _1) + return(0, _1) + } + } + object "D_8_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + if eq(0x26121ff0, shr(224, calldataload(_1))) + { + if callvalue() { revert(_1, _1) } + if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + mstore(64, 128) + return(128, _1) + } + } + revert(0, 0) + } + } + } +} diff --git a/test/cmdlineTests/linking_solidity/args b/test/cmdlineTests/linking_solidity/args new file mode 100644 index 000000000000..0b04ebd47ae4 --- /dev/null +++ b/test/cmdlineTests/linking_solidity/args @@ -0,0 +1 @@ +--bin --bin-runtime --libraries linking_solidity/input.sol:L:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_solidity/input.sol b/test/cmdlineTests/linking_solidity/input.sol new file mode 100644 index 000000000000..eb06631bbee4 --- /dev/null +++ b/test/cmdlineTests/linking_solidity/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +library L { + function f() external {} +} + +contract C { + function foo() public { + L.f(); + } +} diff --git a/test/cmdlineTests/linking_solidity/output b/test/cmdlineTests/linking_solidity/output new file mode 100644 index 000000000000..307b131b5af3 --- /dev/null +++ b/test/cmdlineTests/linking_solidity/output @@ -0,0 +1,12 @@ + +======= linking_solidity/input.sol:C ======= +Binary: + +Binary of the runtime part: + + +======= linking_solidity/input.sol:L ======= +Binary: + +Binary of the runtime part: + diff --git a/test/cmdlineTests/linking_solidity_unresolved_references/args b/test/cmdlineTests/linking_solidity_unresolved_references/args new file mode 100644 index 000000000000..a08ce1cb4ae3 --- /dev/null +++ b/test/cmdlineTests/linking_solidity_unresolved_references/args @@ -0,0 +1 @@ +--bin --bin-runtime --libraries linking_solidity_unresolved_references/input.sol:L1:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_solidity_unresolved_references/input.sol b/test/cmdlineTests/linking_solidity_unresolved_references/input.sol new file mode 100644 index 000000000000..2a5f673058d3 --- /dev/null +++ b/test/cmdlineTests/linking_solidity_unresolved_references/input.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +library L1 { + function f() external {} +} + +library L2 { + function f() external {} +} + +contract C { + function foo() public { + L1.f(); + L2.f(); + L1.f(); + L2.f(); + L1.f(); + } +} diff --git a/test/cmdlineTests/linking_solidity_unresolved_references/output b/test/cmdlineTests/linking_solidity_unresolved_references/output new file mode 100644 index 000000000000..f42191f51e0b --- /dev/null +++ b/test/cmdlineTests/linking_solidity_unresolved_references/output @@ -0,0 +1,24 @@ + +======= linking_solidity_unresolved_references/input.sol:C ======= +Binary: +__$8ef13d1c56d5343bf69cf9444272079aa5$____$8ef13d1c56d5343bf69cf9444272079aa5$__ + +// $8ef13d1c56d5343bf69cf9444272079aa5$ -> linking_solidity_unresolved_references/input.sol:L2 +// $8ef13d1c56d5343bf69cf9444272079aa5$ -> linking_solidity_unresolved_references/input.sol:L2 +Binary of the runtime part: +__$8ef13d1c56d5343bf69cf9444272079aa5$____$8ef13d1c56d5343bf69cf9444272079aa5$__ + +// $8ef13d1c56d5343bf69cf9444272079aa5$ -> linking_solidity_unresolved_references/input.sol:L2 +// $8ef13d1c56d5343bf69cf9444272079aa5$ -> linking_solidity_unresolved_references/input.sol:L2 + +======= linking_solidity_unresolved_references/input.sol:L1 ======= +Binary: + +Binary of the runtime part: + + +======= linking_solidity_unresolved_references/input.sol:L2 ======= +Binary: + +Binary of the runtime part: + diff --git a/test/cmdlineTests/linking_standard_solidity/input.json b/test/cmdlineTests/linking_standard_solidity/input.json new file mode 100644 index 000000000000..921aa501805b --- /dev/null +++ b/test/cmdlineTests/linking_standard_solidity/input.json @@ -0,0 +1,33 @@ +{ + "language": "Solidity", + "sources": { + "A": { + "content": " + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.0; + + library L { + function f() external {} + } + + contract C { + function foo() public { + L.f(); + } + } + " + } + }, + "settings": { + "libraries": { + "A": { + "L": "0x1234567890123456789012345678901234567890" + } + }, + "outputSelection": { + "*": { + "C": ["evm.bytecode.object", "evm.bytecode.linkReferences"] + } + } + } +} diff --git a/test/cmdlineTests/linking_standard_solidity/output.json b/test/cmdlineTests/linking_standard_solidity/output.json new file mode 100644 index 000000000000..acf3bbc0089a --- /dev/null +++ b/test/cmdlineTests/linking_standard_solidity/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"C":{"evm":{"bytecode":{"linkReferences":{},"object":""}}}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/input.json b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/input.json new file mode 100644 index 000000000000..d96e65c5fe34 --- /dev/null +++ b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/input.json @@ -0,0 +1,33 @@ +{ + "language": "Solidity", + "sources": { + "A\"B": { + "content": " + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.0; + + library L { + function f() external {} + } + + contract C { + function foo() public { + L.f(); + } + } + " + } + }, + "settings": { + "libraries": { + "A\"B": { + "L": "0x1234567890123456789012345678901234567890" + } + }, + "outputSelection": { + "*": { + "C": ["evm.bytecode.object", "evm.bytecode.linkReferences"] + } + } + } +} diff --git a/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/output.json b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/output.json new file mode 100644 index 000000000000..370772d7da30 --- /dev/null +++ b/test/cmdlineTests/linking_standard_solidity_quote_in_file_name/output.json @@ -0,0 +1 @@ +{"contracts":{"A\"B":{"C":{"evm":{"bytecode":{"linkReferences":{},"object":""}}}}},"sources":{"A\"B":{"id":0}}} diff --git a/test/cmdlineTests/linking_standard_solidity_unresolved_references/input.json b/test/cmdlineTests/linking_standard_solidity_unresolved_references/input.json new file mode 100644 index 000000000000..9a6ce25a7631 --- /dev/null +++ b/test/cmdlineTests/linking_standard_solidity_unresolved_references/input.json @@ -0,0 +1,41 @@ +{ + "language": "Solidity", + "sources": { + "A": { + "content": " + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.0; + + library L1 { + function f() external {} + } + + library L2 { + function f() external {} + } + + contract C { + function foo() public { + L1.f(); + L2.f(); + L1.f(); + L2.f(); + L1.f(); + } + } + " + } + }, + "settings": { + "libraries": { + "A": { + "L1": "0x1234567890123456789012345678901234567890" + } + }, + "outputSelection": { + "*": { + "C": ["evm.bytecode.object", "evm.bytecode.linkReferences"] + } + } + } +} diff --git a/test/cmdlineTests/linking_standard_solidity_unresolved_references/output.json b/test/cmdlineTests/linking_standard_solidity_unresolved_references/output.json new file mode 100644 index 000000000000..65db86584de3 --- /dev/null +++ b/test/cmdlineTests/linking_standard_solidity_unresolved_references/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"C":{"evm":{"bytecode":{"linkReferences":{"A":{"L2":[{"length":20,"start":184},{"length":20,"start":368}]}},"object":"__$622b2f540b6a16ff5db7bea656ad8fcf4f$____$622b2f540b6a16ff5db7bea656ad8fcf4f$__"}}}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/linking_standard_yul/input.json b/test/cmdlineTests/linking_standard_yul/input.json new file mode 100644 index 000000000000..3825a1b12837 --- /dev/null +++ b/test/cmdlineTests/linking_standard_yul/input.json @@ -0,0 +1,20 @@ +{ + "language": "Yul", + "sources": { + "A": { + "content": "object \"a\" { code { let addr := linkersymbol(\"contract/test.sol:L\") } }" + } + }, + "settings": { + "libraries": { + "contract/test.sol": { + "L": "0x1234567890123456789012345678901234567890" + } + }, + "outputSelection": { + "*": { + "*": ["evm.bytecode.object", "evm.bytecode.linkReferences"] + } + } + } +} diff --git a/test/cmdlineTests/linking_standard_yul/output.json b/test/cmdlineTests/linking_standard_yul/output.json new file mode 100644 index 000000000000..7816df4ecf75 --- /dev/null +++ b/test/cmdlineTests/linking_standard_yul/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"a":{"evm":{"bytecode":{"linkReferences":{},"object":""}}}}},"errors":[{"component":"general","formattedMessage":"Yul is still experimental. Please use the output with care.","message":"Yul is still experimental. Please use the output with care.","severity":"warning","type":"Warning"}]} diff --git a/test/cmdlineTests/linking_standard_yul_quote_in_file_name/input.json b/test/cmdlineTests/linking_standard_yul_quote_in_file_name/input.json new file mode 100644 index 000000000000..24fe9d61ab43 --- /dev/null +++ b/test/cmdlineTests/linking_standard_yul_quote_in_file_name/input.json @@ -0,0 +1,20 @@ +{ + "language": "Yul", + "sources": { + "A": { + "content": "object \"a\" { code { let addr := linkersymbol(\"contract/test\\\".sol:L\") } }" + } + }, + "settings": { + "libraries": { + "contract/test\".sol": { + "L": "0x1234567890123456789012345678901234567890" + } + }, + "outputSelection": { + "*": { + "*": ["evm.bytecode.object", "evm.bytecode.linkReferences"] + } + } + } +} diff --git a/test/cmdlineTests/linking_standard_yul_quote_in_file_name/output.json b/test/cmdlineTests/linking_standard_yul_quote_in_file_name/output.json new file mode 100644 index 000000000000..7816df4ecf75 --- /dev/null +++ b/test/cmdlineTests/linking_standard_yul_quote_in_file_name/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"a":{"evm":{"bytecode":{"linkReferences":{},"object":""}}}}},"errors":[{"component":"general","formattedMessage":"Yul is still experimental. Please use the output with care.","message":"Yul is still experimental. Please use the output with care.","severity":"warning","type":"Warning"}]} diff --git a/test/cmdlineTests/linking_standard_yul_unresolved_references/input.json b/test/cmdlineTests/linking_standard_yul_unresolved_references/input.json new file mode 100644 index 000000000000..39f0f7c3a3cf --- /dev/null +++ b/test/cmdlineTests/linking_standard_yul_unresolved_references/input.json @@ -0,0 +1,20 @@ +{ + "language": "Yul", + "sources": { + "A": { + "content": "object \"a\" { code { let addr1 := linkersymbol(\"contract/test.sol:L1\") let addr2 := linkersymbol(\"contract/test.sol:L2\") } }" + } + }, + "settings": { + "libraries": { + "contract/test.sol": { + "L1": "0x1234567890123456789012345678901234567890" + } + }, + "outputSelection": { + "*": { + "*": ["evm.bytecode.object", "evm.bytecode.linkReferences"] + } + } + } +} diff --git a/test/cmdlineTests/linking_standard_yul_unresolved_references/output.json b/test/cmdlineTests/linking_standard_yul_unresolved_references/output.json new file mode 100644 index 000000000000..4dab68351529 --- /dev/null +++ b/test/cmdlineTests/linking_standard_yul_unresolved_references/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"a":{"evm":{"bytecode":{"linkReferences":{"contract/test.sol":{"L2":[{"length":20,"start":22}]}},"object":"__$fb58009a6b1ecea3b9d99bedd645df4ec3$__"}}}}},"errors":[{"component":"general","formattedMessage":"Yul is still experimental. Please use the output with care.","message":"Yul is still experimental. Please use the output with care.","severity":"warning","type":"Warning"}]} diff --git a/test/cmdlineTests/linking_strict_assembly/args b/test/cmdlineTests/linking_strict_assembly/args new file mode 100644 index 000000000000..7a735d327da9 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly/args @@ -0,0 +1 @@ +--strict-assembly --libraries contract/test.sol:L:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_strict_assembly/err b/test/cmdlineTests/linking_strict_assembly/err new file mode 100644 index 000000000000..014a1178fa22 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly/input.yul b/test/cmdlineTests/linking_strict_assembly/input.yul new file mode 100644 index 000000000000..be2052771608 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly/input.yul @@ -0,0 +1,5 @@ +object "a" { + code { + let addr := linkersymbol("contract/test.sol:L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly/output b/test/cmdlineTests/linking_strict_assembly/output new file mode 100644 index 000000000000..23b2826f174c --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly/output @@ -0,0 +1,18 @@ + +======= linking_strict_assembly/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { + let addr := linkersymbol("contract/test.sol:L") + } +} + + +Binary representation: +73123456789012345678901234567890123456789050 + +Text representation: + linkerSymbol("f919ba91ac99f96129544b80b9516b27a80e376b9dc693819d0b18b7e0395612") + /* "linking_strict_assembly/input.yul":22:85 */ + pop diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/args b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/args new file mode 100644 index 000000000000..54be2478af54 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/args @@ -0,0 +1 @@ +--strict-assembly --libraries library.sol:L:0x1234567890123456789012345678901234567890,library.sol:L:0x0987654321098765432109876543210987654321 diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/err b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/err new file mode 100644 index 000000000000..973e9e06eea7 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/err @@ -0,0 +1 @@ +Address specified more than once for library "library.sol:L". diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/exit b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/input.yul b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/input.yul new file mode 100644 index 000000000000..c4dda1ceb376 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_duplicate_library_name/input.yul @@ -0,0 +1,5 @@ +object "a" { + code { + let addr := linkersymbol("library.sol:L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/args b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/args new file mode 100644 index 000000000000..57ee5078b3e4 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/args @@ -0,0 +1 @@ +--strict-assembly --libraries L:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/err b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/err new file mode 100644 index 000000000000..014a1178fa22 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/input.yul b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/input.yul new file mode 100644 index 000000000000..b4dbc3cbc031 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/input.yul @@ -0,0 +1,5 @@ +object "a" { + code { + let addr := linkersymbol("L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output new file mode 100644 index 000000000000..c7a9339b6f6e --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_no_file_name_in_link_reference/output @@ -0,0 +1,16 @@ + +======= linking_strict_assembly_no_file_name_in_link_reference/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { let addr := linkersymbol("L") } +} + + +Binary representation: +73123456789012345678901234567890123456789050 + +Text representation: + linkerSymbol("8aa64f937099b65a4febc243a5ae0f2d6416bb9e473c30dd29c1ee498fb7c5a8") + /* "linking_strict_assembly_no_file_name_in_link_reference/input.yul":22:67 */ + pop diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/args b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/args new file mode 100644 index 000000000000..a424a337aac4 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/args @@ -0,0 +1 @@ +--strict-assembly --libraries library1.sol:L:0x1111111111111111111111111111111111111111,library2.sol:L:0x2222222222222222222222222222222222222222 diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/err b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/err new file mode 100644 index 000000000000..014a1178fa22 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/input.yul b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/input.yul new file mode 100644 index 000000000000..acb6128576f3 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/input.yul @@ -0,0 +1,6 @@ +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output new file mode 100644 index 000000000000..80696c5942e6 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files/output @@ -0,0 +1,22 @@ + +======= linking_strict_assembly_same_library_name_different_files/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} + + +Binary representation: +7311111111111111111111111111111111111111117322222222222222222222222222222222222222225050 + +Text representation: + linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") + /* "linking_strict_assembly_same_library_name_different_files/input.yul":32:75 */ + linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") + /* "linking_strict_assembly_same_library_name_different_files/input.yul":22:133 */ + pop + pop diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/args b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/args new file mode 100644 index 000000000000..cce3dedbf7dc --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/args @@ -0,0 +1 @@ +--strict-assembly --libraries library1.sol:L:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/err b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/err new file mode 100644 index 000000000000..014a1178fa22 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul new file mode 100644 index 000000000000..acb6128576f3 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul @@ -0,0 +1,6 @@ +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output new file mode 100644 index 000000000000..150355ba1073 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_same_library_name_different_files_in_link_references/output @@ -0,0 +1,22 @@ + +======= linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { + let addr1 := linkersymbol("library1.sol:L") + let addr2 := linkersymbol("library2.sol:L") + } +} + + +Binary representation: +73123456789012345678901234567890123456789073__$c3523432985587641d17c68161d2f700c5$__5050 + +Text representation: + linkerSymbol("f3ffc10c396a7cc41ae954b050792839d20947bf73497d30c49a9fda1ea477ec") + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":32:75 */ + linkerSymbol("c3523432985587641d17c68161d2f700c57aaf4ed21cda4f25d76193c831f97f") + /* "linking_strict_assembly_same_library_name_different_files_in_link_references/input.yul":22:133 */ + pop + pop diff --git a/test/cmdlineTests/linking_strict_assembly_unresolved_references/args b/test/cmdlineTests/linking_strict_assembly_unresolved_references/args new file mode 100644 index 000000000000..30b97f0a7b2f --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_unresolved_references/args @@ -0,0 +1 @@ +--strict-assembly --libraries contract/test.sol:L1:0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linking_strict_assembly_unresolved_references/err b/test/cmdlineTests/linking_strict_assembly_unresolved_references/err new file mode 100644 index 000000000000..014a1178fa22 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_unresolved_references/err @@ -0,0 +1 @@ +Warning: Yul is still experimental. Please use the output with care. diff --git a/test/cmdlineTests/linking_strict_assembly_unresolved_references/input.yul b/test/cmdlineTests/linking_strict_assembly_unresolved_references/input.yul new file mode 100644 index 000000000000..b64cc4bae7b7 --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_unresolved_references/input.yul @@ -0,0 +1,6 @@ +object "a" { + code { + let addr1 := linkersymbol("contract/test.sol:L1") + let addr2 := linkersymbol("contract/test.sol:L2") + } +} diff --git a/test/cmdlineTests/linking_strict_assembly_unresolved_references/output b/test/cmdlineTests/linking_strict_assembly_unresolved_references/output new file mode 100644 index 000000000000..accbaa09e7dc --- /dev/null +++ b/test/cmdlineTests/linking_strict_assembly_unresolved_references/output @@ -0,0 +1,22 @@ + +======= linking_strict_assembly_unresolved_references/input.yul (EVM) ======= + +Pretty printed source: +object "a" { + code { + let addr1 := linkersymbol("contract/test.sol:L1") + let addr2 := linkersymbol("contract/test.sol:L2") + } +} + + +Binary representation: +73123456789012345678901234567890123456789073__$fb58009a6b1ecea3b9d99bedd645df4ec3$__5050 + +Text representation: + linkerSymbol("05b0326038374a21e0895480a58bda0768cdcc04c8d18f154362d1ca5223d245") + /* "linking_strict_assembly_unresolved_references/input.yul":32:81 */ + linkerSymbol("fb58009a6b1ecea3b9d99bedd645df4ec308f17bc0087e5f39d078f77f809177") + /* "linking_strict_assembly_unresolved_references/input.yul":22:145 */ + pop + pop diff --git a/test/cmdlineTests/model_checker_engine_all/args b/test/cmdlineTests/model_checker_engine_all/args new file mode 100644 index 000000000000..5aeb1490ed43 --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_all/args @@ -0,0 +1 @@ +--model-checker-engine all diff --git a/test/cmdlineTests/model_checker_engine_all/err b/test/cmdlineTests/model_checker_engine_all/err new file mode 100644 index 000000000000..0707adfd8f4d --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_all/err @@ -0,0 +1,13 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + + +Transaction trace: +constructor() +f(0) + --> model_checker_engine_all/input.sol:6:3: + | +6 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_engine_all/input.sol b/test/cmdlineTests/model_checker_engine_all/input.sol new file mode 100644 index 000000000000..166416c0621a --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_all/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma experimental SMTChecker; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_engine_bmc/args b/test/cmdlineTests/model_checker_engine_bmc/args new file mode 100644 index 000000000000..549f20236336 --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc diff --git a/test/cmdlineTests/model_checker_engine_bmc/err b/test/cmdlineTests/model_checker_engine_bmc/err new file mode 100644 index 000000000000..82d3b6e0afac --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_bmc/err @@ -0,0 +1,10 @@ +Warning: BMC: Assertion violation happens here. + --> model_checker_engine_bmc/input.sol:6:3: + | +6 | assert(x > 0); + | ^^^^^^^^^^^^^ +Note: Counterexample: + x = 0 + +Note: Callstack: +Note: diff --git a/test/cmdlineTests/model_checker_engine_bmc/input.sol b/test/cmdlineTests/model_checker_engine_bmc/input.sol new file mode 100644 index 000000000000..166416c0621a --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_bmc/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma experimental SMTChecker; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_engine_chc/args b/test/cmdlineTests/model_checker_engine_chc/args new file mode 100644 index 000000000000..7458a47d35b9 --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc diff --git a/test/cmdlineTests/model_checker_engine_chc/err b/test/cmdlineTests/model_checker_engine_chc/err new file mode 100644 index 000000000000..69abcd82c012 --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_chc/err @@ -0,0 +1,13 @@ +Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + + +Transaction trace: +constructor() +f(0) + --> model_checker_engine_chc/input.sol:6:3: + | +6 | assert(x > 0); + | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_engine_chc/input.sol b/test/cmdlineTests/model_checker_engine_chc/input.sol new file mode 100644 index 000000000000..166416c0621a --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_chc/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma experimental SMTChecker; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_engine_none/args b/test/cmdlineTests/model_checker_engine_none/args new file mode 100644 index 000000000000..cac942cb23b4 --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_none/args @@ -0,0 +1 @@ +--model-checker-engine none diff --git a/test/cmdlineTests/model_checker_engine_none/err b/test/cmdlineTests/model_checker_engine_none/err new file mode 100644 index 000000000000..5b30ff59a75e --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_none/err @@ -0,0 +1,5 @@ +Warning: SPDX license identifier not provided in source file. Before publishing, consider adding a comment containing "SPDX-License-Identifier: " to each source file. Use "SPDX-License-Identifier: UNLICENSED" for non-open-source code. Please see https://spdx.org for more information. +--> model_checker_engine_none/input.sol + +Warning: Source file does not specify required compiler version! +--> model_checker_engine_none/input.sol diff --git a/test/cmdlineTests/model_checker_engine_none/input.sol b/test/cmdlineTests/model_checker_engine_none/input.sol new file mode 100644 index 000000000000..8727d4b40341 --- /dev/null +++ b/test/cmdlineTests/model_checker_engine_none/input.sol @@ -0,0 +1,9 @@ +// Removed to yield a warning, otherwise CI test fails with the expectation +// "no output requested" +//pragma solidity >=0.0; +pragma experimental SMTChecker; +contract test { + function f(uint x) public pure { + assert(x > 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_timeout_all/args b/test/cmdlineTests/model_checker_timeout_all/args new file mode 100644 index 000000000000..8fe8ce96a1ab --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_all/args @@ -0,0 +1 @@ +--model-checker-timeout 1000 diff --git a/test/cmdlineTests/model_checker_timeout_all/err b/test/cmdlineTests/model_checker_timeout_all/err new file mode 100644 index 000000000000..e2473dc1749b --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_all/err @@ -0,0 +1,12 @@ +Warning: CHC: Assertion violation might happen here. + --> model_checker_timeout_all/input.sol:10:3: + | +10 | assert(r % k == 0); + | ^^^^^^^^^^^^^^^^^^ + +Warning: BMC: Assertion violation might happen here. + --> model_checker_timeout_all/input.sol:10:3: + | +10 | assert(r % k == 0); + | ^^^^^^^^^^^^^^^^^^ +Note: diff --git a/test/cmdlineTests/model_checker_timeout_all/input.sol b/test/cmdlineTests/model_checker_timeout_all/input.sol new file mode 100644 index 000000000000..bae0d7a93601 --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_all/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma experimental SMTChecker; +contract test { + function f(uint x, uint y, uint k) public pure { + require(k > 0); + require(x % k == 0); + require(y % k == 0); + uint r = mulmod(x, y, k); + assert(r % k == 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_timeout_bmc/args b/test/cmdlineTests/model_checker_timeout_bmc/args new file mode 100644 index 000000000000..6c50b2711409 --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_bmc/args @@ -0,0 +1 @@ +--model-checker-engine bmc --model-checker-timeout 1000 diff --git a/test/cmdlineTests/model_checker_timeout_bmc/err b/test/cmdlineTests/model_checker_timeout_bmc/err new file mode 100644 index 000000000000..4334c5e4c60b --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_bmc/err @@ -0,0 +1,6 @@ +Warning: BMC: Assertion violation might happen here. + --> model_checker_timeout_bmc/input.sol:10:3: + | +10 | assert(r % k == 0); + | ^^^^^^^^^^^^^^^^^^ +Note: diff --git a/test/cmdlineTests/model_checker_timeout_bmc/input.sol b/test/cmdlineTests/model_checker_timeout_bmc/input.sol new file mode 100644 index 000000000000..bae0d7a93601 --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_bmc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma experimental SMTChecker; +contract test { + function f(uint x, uint y, uint k) public pure { + require(k > 0); + require(x % k == 0); + require(y % k == 0); + uint r = mulmod(x, y, k); + assert(r % k == 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/model_checker_timeout_chc/args b/test/cmdlineTests/model_checker_timeout_chc/args new file mode 100644 index 000000000000..8e3a1efb7e02 --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_chc/args @@ -0,0 +1 @@ +--model-checker-engine chc --model-checker-timeout 1000 diff --git a/test/cmdlineTests/model_checker_timeout_chc/err b/test/cmdlineTests/model_checker_timeout_chc/err new file mode 100644 index 000000000000..e731ea28d122 --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_chc/err @@ -0,0 +1,5 @@ +Warning: CHC: Assertion violation might happen here. + --> model_checker_timeout_chc/input.sol:10:3: + | +10 | assert(r % k == 0); + | ^^^^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_timeout_chc/input.sol b/test/cmdlineTests/model_checker_timeout_chc/input.sol new file mode 100644 index 000000000000..bae0d7a93601 --- /dev/null +++ b/test/cmdlineTests/model_checker_timeout_chc/input.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma experimental SMTChecker; +contract test { + function f(uint x, uint y, uint k) public pure { + require(k > 0); + require(x % k == 0); + require(y % k == 0); + uint r = mulmod(x, y, k); + assert(r % k == 0); + } +} \ No newline at end of file diff --git a/test/cmdlineTests/name_simplifier/args b/test/cmdlineTests/name_simplifier/args new file mode 100644 index 000000000000..8539e69f5e1c --- /dev/null +++ b/test/cmdlineTests/name_simplifier/args @@ -0,0 +1 @@ +--optimize --ir-optimized --metadata-hash none diff --git a/test/cmdlineTests/name_simplifier/input.sol b/test/cmdlineTests/name_simplifier/input.sol new file mode 100644 index 000000000000..39034e27564f --- /dev/null +++ b/test/cmdlineTests/name_simplifier/input.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma abicoder v2; + +// The point of this test is to check that the +// AST IDs are removed from the optimized IR +// so that they do not have a big effect on the +// optimizer if it has a bug that makes it +// depen on the actual identifiers. + +struct S { uint x; } +struct T { uint[2] y; } + +contract C { + S[2] values; + T t; + + function sumArray(S[] memory _s) public returns (uint, string memory) { + values[0].x = _s[0].x; + t.y[0] = _s[1].x; + return (t.y[0], "longstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstring"); + } +} diff --git a/test/cmdlineTests/name_simplifier/output b/test/cmdlineTests/name_simplifier/output new file mode 100644 index 000000000000..229b45910c98 --- /dev/null +++ b/test/cmdlineTests/name_simplifier/output @@ -0,0 +1,145 @@ +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "C_59" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("C_59_deployed") + codecopy(0, dataoffset("C_59_deployed"), _1) + return(0, _1) + } + } + object "C_59_deployed" { + code { + { + let _1 := 64 + mstore(_1, 128) + if iszero(lt(calldatasize(), 4)) + { + let _2 := 0 + if eq(0xf8eddcc6, shr(224, calldataload(_2))) + { + if callvalue() { revert(_2, _2) } + let _3 := 32 + if slt(add(calldatasize(), not(3)), _3) { revert(_2, _2) } + let offset := calldataload(4) + let _4 := 0xffffffffffffffff + if gt(offset, _4) { revert(_2, _2) } + if iszero(slt(add(offset, 35), calldatasize())) { revert(_2, _2) } + let _5 := calldataload(add(4, offset)) + if gt(_5, _4) { panic_error_0x41() } + let _6 := mul(_5, _3) + let dst := allocateMemory(add(_6, _3)) + let dst_1 := dst + mstore(dst, _5) + dst := add(dst, _3) + let src := add(offset, 36) + if gt(add(add(offset, _6), 36), calldatasize()) { revert(_2, _2) } + let i := _2 + for { } lt(i, _5) { i := add(i, 1) } + { + if slt(sub(calldatasize(), src), _3) { revert(_2, _2) } + let memPtr := mload(_1) + let newFreePtr := add(memPtr, _3) + if or(gt(newFreePtr, _4), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(_1, newFreePtr) + mstore(memPtr, calldataload(src)) + mstore(dst, memPtr) + dst := add(dst, _3) + src := add(src, _3) + } + let ret, ret_1 := fun_sumArray_58(dst_1) + let memPos := allocateMemory(_2) + return(memPos, sub(abi_encode_uint256_t_string(memPos, ret, ret_1), memPos)) + } + } + revert(0, 0) + } + function abi_encode_uint256_t_string(headStart, value0, value1) -> tail + { + mstore(headStart, value0) + let _1 := 32 + mstore(add(headStart, _1), 64) + let length := mload(value1) + mstore(add(headStart, 64), length) + let i := tail + for { } lt(i, length) { i := add(i, _1) } + { + mstore(add(add(headStart, i), 96), mload(add(add(value1, i), _1))) + } + if gt(i, length) + { + mstore(add(add(headStart, length), 96), tail) + } + tail := add(add(headStart, and(add(length, 31), not(31))), 96) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + function convert_t_stringliteral_6490_to_t_string() -> converted + { + let memPtr := mload(64) + let newFreePtr := add(memPtr, 160) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + converted := memPtr + mstore(memPtr, 100) + mstore(add(memPtr, 32), "longstringlongstringlongstringlo") + mstore(add(memPtr, 64), "ngstringlongstringlongstringlong") + mstore(add(memPtr, 96), "stringlongstringlongstringlongst") + mstore(add(memPtr, 128), "ring") + } + function extract_from_storage_value_dynamict_uint256(slot_value, offset) -> value + { + value := shr(mul(offset, 8), slot_value) + } + function fun_sumArray_58(vloc__s_22_mpos) -> vloc, vloc__27_mpos + { + if iszero(lt(vloc, mload(vloc__s_22_mpos))) { panic_error_0x32() } + let _1 := mload(mload(add(add(vloc__s_22_mpos, mul(vloc, 32)), 32))) + let _2, _3 := storage_array_index_access$_t_struct$_S_storage(vloc, vloc) + sstore(_2, _1) + if iszero(lt(0x01, mload(vloc__s_22_mpos))) { panic_error_0x32() } + let _4 := mload(mload(add(vloc__s_22_mpos, 64))) + if iszero(lt(vloc, 0x02)) { panic_error_0x32() } + let slot := add(0x02, vloc) + let _5 := sload(slot) + let shiftBits := mul(vloc, 8) + let mask := shl(shiftBits, not(0)) + sstore(slot, or(and(_5, not(mask)), and(shl(shiftBits, _4), mask))) + let _6, _7 := storage_array_index_access$_t_struct$_S_storage(0x02, vloc) + vloc := extract_from_storage_value_dynamict_uint256(sload(_6), _7) + vloc__27_mpos := convert_t_stringliteral_6490_to_t_string() + } + function panic_error_0x32() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x32) + revert(0, 0x24) + } + function panic_error_0x41() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + function storage_array_index_access$_t_struct$_S_storage(array, index) -> slot, offset + { + if iszero(lt(index, 0x02)) { panic_error_0x32() } + slot := add(array, index) + offset := offset + } + } + } +} diff --git a/test/cmdlineTests/object_compiler/input.sol b/test/cmdlineTests/object_compiler/input.yul similarity index 100% rename from test/cmdlineTests/object_compiler/input.sol rename to test/cmdlineTests/object_compiler/input.yul diff --git a/test/cmdlineTests/object_compiler/output b/test/cmdlineTests/object_compiler/output index f257721b963b..66ad61fb75d7 100644 --- a/test/cmdlineTests/object_compiler/output +++ b/test/cmdlineTests/object_compiler/output @@ -1,5 +1,5 @@ -======= object_compiler/input.sol (EVM) ======= +======= object_compiler/input.yul (EVM) ======= Pretty printed source: object "MyContract" { @@ -26,42 +26,42 @@ Binary representation: 33600055600b806012600039806000f350fe60005460005260206000f3 Text representation: - /* "object_compiler/input.sol":128:136 */ + /* "object_compiler/input.yul":128:136 */ caller - /* "object_compiler/input.sol":125:126 */ + /* "object_compiler/input.yul":125:126 */ 0x00 - /* "object_compiler/input.sol":118:137 */ + /* "object_compiler/input.yul":118:137 */ sstore dataSize(sub_0) - /* "object_compiler/input.sol":240:259 */ + /* "object_compiler/input.yul":240:259 */ dup1 dataOffset(sub_0) - /* "object_compiler/input.sol":125:126 */ + /* "object_compiler/input.yul":125:126 */ 0x00 - /* "object_compiler/input.sol":205:260 */ + /* "object_compiler/input.yul":205:260 */ codecopy - /* "object_compiler/input.sol":275:294 */ + /* "object_compiler/input.yul":275:294 */ dup1 - /* "object_compiler/input.sol":125:126 */ + /* "object_compiler/input.yul":125:126 */ 0x00 - /* "object_compiler/input.sol":265:295 */ + /* "object_compiler/input.yul":265:295 */ return pop stop sub_0: assembly { - /* "object_compiler/input.sol":397:398 */ + /* "object_compiler/input.yul":397:398 */ 0x00 - /* "object_compiler/input.sol":391:399 */ + /* "object_compiler/input.yul":391:399 */ sload - /* "object_compiler/input.sol":397:398 */ + /* "object_compiler/input.yul":397:398 */ 0x00 - /* "object_compiler/input.sol":381:400 */ + /* "object_compiler/input.yul":381:400 */ mstore - /* "object_compiler/input.sol":417:421 */ + /* "object_compiler/input.yul":417:421 */ 0x20 - /* "object_compiler/input.sol":397:398 */ + /* "object_compiler/input.yul":397:398 */ 0x00 - /* "object_compiler/input.sol":407:422 */ + /* "object_compiler/input.yul":407:422 */ return } diff --git a/test/cmdlineTests/optimizer_BlockDeDuplicator/output b/test/cmdlineTests/optimizer_BlockDeDuplicator/output index e5c45e53e209..3af48a2f0054 100644 --- a/test/cmdlineTests/optimizer_BlockDeDuplicator/output +++ b/test/cmdlineTests/optimizer_BlockDeDuplicator/output @@ -87,5 +87,5 @@ sub_0: assembly { tag_7: jump // out - auxdata: AUXDATA REMOVED + auxdata: } diff --git a/test/cmdlineTests/optimizer_array_sload/args b/test/cmdlineTests/optimizer_array_sload/args new file mode 100644 index 000000000000..8539e69f5e1c --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/args @@ -0,0 +1 @@ +--optimize --ir-optimized --metadata-hash none diff --git a/test/cmdlineTests/optimizer_array_sload/input.sol b/test/cmdlineTests/optimizer_array_sload/input.sol new file mode 100644 index 000000000000..94f42219a48b --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/input.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma abicoder v2; + +contract Arraysum { + uint256[] values; + + function sumArray() public view returns(uint sum) { + sum = 0; + // The optimizer should read the length of the array only once, because + // LoopInvariantCodeMotion can move the `sload` corresponding to the length outside of the + // loop. + for(uint i = 0; i < values.length; i++) + sum += values[i]; + } +} diff --git a/test/cmdlineTests/optimizer_array_sload/output b/test/cmdlineTests/optimizer_array_sload/output new file mode 100644 index 000000000000..d009c9d9a8a4 --- /dev/null +++ b/test/cmdlineTests/optimizer_array_sload/output @@ -0,0 +1,79 @@ +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "Arraysum_34" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("Arraysum_34_deployed") + codecopy(0, dataoffset("Arraysum_34_deployed"), _1) + return(0, _1) + } + } + object "Arraysum_34_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + if eq(0x81d73423, shr(224, calldataload(_1))) + { + if callvalue() { revert(_1, _1) } + if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + let vloc_sum := _1 + let vloc_i := _1 + let _2 := sload(_1) + for { } + lt(vloc_i, _2) + { + if eq(vloc_i, not(0)) { panic_error_0x11() } + vloc_i := add(vloc_i, 1) + } + { + mstore(_1, _1) + vloc_sum := checked_add_t_uint256(vloc_sum, sload(add(keccak256(_1, 0x20), vloc_i))) + } + let memPos := allocateMemory(_1) + return(memPos, sub(abi_encode_uint(memPos, vloc_sum), memPos)) + } + } + revert(0, 0) + } + function abi_encode_uint(headStart, value0) -> tail + { + tail := add(headStart, 32) + mstore(headStart, value0) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + mstore(64, newFreePtr) + } + function checked_add_t_uint256(x, y) -> sum + { + if gt(x, not(y)) { panic_error_0x11() } + sum := add(x, y) + } + function panic_error_0x11() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x11) + revert(0, 0x24) + } + } + } +} diff --git a/test/cmdlineTests/optimizer_user_yul/output b/test/cmdlineTests/optimizer_user_yul/output index 34f35c643a85..f6e7705c7959 100644 --- a/test/cmdlineTests/optimizer_user_yul/output +++ b/test/cmdlineTests/optimizer_user_yul/output @@ -48,19 +48,22 @@ tag_5: 0x02 /* "optimizer_user_yul/input.sol":359:371 sstore(2, 3) */ sstore - /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ -tag_6: /* "optimizer_user_yul/input.sol":390:391 5 */ 0x05 /* "optimizer_user_yul/input.sol":384:392 sload(5) */ sload - tag_9 - jumpi - jump(tag_8) -tag_9: + iszero + /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ +tag_6: + /* "optimizer_user_yul/input.sol":384:392 sload(5) */ + dup1 /* "optimizer_user_yul/input.sol":376:509 for { } sload(5) { } {... */ + tag_8 + jumpi jump(tag_6) tag_8: + /* "optimizer_user_yul/input.sol":380:383 { } */ + pop /* "optimizer_user_yul/input.sol":340:513 {... */ pop /* "optimizer_user_yul/input.sol":60:518 contract C... */ @@ -80,5 +83,5 @@ sub_0: assembly { dup1 revert - auxdata: AUXDATA REMOVED + auxdata: } diff --git a/test/cmdlineTests/output_selection_all_A1/input.json b/test/cmdlineTests/output_selection_all_A1/input.json index a7a84163de96..02c04eb2dfd0 100644 --- a/test/cmdlineTests/output_selection_all_A1/input.json +++ b/test/cmdlineTests/output_selection_all_A1/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" }, "b.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" } }, "settings": { diff --git a/test/cmdlineTests/output_selection_all_A1/output.json b/test/cmdlineTests/output_selection_all_A1/output.json index 1e4a0b11f85b..5592b43f0d92 100644 --- a/test/cmdlineTests/output_selection_all_A1/output.json +++ b/test/cmdlineTests/output_selection_all_A1/output.json @@ -1,6 +1 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}},"b.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"3420","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"2018","formattedMessage":"b.sol:2:15: Warning: Function state mutability can be restricted to pure -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } - ^------------------------------------------^ -","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":93,"file":"b.sol","start":49},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"object":""}}}},"b.sol":{"A1":{"evm":{"bytecode":{"object":""}}}}},"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_all_A2/input.json b/test/cmdlineTests/output_selection_all_A2/input.json index 3107a5e4c783..791d787d5e60 100644 --- a/test/cmdlineTests/output_selection_all_A2/input.json +++ b/test/cmdlineTests/output_selection_all_A2/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" }, "b.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" } }, "settings": { diff --git a/test/cmdlineTests/output_selection_all_A2/output.json b/test/cmdlineTests/output_selection_all_A2/output.json index 5b0b55bbf6aa..d300efa1cbce 100644 --- a/test/cmdlineTests/output_selection_all_A2/output.json +++ b/test/cmdlineTests/output_selection_all_A2/output.json @@ -1,6 +1 @@ -{"contracts":{"a.sol":{"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"3420","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"2018","formattedMessage":"b.sol:2:15: Warning: Function state mutability can be restricted to pure -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } - ^------------------------------------------^ -","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":93,"file":"b.sol","start":49},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"a.sol":{"A2":{"evm":{"bytecode":{"object":""}}}}},"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_all_blank/input.json b/test/cmdlineTests/output_selection_all_blank/input.json index 85bc114b3317..e65c7ff44ab8 100644 --- a/test/cmdlineTests/output_selection_all_blank/input.json +++ b/test/cmdlineTests/output_selection_all_blank/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" }, "b.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" } }, "settings": { diff --git a/test/cmdlineTests/output_selection_all_blank/output.json b/test/cmdlineTests/output_selection_all_blank/output.json index 32c04bf8ed9c..880514054008 100644 --- a/test/cmdlineTests/output_selection_all_blank/output.json +++ b/test/cmdlineTests/output_selection_all_blank/output.json @@ -1,6 +1 @@ -{"errors":[{"component":"general","errorCode":"3420","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"3420","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"2018","formattedMessage":"b.sol:2:15: Warning: Function state mutability can be restricted to pure -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } - ^------------------------------------------^ -","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":93,"file":"b.sol","start":49},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_all_star/input.json b/test/cmdlineTests/output_selection_all_star/input.json index 450286311a48..9c475aa08c61 100644 --- a/test/cmdlineTests/output_selection_all_star/input.json +++ b/test/cmdlineTests/output_selection_all_star/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" }, "b.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" } }, "settings": { diff --git a/test/cmdlineTests/output_selection_all_star/output.json b/test/cmdlineTests/output_selection_all_star/output.json index d885876d905e..17440150061c 100644 --- a/test/cmdlineTests/output_selection_all_star/output.json +++ b/test/cmdlineTests/output_selection_all_star/output.json @@ -1,6 +1 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}},"b.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"B2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"3420","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"2018","formattedMessage":"b.sol:2:15: Warning: Function state mutability can be restricted to pure -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } - ^------------------------------------------^ -","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":93,"file":"b.sol","start":49},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"object":""}}},"A2":{"evm":{"bytecode":{"object":""}}}},"b.sol":{"A1":{"evm":{"bytecode":{"object":""}}},"B2":{"evm":{"bytecode":{"object":""}}}}},"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_single_A1/input.json b/test/cmdlineTests/output_selection_single_A1/input.json index 2ea69a594f40..964f3f065990 100644 --- a/test/cmdlineTests/output_selection_single_A1/input.json +++ b/test/cmdlineTests/output_selection_single_A1/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" }, "b.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" } }, "settings": { diff --git a/test/cmdlineTests/output_selection_single_A1/output.json b/test/cmdlineTests/output_selection_single_A1/output.json index b32b519ddbb1..43d5be4069a2 100644 --- a/test/cmdlineTests/output_selection_single_A1/output.json +++ b/test/cmdlineTests/output_selection_single_A1/output.json @@ -1,2 +1 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"object":""}}}}},"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_single_B1/input.json b/test/cmdlineTests/output_selection_single_B1/input.json index f25a1b57dce5..216d3f08b38b 100644 --- a/test/cmdlineTests/output_selection_single_B1/input.json +++ b/test/cmdlineTests/output_selection_single_B1/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" }, "b.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" } }, "settings": { diff --git a/test/cmdlineTests/output_selection_single_B1/output.json b/test/cmdlineTests/output_selection_single_B1/output.json index aeb29d7aefb5..101f2e7a20b0 100644 --- a/test/cmdlineTests/output_selection_single_B1/output.json +++ b/test/cmdlineTests/output_selection_single_B1/output.json @@ -1,5 +1 @@ -{"contracts":{"b.sol":{"B2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"b.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"b.sol","start":-1},"type":"Warning"},{"component":"general","errorCode":"2018","formattedMessage":"b.sol:2:15: Warning: Function state mutability can be restricted to pure -contract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } } - ^------------------------------------------^ -","message":"Function state mutability can be restricted to pure","severity":"warning","sourceLocation":{"end":93,"file":"b.sol","start":49},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"b.sol":{"B2":{"evm":{"bytecode":{"object":""}}}}},"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/output_selection_single_all/input.json b/test/cmdlineTests/output_selection_single_all/input.json index a4137b063cdf..ae8304f02cad 100644 --- a/test/cmdlineTests/output_selection_single_all/input.json +++ b/test/cmdlineTests/output_selection_single_all/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function a(uint x) public pure { assert(x > 0); } } contract A2 { function a(uint x) public pure { assert(x > 0); } }" }, "b.sol": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A1 { function b(uint x) public { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A1 { function b(uint x) public pure { assert(x > 0); } } contract B2 { function b(uint x) public pure { assert(x > 0); } }" } }, "settings": { diff --git a/test/cmdlineTests/output_selection_single_all/output.json b/test/cmdlineTests/output_selection_single_all/output.json index 07b1a5453d7e..e04588cb4708 100644 --- a/test/cmdlineTests/output_selection_single_all/output.json +++ b/test/cmdlineTests/output_selection_single_all/output.json @@ -1,2 +1 @@ -{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}},"A2":{"evm":{"bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} +{"contracts":{"a.sol":{"A1":{"evm":{"bytecode":{"object":""}}},"A2":{"evm":{"bytecode":{"object":""}}}}},"sources":{"a.sol":{"id":0},"b.sol":{"id":1}}} diff --git a/test/cmdlineTests/recovery_ast_constructor/args b/test/cmdlineTests/recovery_ast_constructor/args index c5786054e5c2..c7bde99421ca 100644 --- a/test/cmdlineTests/recovery_ast_constructor/args +++ b/test/cmdlineTests/recovery_ast_constructor/args @@ -1 +1 @@ ---error-recovery --ast-json --hashes +--error-recovery --ast-compact-json --hashes diff --git a/test/cmdlineTests/recovery_ast_constructor/output b/test/cmdlineTests/recovery_ast_constructor/output index d4a7d02884b6..5c11b5b99252 100644 --- a/test/cmdlineTests/recovery_ast_constructor/output +++ b/test/cmdlineTests/recovery_ast_constructor/output @@ -1,239 +1,181 @@ -JSON AST: +JSON AST (compact format): ======= recovery_ast_constructor/input.sol ======= { - "attributes": + "absolutePath": "recovery_ast_constructor/input.sol", + "exportedSymbols": { - "absolutePath": "recovery_ast_constructor/input.sol", - "exportedSymbols": - { - "Error1": - [ - 18 - ] - }, - "license": "GPL-3.0" + "Error1": + [ + 18 + ] }, - "children": + "id": 19, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": [ { - "attributes": - { - "literals": - [ - "solidity", - ">=", - "0.0", - ".0" - ] - }, "id": 1, - "name": "PragmaDirective", + "literals": + [ + "solidity", + ">=", + "0.0", + ".0" + ], + "nodeType": "PragmaDirective", "src": "36:24:0" }, { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 18 - ], - "name": "Error1", - "scope": 19 - }, - "children": + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 18, + "linearizedBaseContracts": + [ + 18 + ], + "name": "Error1", + "nodeType": "ContractDefinition", + "nodes": [ { - "attributes": + "body": { - "implemented": true, - "isConstructor": true, - "kind": "constructor", - "modifiers": + "id": 8, + "nodeType": "Block", + "src": "96:49:0", + "statements": [ null - ], - "name": "", - "scope": 18, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" + ] }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "93:2:0" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "ParameterList", - "src": "96:0:0" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 8, - "name": "Block", - "src": "96:49:0" - } - ], "id": 9, - "name": "FunctionDefinition", - "src": "82:63:0" + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "93:2:0" + }, + "returnParameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": [], + "src": "96:0:0" + }, + "scope": 18, + "src": "82:63:0", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" }, { - "attributes": + "body": { - "functionSelector": "af11c34c", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": + "id": 16, + "nodeType": "Block", + "src": "440:19:0", + "statements": [ - null - ], - "name": "five", - "scope": 18, - "stateMutability": "view", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": { - "parameters": - [ - null - ] - }, - "children": [], - "id": 10, - "name": "ParameterList", - "src": "411:2:0" - }, - { - "children": - [ + "expression": { - "attributes": + "hexValue": "35", + "id": 14, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "453:1:0", + "typeDescriptions": { - "constant": false, - "mutability": "mutable", - "name": "", - "scope": 17, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" + "typeIdentifier": "t_rational_5_by_1", + "typeString": "int_const 5" }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 11, - "name": "ElementaryTypeName", - "src": "434:4:0" - } - ], - "id": 12, - "name": "VariableDeclaration", - "src": "434:4:0" - } - ], - "id": 13, - "name": "ParameterList", - "src": "433:6:0" - }, - { - "children": - [ + "value": "5" + }, + "functionReturnParameters": 13, + "id": 15, + "nodeType": "Return", + "src": "446:8:0" + } + ] + }, + "functionSelector": "af11c34c", + "id": 17, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "five", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 10, + "nodeType": "ParameterList", + "parameters": [], + "src": "411:2:0" + }, + "returnParameters": + { + "id": 13, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 12, + "mutability": "mutable", + "name": "", + "nodeType": "VariableDeclaration", + "scope": 17, + "src": "434:4:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { - "attributes": + "id": 11, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "434:4:0", + "typeDescriptions": { - "functionReturnParameters": 13 - }, - "children": - [ - { - "attributes": - { - "hexvalue": "35", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 5", - "value": "5" - }, - "id": 14, - "name": "Literal", - "src": "453:1:0" - } - ], - "id": 15, - "name": "Return", - "src": "446:8:0" - } - ], - "id": 16, - "name": "Block", - "src": "440:19:0" - } - ], - "id": 17, - "name": "FunctionDefinition", - "src": "398:61:0" + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "433:6:0" + }, + "scope": 18, + "src": "398:61:0", + "stateMutability": "view", + "virtual": false, + "visibility": "public" } ], - "id": 18, - "name": "ContractDefinition", + "scope": 19, "src": "62:399:0" } ], - "id": 19, - "name": "SourceUnit", "src": "36:426:0" } diff --git a/test/cmdlineTests/recovery_ast_empty_contract/err b/test/cmdlineTests/recovery_ast_empty_contract/err index 588067877fc1..f7324da63db6 100644 --- a/test/cmdlineTests/recovery_ast_empty_contract/err +++ b/test/cmdlineTests/recovery_ast_empty_contract/err @@ -1,4 +1,4 @@ -Error: Expected pragma, import directive or contract/interface/library/struct/enum definition. +Error: Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition. --> recovery_ast_empty_contract/input.sol:3:1: | 3 | c diff --git a/test/cmdlineTests/recovery_standard_json/output.json b/test/cmdlineTests/recovery_standard_json/output.json index a4113e18679e..42926ae173e4 100644 --- a/test/cmdlineTests/recovery_standard_json/output.json +++ b/test/cmdlineTests/recovery_standard_json/output.json @@ -1,7 +1,13 @@ -{"errors":[{"component":"general","errorCode":"3546","formattedMessage":"A:2:58: ParserError: Expected type name -pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ } - ^ -","message":"Expected type name","severity":"error","sourceLocation":{"end":94,"file":"A","start":93},"type":"ParserError"},{"component":"general","errorCode":"3796","formattedMessage":"A:2:84: Warning: Recovered in ContractDefinition at '}'. -pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ } - ^ -","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}} +{"errors":[{"component":"general","errorCode":"3546","formattedMessage":"ParserError: Expected type name + --> A:2:58: + | +2 | pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ } + | ^ + +","message":"Expected type name","severity":"error","sourceLocation":{"end":94,"file":"A","start":93},"type":"ParserError"},{"component":"general","errorCode":"3796","formattedMessage":"Warning: Recovered in ContractDefinition at '}'. + --> A:2:84: + | +2 | pragma solidity >=0.0; contract Errort6 { using foo for ; /* missing type name */ } + | ^ + +","message":"Recovered in ContractDefinition at '}'.","severity":"warning","sourceLocation":{"end":120,"file":"A","start":119},"type":"Warning"}],"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"Errort6":[3]},"id":4,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0"}],"src":"36:84:0"},"id":0}}} diff --git a/test/cmdlineTests/standard_empty_file_name/output.json b/test/cmdlineTests/standard_empty_file_name/output.json index 04bc54a92a9f..7a70b6aff5b8 100644 --- a/test/cmdlineTests/standard_empty_file_name/output.json +++ b/test/cmdlineTests/standard_empty_file_name/output.json @@ -1,4 +1,3 @@ -{"errors":[{"component":"general","errorCode":"2904","formattedMessage":":2:24: DeclarationError: Declaration \"A\" not found in \"\" (referenced as \".\"). -pragma solidity >=0.0; import {A} from \".\"; - ^------------------^ -","message":"Declaration \"A\" not found in \"\" (referenced as \".\").","severity":"error","type":"DeclarationError"}],"sources":{}} +{"errors":[{"component":"general","errorCode":"2904","formattedMessage":"DeclarationError: Declaration \"A\" not found in \"\" (referenced as \".\"). + +","message":"Declaration \"A\" not found in \"\" (referenced as \".\").","severity":"error","type":"DeclarationError"}],"sources":{"":{"id":0}}} diff --git a/test/cmdlineTests/standard_eWasm_requested/input.json b/test/cmdlineTests/standard_ewasm_requested/input.json similarity index 72% rename from test/cmdlineTests/standard_eWasm_requested/input.json rename to test/cmdlineTests/standard_ewasm_requested/input.json index 25cc1705dfd9..53dcc68eefe7 100644 --- a/test/cmdlineTests/standard_eWasm_requested/input.json +++ b/test/cmdlineTests/standard_ewasm_requested/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { }" + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { }" } }, "settings": @@ -16,7 +16,7 @@ }, "outputSelection": { - "*": { "*": ["ewasm.wast"] } + "*": { "*": ["ewasm.wast", "ewasm.wasm"] } } } } diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_ewasm_requested/output.json similarity index 52% rename from test/cmdlineTests/standard_eWasm_requested/output.json rename to test/cmdlineTests/standard_ewasm_requested/output.json index a3885d128238..b63bc50dc655 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_ewasm_requested/output.json @@ -1,5 +1,7 @@ -{"contracts":{"A":{"C":{"ewasm":{"wast":"(module - ;; sub-module \"C_2_deployed\" will be encoded as custom section in binary here, but is skipped in text mode. +{"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d01000000013a0860000060017e017e60047e7e7e7e017f60087e7e7e7e7e7e7e7e00600c7e7e7e7e7e7e7e7e7e7e7e7e0060017f0060027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000508657468657265756d0666696e6973680006030a090002020101010403030503010001060100071102066d656d6f72790200046d61696e0004009d030c435f335f6465706c6f7965640061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b0aec0309dc0103017e027f057e02404200210020002000200042c00010052101200141c0006a210220022001490440000b20001009210320022003370000200241086a2003370000200241106a2003370000200241186a428001100937000041001002410029000010092104410041086a29000010092105410041106a2900001009210620042005842006410041186a290000100984845045044020002000200020002000200020002000100c0b4290032107200020002000200020002000200042ce012000200020002007100a20002000200020002000200020002007100b0b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b2601027f0240200020012002200310052105200541c0006a210420042005490440000b0b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100742108621022002200042108810078421010b20010b1e01027e02402000100842208621022002200042208810088421010b20010b25000240200020012002200310062004200520062007100520082009200a200b100510000b0b1b000240200020012002200310062004200520062007100510030b0b1b000240200020012002200310062004200520062007100510010b0b","wast":"(module + ;; custom section for sub-module + ;; The Keccak-256 hash of the text representation of \"C_3_deployed\": 0289c074ac70ccfdbeb7817862087cc066a9f7707de1a981bb8b5b12dd2ce4e9 + ;; (@custom \"C_3_deployed\" \"0061736d0100000001160460000060017e017e60047e7e7e7e017f60027f7f0002130108657468657265756d067265766572740003030504000201010503010001060100071102066d656d6f72790200046d61696e00010ab60204ca0104017e027f057e037f02404200210020002000200042c00010022101200141c0006a210220022001490440000b20001003421086210320032000421088100384422086210420042000422088100484210520022005370000200241086a2005370000200241106a20053700004280011003421086210620064280014210881003844220862107200241186a2007428001422088100484370000200020002000200010022108200020002000200010022109200941c0006a210a200a2009490440000b200a200810000b0b2901017f024042002000200184200284520440000b42002003422088520440000b2003a721040b20040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100342108621022002200042108810038421010b20010b\") (import \"ethereum\" \"codeCopy\" (func $eth.codeCopy (param i32 i32 i32))) (import \"ethereum\" \"revert\" (func $eth.revert (param i32 i32))) (import \"ethereum\" \"getCallValue\" (func $eth.getCallValue (param i32))) @@ -22,19 +24,19 @@ (local.set $r (i32.add (local.get $p) (i32.const 64))) (if (i32.lt_u (local.get $r) (local.get $p)) (then (unreachable))) - (local.set $_2 (call $endian_swap (local.get $_1))) + (local.set $_2 (call $bswap64 (local.get $_1))) (i64.store (local.get $r) (local.get $_2)) (i64.store (i32.add (local.get $r) (i32.const 8)) (local.get $_2)) (i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $_2)) - (i64.store (i32.add (local.get $r) (i32.const 24)) (call $endian_swap (i64.const 128))) + (i64.store (i32.add (local.get $r) (i32.const 24)) (call $bswap64 (i64.const 128))) (call $eth.getCallValue (i32.const 0)) - (local.set $z1 (call $endian_swap (i64.load (i32.const 0)))) - (local.set $z2 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8))))) - (local.set $z3 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16))))) - (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then + (local.set $z1 (call $bswap64 (i64.load (i32.const 0)))) + (local.set $z2 (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 8))))) + (local.set $z3 (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 16))))) + (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $bswap64 (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then (call $revert (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)))) - (local.set $_3 (datasize \"C_2_deployed\")) - (call $codecopy (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\") (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) + (local.set $_3 (datasize \"C_3_deployed\")) + (call $codecopy (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_3_deployed\") (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) (call $return (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3)) ) ) @@ -75,61 +77,61 @@ (local.get $r) ) -(func $codecopy - (param $x1 i64) - (param $x2 i64) - (param $x3 i64) - (param $x4 i64) - (param $y1 i64) - (param $y2 i64) - (param $y3 i64) - (param $y4 i64) - (param $z1 i64) - (param $z2 i64) - (param $z3 i64) - (param $z4 i64) - (block $label__3 - (call $eth.codeCopy (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4))) - ) -) - -(func $endian_swap_16 +(func $bswap16 (param $x i64) (result i64) (local $y i64) - (block $label__4 + (block $label__3 (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) ) (local.get $y) ) -(func $endian_swap_32 +(func $bswap32 (param $x i64) (result i64) (local $y i64) (local $hi i64) - (block $label__5 - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) + (block $label__4 + (local.set $hi (i64.shl (call $bswap16 (local.get $x)) (i64.const 16))) + (local.set $y (i64.or (local.get $hi) (call $bswap16 (i64.shr_u (local.get $x) (i64.const 16))))) ) (local.get $y) ) -(func $endian_swap +(func $bswap64 (param $x i64) (result i64) (local $y i64) (local $hi i64) - (block $label__6 - (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) - (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) + (block $label__5 + (local.set $hi (i64.shl (call $bswap32 (local.get $x)) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $bswap32 (i64.shr_u (local.get $x) (i64.const 32))))) ) (local.get $y) ) +(func $codecopy + (param $x1 i64) + (param $x2 i64) + (param $x3 i64) + (param $x4 i64) + (param $y1 i64) + (param $y2 i64) + (param $y3 i64) + (param $y4 i64) + (param $z1 i64) + (param $z2 i64) + (param $z3 i64) + (param $z4 i64) + (block $label__6 + (call $eth.codeCopy (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4))) + ) +) + (func $return (param $x1 i64) (param $x2 i64) diff --git a/test/cmdlineTests/standard_ewasm_requested_abstract/input.json b/test/cmdlineTests/standard_ewasm_requested_abstract/input.json new file mode 100644 index 000000000000..2ee666fe2d58 --- /dev/null +++ b/test/cmdlineTests/standard_ewasm_requested_abstract/input.json @@ -0,0 +1,22 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; abstract contract C { }" + } + }, + "settings": + { + "optimizer": + { + "enabled": true, + "details": {"yul": true} + }, + "outputSelection": + { + "*": { "*": ["ewasm.wast", "ewasm.wasm"] } + } + } +} diff --git a/test/cmdlineTests/standard_ewasm_requested_abstract/output.json b/test/cmdlineTests/standard_ewasm_requested_abstract/output.json new file mode 100644 index 000000000000..2858515dfa3f --- /dev/null +++ b/test/cmdlineTests/standard_ewasm_requested_abstract/output.json @@ -0,0 +1 @@ +{"contracts":{"A":{"C":{"ewasm":{"wasm":"","wast":""}}}},"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_file_not_found/args b/test/cmdlineTests/standard_file_not_found/args new file mode 100644 index 000000000000..69ea051f14c6 --- /dev/null +++ b/test/cmdlineTests/standard_file_not_found/args @@ -0,0 +1 @@ +--standard-json \ No newline at end of file diff --git a/test/cmdlineTests/standard_file_not_found/err b/test/cmdlineTests/standard_file_not_found/err new file mode 100644 index 000000000000..e2f23b9f3159 --- /dev/null +++ b/test/cmdlineTests/standard_file_not_found/err @@ -0,0 +1 @@ +File not found: standard_file_not_found/input.sol diff --git a/test/cmdlineTests/standard_file_not_found/exit b/test/cmdlineTests/standard_file_not_found/exit new file mode 100644 index 000000000000..56a6051ca2b0 --- /dev/null +++ b/test/cmdlineTests/standard_file_not_found/exit @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/test/cmdlineTests/standard_generatedSources/input.json b/test/cmdlineTests/standard_generatedSources/input.json new file mode 100644 index 000000000000..a179db435bd2 --- /dev/null +++ b/test/cmdlineTests/standard_generatedSources/input.json @@ -0,0 +1,20 @@ +{ + "language": "Solidity", + "sources": { + "a.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma abicoder v2; contract A { function f(uint[] memory) public view returns (uint256) { } }" + } + }, + "settings": { + "evmVersion": "petersburg", + "outputSelection": { + "*": { + "A": [ + "evm.bytecode.object", + "evm.deployedBytecode.generatedSources", + "evm.bytecode.generatedSources" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_generatedSources/output.json b/test/cmdlineTests/standard_generatedSources/output.json new file mode 100644 index 000000000000..78a68d02c26b --- /dev/null +++ b/test/cmdlineTests/standard_generatedSources/output.json @@ -0,0 +1,92 @@ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:2886:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:520:1","statements":[{"nodeType":"YulAssignment","src":"136:89:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"217:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"160:56:1"},"nodeType":"YulFunctionCall","src":"160:64:1"}],"functionName":{"name":"allocateMemory","nodeType":"YulIdentifier","src":"145:14:1"},"nodeType":"YulFunctionCall","src":"145:80:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"234:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"245:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"238:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"266:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"273:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"259:6:1"},"nodeType":"YulFunctionCall","src":"259:21:1"},"nodeType":"YulExpressionStatement","src":"259:21:1"},{"nodeType":"YulAssignment","src":"281:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"292:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"299:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"288:3:1"},"nodeType":"YulFunctionCall","src":"288:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"281:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"313:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"324:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"317:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"379:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"388:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"391:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"381:6:1"},"nodeType":"YulFunctionCall","src":"381:12:1"},"nodeType":"YulExpressionStatement","src":"381:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"349:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"358:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"366:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"354:3:1"},"nodeType":"YulFunctionCall","src":"354:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"345:3:1"},"nodeType":"YulFunctionCall","src":"345:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"374:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"342:2:1"},"nodeType":"YulFunctionCall","src":"342:36:1"},"nodeType":"YulIf","src":"339:2:1"},{"body":{"nodeType":"YulBlock","src":"464:176:1","statements":[{"nodeType":"YulVariableDeclaration","src":"478:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"496:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"482:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"519:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"545:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"557:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"524:20:1"},"nodeType":"YulFunctionCall","src":"524:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"512:6:1"},"nodeType":"YulFunctionCall","src":"512:50:1"},"nodeType":"YulExpressionStatement","src":"512:50:1"},{"nodeType":"YulAssignment","src":"575:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"586:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"591:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"582:3:1"},"nodeType":"YulFunctionCall","src":"582:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"575:3:1"}]},{"nodeType":"YulAssignment","src":"609:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"620:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"625:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"616:3:1"},"nodeType":"YulFunctionCall","src":"616:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"609:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"426:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"429:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"423:2:1"},"nodeType":"YulFunctionCall","src":"423:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"437:18:1","statements":[{"nodeType":"YulAssignment","src":"439:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"448:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"451:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"444:3:1"},"nodeType":"YulFunctionCall","src":"444:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"439:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"408:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"410:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"419:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"414:1:1","type":""}]}]},"src":"404:236:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"96:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"104:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"112:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"120:5:1","type":""}],"src":"24:622:1"},{"body":{"nodeType":"YulBlock","src":"746:226:1","statements":[{"body":{"nodeType":"YulBlock","src":"795:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"804:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"807:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"797:6:1"},"nodeType":"YulFunctionCall","src":"797:12:1"},"nodeType":"YulExpressionStatement","src":"797:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"774:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"782:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"770:3:1"},"nodeType":"YulFunctionCall","src":"770:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"789:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"766:3:1"},"nodeType":"YulFunctionCall","src":"766:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"759:6:1"},"nodeType":"YulFunctionCall","src":"759:35:1"},"nodeType":"YulIf","src":"756:2:1"},{"nodeType":"YulVariableDeclaration","src":"820:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"847:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"834:12:1"},"nodeType":"YulFunctionCall","src":"834:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"824:6:1","type":""}]},{"nodeType":"YulAssignment","src":"863:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"939:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"947:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"935:3:1"},"nodeType":"YulFunctionCall","src":"935:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"954:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"962:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"872:62:1"},"nodeType":"YulFunctionCall","src":"872:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"863:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"724:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"732:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"740:5:1","type":""}],"src":"669:303:1"},{"body":{"nodeType":"YulBlock","src":"1030:87:1","statements":[{"nodeType":"YulAssignment","src":"1040:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1062:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1049:12:1"},"nodeType":"YulFunctionCall","src":"1049:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1040:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1105:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1078:26:1"},"nodeType":"YulFunctionCall","src":"1078:33:1"},"nodeType":"YulExpressionStatement","src":"1078:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1008:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1016:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1024:5:1","type":""}],"src":"978:139:1"},{"body":{"nodeType":"YulBlock","src":"1214:314:1","statements":[{"body":{"nodeType":"YulBlock","src":"1260:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1269:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1272:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1262:6:1"},"nodeType":"YulFunctionCall","src":"1262:12:1"},"nodeType":"YulExpressionStatement","src":"1262:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1235:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1244:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1231:3:1"},"nodeType":"YulFunctionCall","src":"1231:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1256:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1227:3:1"},"nodeType":"YulFunctionCall","src":"1227:32:1"},"nodeType":"YulIf","src":"1224:2:1"},{"nodeType":"YulBlock","src":"1286:235:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1301:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1332:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1343:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1328:3:1"},"nodeType":"YulFunctionCall","src":"1328:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1315:12:1"},"nodeType":"YulFunctionCall","src":"1315:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1305:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1393:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1402:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1405:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1395:6:1"},"nodeType":"YulFunctionCall","src":"1395:12:1"},"nodeType":"YulExpressionStatement","src":"1395:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1365:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1373:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1362:2:1"},"nodeType":"YulFunctionCall","src":"1362:30:1"},"nodeType":"YulIf","src":"1359:2:1"},{"nodeType":"YulAssignment","src":"1423:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1483:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1494:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1479:3:1"},"nodeType":"YulFunctionCall","src":"1479:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1503:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1433:45:1"},"nodeType":"YulFunctionCall","src":"1433:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1423:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1184:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1195:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1207:6:1","type":""}],"src":"1123:405:1"},{"body":{"nodeType":"YulBlock","src":"1599:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1616:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1639:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1621:17:1"},"nodeType":"YulFunctionCall","src":"1621:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1609:6:1"},"nodeType":"YulFunctionCall","src":"1609:37:1"},"nodeType":"YulExpressionStatement","src":"1609:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1587:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1594:3:1","type":""}],"src":"1534:118:1"},{"body":{"nodeType":"YulBlock","src":"1756:124:1","statements":[{"nodeType":"YulAssignment","src":"1766:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1778:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1789:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1774:3:1"},"nodeType":"YulFunctionCall","src":"1774:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1766:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"1846:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1859:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1870:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1855:3:1"},"nodeType":"YulFunctionCall","src":"1855:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"1802:43:1"},"nodeType":"YulFunctionCall","src":"1802:71:1"},"nodeType":"YulExpressionStatement","src":"1802:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1728:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1740:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1751:4:1","type":""}],"src":"1658:222:1"},{"body":{"nodeType":"YulBlock","src":"1926:243:1","statements":[{"nodeType":"YulAssignment","src":"1936:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1952:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"1946:5:1"},"nodeType":"YulFunctionCall","src":"1946:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"1936:6:1"}]},{"nodeType":"YulVariableDeclaration","src":"1964:35:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"1986:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"1994:4:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1982:3:1"},"nodeType":"YulFunctionCall","src":"1982:17:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"1968:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"2110:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2112:16:1"},"nodeType":"YulFunctionCall","src":"2112:18:1"},"nodeType":"YulExpressionStatement","src":"2112:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2053:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2065:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2050:2:1"},"nodeType":"YulFunctionCall","src":"2050:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2089:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"2101:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"2086:2:1"},"nodeType":"YulFunctionCall","src":"2086:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2047:2:1"},"nodeType":"YulFunctionCall","src":"2047:62:1"},"nodeType":"YulIf","src":"2044:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2148:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2152:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2141:6:1"},"nodeType":"YulFunctionCall","src":"2141:22:1"},"nodeType":"YulExpressionStatement","src":"2141:22:1"}]},"name":"allocateMemory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"1910:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"1919:6:1","type":""}],"src":"1886:283:1"},{"body":{"nodeType":"YulBlock","src":"2257:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2362:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2364:16:1"},"nodeType":"YulFunctionCall","src":"2364:18:1"},"nodeType":"YulExpressionStatement","src":"2364:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2334:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2342:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2331:2:1"},"nodeType":"YulFunctionCall","src":"2331:30:1"},"nodeType":"YulIf","src":"2328:2:1"},{"nodeType":"YulAssignment","src":"2394:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2406:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2414:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2402:3:1"},"nodeType":"YulFunctionCall","src":"2402:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2394:4:1"}]},{"nodeType":"YulAssignment","src":"2456:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2468:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2474:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2464:3:1"},"nodeType":"YulFunctionCall","src":"2464:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2456:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2241:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2252:4:1","type":""}],"src":"2175:311:1"},{"body":{"nodeType":"YulBlock","src":"2537:32:1","statements":[{"nodeType":"YulAssignment","src":"2547:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2558:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2547:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2519:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2529:7:1","type":""}],"src":"2492:77:1"},{"body":{"nodeType":"YulBlock","src":"2603:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2620:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2623:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2613:6:1"},"nodeType":"YulFunctionCall","src":"2613:88:1"},"nodeType":"YulExpressionStatement","src":"2613:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2717:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"2720:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2710:6:1"},"nodeType":"YulFunctionCall","src":"2710:15:1"},"nodeType":"YulExpressionStatement","src":"2710:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2741:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2744:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"2734:6:1"},"nodeType":"YulFunctionCall","src":"2734:15:1"},"nodeType":"YulExpressionStatement","src":"2734:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"2575:180:1"},{"body":{"nodeType":"YulBlock","src":"2804:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"2861:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2870:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2873:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"2863:6:1"},"nodeType":"YulFunctionCall","src":"2863:12:1"},"nodeType":"YulExpressionStatement","src":"2863:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"2827:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"2852:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"2834:17:1"},"nodeType":"YulFunctionCall","src":"2834:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"2824:2:1"},"nodeType":"YulFunctionCall","src":"2824:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"2817:6:1"},"nodeType":"YulFunctionCall","src":"2817:43:1"},"nodeType":"YulIf","src":"2814:2:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2797:5:1","type":""}],"src":"2761:122:1"}]},"contents":"{ + + // uint256[] + function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { + array := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length)) + let dst := array + mstore(array, length) dst := add(array, 0x20) + let src := offset + if gt(add(src, mul(length, 0x20)), end) { revert(0, 0) } + for { let i := 0 } lt(i, length) { i := add(i, 1) } + { + let elementPos := src + mstore(dst, abi_decode_t_uint256(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x20) + } + } + + // uint256[] + function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } + let length := calldataload(offset) + array := abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(add(offset, 0x20), length, end) + } + + function abi_decode_t_uint256(offset, end) -> value { + value := calldataload(offset) + validator_revert_t_uint256(value) + } + + function abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr(headStart, dataEnd) -> value0 { + if slt(sub(dataEnd, headStart), 32) { revert(0, 0) } + + { + + let offset := calldataload(add(headStart, 0)) + if gt(offset, 0xffffffffffffffff) { revert(0, 0) } + + value0 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(headStart, offset), dataEnd) + } + + } + + function abi_encode_t_uint256_to_t_uint256_fromStack(value, pos) { + mstore(pos, cleanup_t_uint256(value)) + } + + function abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed(headStart , value0) -> tail { + tail := add(headStart, 32) + + abi_encode_t_uint256_to_t_uint256_fromStack(value0, add(headStart, 0)) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length) -> size { + // Make sure we can allocate memory without overflow + if gt(length, 0xffffffffffffffff) { panic_error_0x41() } + + size := mul(length, 0x20) + + // add length slot + size := add(size, 0x20) + + } + + function cleanup_t_uint256(value) -> cleaned { + cleaned := value + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function validator_revert_t_uint256(value) { + if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) } + } + +} +","id":1,"language":"Yul","name":"#utility.yul"}]}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"Warning: Source file does not specify required compiler version! +--> a.sol + +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0}}} diff --git a/test/cmdlineTests/standard_immutable_references/input.json b/test/cmdlineTests/standard_immutable_references/input.json index d81365c8adc2..dd3a77a8d491 100644 --- a/test/cmdlineTests/standard_immutable_references/input.json +++ b/test/cmdlineTests/standard_immutable_references/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "a.sol": { - "content": "// SPDX-License-Identifier: GPL-3.0\ncontract A { uint256 immutable x = 1; function f() public view returns (uint256) { return x; } }" + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { uint256 immutable x = 1 + 3; function f() public pure returns (uint256) { return x; } }" } }, "settings": { diff --git a/test/cmdlineTests/standard_immutable_references/output.json b/test/cmdlineTests/standard_immutable_references/output.json index 48376adc5860..9742233e6b83 100644 --- a/test/cmdlineTests/standard_immutable_references/output.json +++ b/test/cmdlineTests/standard_immutable_references/output.json @@ -1,2 +1 @@ -{"contracts":{"a.sol":{"A":{"evm":{"deployedBytecode":{"immutableReferences":{"3":[{"length":32,"start":77}]},"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"36:96:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;74:56;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;108:7;126:1;119:8;;74:56;:::o"}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0}}} +{"contracts":{"a.sol":{"A":{"evm":{"deployedBytecode":{"immutableReferences":{"6":[{"length":32,"start":75}]}}}}}},"sources":{"a.sol":{"id":0}}} diff --git a/test/cmdlineTests/standard_irOptimized_requested/input.json b/test/cmdlineTests/standard_irOptimized_requested/input.json index 09aa37baeb3b..48e5c7b0a2db 100644 --- a/test/cmdlineTests/standard_irOptimized_requested/input.json +++ b/test/cmdlineTests/standard_irOptimized_requested/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { function f() public pure {} }" + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { function f() public pure {} }" } }, "settings": diff --git a/test/cmdlineTests/standard_irOptimized_requested/output.json b/test/cmdlineTests/standard_irOptimized_requested/output.json index ba3cb8b2ad93..479f424a81b8 100644 --- a/test/cmdlineTests/standard_irOptimized_requested/output.json +++ b/test/cmdlineTests/standard_irOptimized_requested/output.json @@ -5,17 +5,17 @@ * !USE AT YOUR OWN RISK! * *******************************************************/ -object \"C_6\" { +object \"C_7\" { code { mstore(64, 128) if callvalue() { revert(0, 0) } - constructor_C_6() - codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\")) - return(0, datasize(\"C_6_deployed\")) - function constructor_C_6() + constructor_C_7() + codecopy(0, dataoffset(\"C_7_deployed\"), datasize(\"C_7_deployed\")) + return(0, datasize(\"C_7_deployed\")) + function constructor_C_7() { } } - object \"C_6_deployed\" { + object \"C_7_deployed\" { code { mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -25,7 +25,7 @@ object \"C_6\" { case 0x26121ff0 { if callvalue() { revert(0, 0) } abi_decode_tuple_(4, calldatasize()) - fun_f_5() + fun_f_6() let memPos := allocateMemory(0) let memEnd := abi_encode_tuple__to__fromStack(memPos) return(memPos, sub(memEnd, memPos)) @@ -44,11 +44,17 @@ object \"C_6\" { { memPtr := mload(64) let newFreePtr := add(memPtr, size) - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } - function fun_f_5() + function fun_f_6() { } + function panic_error_0x41() + { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } function shift_right_224_unsigned(value) -> newValue { newValue := shr(224, value) } } diff --git a/test/cmdlineTests/standard_ir_requested/input.json b/test/cmdlineTests/standard_ir_requested/input.json index cc0a91df97af..6eb5295c53c4 100644 --- a/test/cmdlineTests/standard_ir_requested/input.json +++ b/test/cmdlineTests/standard_ir_requested/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { function f() public pure {} }" + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { function f() public pure {} }" } }, "settings": diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index f63075090caa..b25430812ba0 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -6,23 +6,23 @@ *******************************************************/ -object \"C_6\" { +object \"C_7\" { code { mstore(64, 128) if callvalue() { revert(0, 0) } - constructor_C_6() + constructor_C_7() - codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\")) + codecopy(0, dataoffset(\"C_7_deployed\"), datasize(\"C_7_deployed\")) - return(0, datasize(\"C_6_deployed\")) + return(0, datasize(\"C_7_deployed\")) - function constructor_C_6() { + function constructor_C_7() { } } - object \"C_6_deployed\" { + object \"C_7_deployed\" { code { mstore(64, 128) @@ -36,7 +36,7 @@ object \"C_6\" { // f() if callvalue() { revert(0, 0) } abi_decode_tuple_(4, calldatasize()) - fun_f_5() + fun_f_6() let memPos := allocateMemory(0) let memEnd := abi_encode_tuple__to__fromStack(memPos ) return(memPos, sub(memEnd, memPos)) @@ -61,14 +61,20 @@ object \"C_6\" { memPtr := mload(64) let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } - function fun_f_5() { + function fun_f_6() { } + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + function shift_right_224_unsigned(value) -> newValue { newValue := diff --git a/test/cmdlineTests/standard_model_checker_engine_all/input.json b/test/cmdlineTests/standard_model_checker_engine_all/input.json new file mode 100644 index 000000000000..e39baccbc193 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_all/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "all" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_engine_all/output.json b/test/cmdlineTests/standard_model_checker_engine_all/output.json new file mode 100644 index 000000000000..5927786c1fcc --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_all/output.json @@ -0,0 +1,23 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + + +Transaction trace: +constructor() +f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + + +Transaction trace: +constructor() +f(0)","severity":"warning","sourceLocation":{"end":150,"file":"A","start":137},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_engine_bmc/input.json b/test/cmdlineTests/standard_model_checker_engine_bmc/input.json new file mode 100644 index 000000000000..944d64147d0a --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_bmc/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "bmc" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_engine_bmc/output.json b/test/cmdlineTests/standard_model_checker_engine_bmc/output.json new file mode 100644 index 000000000000..663136f6956f --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_bmc/output.json @@ -0,0 +1,36 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x238aade411d63d50406236089e28f3770d51f95888222fdb838f930911e0f763":"(set-option :produce-models true) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |expr_8_0| () Int) +(declare-fun |expr_9_0| () Int) +(declare-fun |expr_10_1| () Bool) + +(assert (and (and (and true true) (and (= expr_10_1 (> expr_8_0 expr_9_0)) (and (= expr_9_0 0) (and (= expr_8_0 x_4_0) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) true))))) (not expr_10_1))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_4_0)) +(check-sat) +(get-value (|EVALEXPR_0| )) +"}},"errors":[{"component":"general","errorCode":"4661","formattedMessage":"Warning: BMC: Assertion violation happens here. + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ +Note: Counterexample: + x = 0 + +Note: Callstack: +Note: + +","message":"BMC: Assertion violation happens here.","secondarySourceLocations":[{"message":"Counterexample: + x = 0 +"},{"message":"Callstack:"},{"message":""}],"severity":"warning","sourceLocation":{"end":150,"file":"A","start":137},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_engine_chc/input.json b/test/cmdlineTests/standard_model_checker_engine_chc/input.json new file mode 100644 index 000000000000..bda610936c94 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_chc/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_engine_chc/output.json b/test/cmdlineTests/standard_model_checker_engine_chc/output.json new file mode 100644 index 000000000000..5927786c1fcc --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_chc/output.json @@ -0,0 +1,23 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +Counterexample: + +x = 0 + + +Transaction trace: +constructor() +f(0) + --> A:4:47: + | +4 | contract C { function f(uint x) public pure { assert(x > 0); } } + | ^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation happens here. +Counterexample: + +x = 0 + + +Transaction trace: +constructor() +f(0)","severity":"warning","sourceLocation":{"end":150,"file":"A","start":137},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_engine_none/input.json b/test/cmdlineTests/standard_model_checker_engine_none/input.json new file mode 100644 index 000000000000..cbb146060474 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_none/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "none" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_engine_none/output.json b/test/cmdlineTests/standard_model_checker_engine_none/output.json new file mode 100644 index 000000000000..59b90c8cc98e --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_engine_none/output.json @@ -0,0 +1 @@ +{"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_all/input.json b/test/cmdlineTests/standard_model_checker_timeout_all/input.json new file mode 100644 index 000000000000..a0de7e267092 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_all/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract test {\nfunction f(uint x, uint y, uint k) public pure {\nrequire(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}}" + } + }, + "settings": + { + "modelChecker": + { + "timeout": 1000 + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_timeout_all/output.json b/test/cmdlineTests/standard_model_checker_timeout_all/output.json new file mode 100644 index 000000000000..3b063aa59f55 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_all/output.json @@ -0,0 +1,256 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x0d6f843ef6990bfad1918be96d1aad42b5d7ca87a171d0ac34009e0d4b6e8e03":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_15_0| () Int) +(declare-fun |r_div_mod_15_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) + +(assert (and (and (and true true) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_15_0)) (and (and (<= 0 r_div_mod_15_0) (or (= expr_19_0 0) (< r_div_mod_15_0 expr_19_0))) (and (= (+ (* d_div_mod_15_0 expr_19_0) r_div_mod_15_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))) expr_22_1)) +(check-sat) +","0x3091365cefd5a713eea87735305ab53c75303343ebf74b77d3578ae0b73c64a2":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) + +(assert (and (and (and true true) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))) (not expr_14_1))) +(check-sat) +","0x6b268fc2d87ebda3f3b3b93c8d0b2b5374fd3bd33387113b9ee138a85c142dae":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_15_0| () Int) +(declare-fun |r_div_mod_15_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_16_0| () Int) +(declare-fun |r_div_mod_16_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) + +(assert (and (and (and true true) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_16_0)) (and (and (<= 0 r_div_mod_16_0) (or (= expr_27_0 0) (< r_div_mod_16_0 expr_27_0))) (and (= (+ (* d_div_mod_16_0 expr_27_0) r_div_mod_16_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_15_0)) (and (and (<= 0 r_div_mod_15_0) (or (= expr_19_0 0) (< r_div_mod_15_0 expr_19_0))) (and (= (+ (* d_div_mod_15_0 expr_19_0) r_div_mod_15_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))))))))))) expr_30_1)) +(check-sat) +","0xa81ce8317a79fc74d85d9edfe56caf9b0274a7da4556bfb418652051e0bfa679":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) + +(assert (and (and (and true true) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))) expr_14_1)) +(check-sat) +","0xaec7aba847ba235b585e4c7e6ec8d8eee964e76bd40a02e00af3354acede95d8":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_15_0| () Int) +(declare-fun |r_div_mod_15_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) + +(assert (and (and (and true true) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_15_0)) (and (and (<= 0 r_div_mod_15_0) (or (= expr_19_0 0) (< r_div_mod_15_0 expr_19_0))) (and (= (+ (* d_div_mod_15_0 expr_19_0) r_div_mod_15_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))) (not expr_22_1))) +(check-sat) +","0xe6aad7b5a7ba681908e47e11921c93815f9a4cdd90ef82dd79a60fcac94492c9":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_15_0| () Int) +(declare-fun |r_div_mod_15_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_16_0| () Int) +(declare-fun |r_div_mod_16_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_0| (Int Int Int ) Int) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_abstract_0| () Int) +(declare-fun |expr_36_0| () Int) +(declare-fun |expr_37_0| () Int) +(declare-fun |expr_38_0| () Int) +(declare-fun |d_div_mod_17_0| () Int) +(declare-fun |r_div_mod_17_0| () Int) +(declare-fun |expr_39_1| () Int) +(declare-fun |r_34_1| () Int) +(declare-fun |expr_42_0| () Int) +(declare-fun |expr_43_0| () Int) +(declare-fun |d_div_mod_18_0| () Int) +(declare-fun |r_div_mod_18_0| () Int) +(declare-fun |expr_44_1| () Int) +(declare-fun |expr_45_0| () Int) +(declare-fun |expr_46_1| () Bool) + +(assert (and (and (and true true) (and (= expr_46_1 (= expr_44_1 expr_45_0)) (and (= expr_45_0 0) (and (= expr_44_1 (ite (= expr_43_0 0) 0 r_div_mod_18_0)) (and (and (<= 0 r_div_mod_18_0) (or (= expr_43_0 0) (< r_div_mod_18_0 expr_43_0))) (and (= (+ (* d_div_mod_18_0 expr_43_0) r_div_mod_18_0) expr_42_0) (and (= expr_43_0 k_8_0) (and (= expr_42_0 r_34_1) (and (ite (and true true) (= r_34_1 expr_39_1) (= r_34_1 r_34_0)) (and (= expr_39_1 (ite (= expr_38_0 0) 0 r_div_mod_17_0)) (and (and (<= 0 r_div_mod_17_0) (or (= expr_38_0 0) (< r_div_mod_17_0 expr_38_0))) (and (= (+ (* d_div_mod_17_0 expr_38_0) r_div_mod_17_0) (* expr_36_0 expr_37_0)) (and (= expr_38_0 k_8_0) (and (= expr_37_0 y_6_0) (and (= expr_36_0 x_4_0) (and true (and (implies (and true true) expr_30_1) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_16_0)) (and (and (<= 0 r_div_mod_16_0) (or (= expr_27_0 0) (< r_div_mod_16_0 expr_27_0))) (and (= (+ (* d_div_mod_16_0 expr_27_0) r_div_mod_16_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_15_0)) (and (and (<= 0 r_div_mod_15_0) (or (= expr_19_0 0) (< r_div_mod_15_0 expr_19_0))) (and (= (+ (* d_div_mod_15_0 expr_19_0) r_div_mod_15_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))))))))))))))))))))))))))) (not expr_46_1))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_4_0)) +(declare-const |EVALEXPR_1| Int) +(assert (= |EVALEXPR_1| y_6_0)) +(declare-const |EVALEXPR_2| Int) +(assert (= |EVALEXPR_2| k_8_0)) +(declare-const |EVALEXPR_3| Int) +(assert (= |EVALEXPR_3| r_34_1)) +(check-sat) +(get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| )) +","0xf877f10b1dc480ae2403c9376947f543da579bf3aff7dfaa2f946e300b81d305":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_15_0| () Int) +(declare-fun |r_div_mod_15_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_16_0| () Int) +(declare-fun |r_div_mod_16_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) + +(assert (and (and (and true true) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_16_0)) (and (and (<= 0 r_div_mod_16_0) (or (= expr_27_0 0) (< r_div_mod_16_0 expr_27_0))) (and (= (+ (* d_div_mod_16_0 expr_27_0) r_div_mod_16_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_15_0)) (and (and (<= 0 r_div_mod_15_0) (or (= expr_19_0 0) (< r_div_mod_15_0 expr_19_0))) (and (= (+ (* d_div_mod_15_0 expr_19_0) r_div_mod_15_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))))))))))) (not expr_30_1))) +(check-sat) +"}},"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. + --> A:6:85: + | +6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} + | ^^^^^^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":258,"file":"A","start":240},"type":"Warning"},{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. + --> A:6:85: + | +6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} + | ^^^^^^^^^^^^^^^^^^ +Note: + +","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":258,"file":"A","start":240},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_bmc/input.json b/test/cmdlineTests/standard_model_checker_timeout_bmc/input.json new file mode 100644 index 000000000000..0cf2c2182972 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_bmc/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract test {\nfunction f(uint x, uint y, uint k) public pure {\nrequire(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}}" + } + }, + "settings": + { + "modelChecker": + { + "engine": "bmc", + "timeout": 1000 + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json b/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json new file mode 100644 index 000000000000..8b6f289b9796 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_bmc/output.json @@ -0,0 +1,506 @@ +{"auxiliaryInputRequested":{"smtlib2queries":{"0x0e2ec6d70e3de7ac14f07c9bbb08f9436e3b832ae8456d340e4d4a8b8712c7f5":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_1_0| () Int) +(declare-fun |r_div_mod_1_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_0| (Int Int Int ) Int) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_abstract_0| () Int) +(declare-fun |expr_36_0| () Int) +(declare-fun |expr_37_0| () Int) +(declare-fun |expr_38_0| () Int) +(declare-fun |d_div_mod_2_0| () Int) +(declare-fun |r_div_mod_2_0| () Int) +(declare-fun |expr_39_1| () Int) +(declare-fun |r_34_1| () Int) +(declare-fun |expr_42_0| () Int) +(declare-fun |expr_43_0| () Int) +(declare-fun |d_div_mod_3_0| () Int) +(declare-fun |r_div_mod_3_0| () Int) +(declare-fun |expr_44_1| () Int) +(declare-fun |expr_45_0| () Int) +(declare-fun |expr_46_1| () Bool) + +(assert (and (and (and true true) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true))))))))))))))))))) (= expr_27_0 0))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_4_0)) +(declare-const |EVALEXPR_1| Int) +(assert (= |EVALEXPR_1| y_6_0)) +(declare-const |EVALEXPR_2| Int) +(assert (= |EVALEXPR_2| k_8_0)) +(declare-const |EVALEXPR_3| Int) +(assert (= |EVALEXPR_3| r_34_0)) +(declare-const |EVALEXPR_4| Int) +(assert (= |EVALEXPR_4| expr_27_0)) +(check-sat) +(get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| |EVALEXPR_4| )) +","0x3091365cefd5a713eea87735305ab53c75303343ebf74b77d3578ae0b73c64a2":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) + +(assert (and (and (and true true) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))) (not expr_14_1))) +(check-sat) +","0x46c0f34a4da6b31138e9238be92256a3c472a5a24c88371ef680f78e0c520cb6":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_1_0| () Int) +(declare-fun |r_div_mod_1_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_0| (Int Int Int ) Int) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_abstract_0| () Int) +(declare-fun |expr_36_0| () Int) +(declare-fun |expr_37_0| () Int) +(declare-fun |expr_38_0| () Int) +(declare-fun |d_div_mod_2_0| () Int) +(declare-fun |r_div_mod_2_0| () Int) +(declare-fun |expr_39_1| () Int) +(declare-fun |r_34_1| () Int) +(declare-fun |expr_42_0| () Int) +(declare-fun |expr_43_0| () Int) +(declare-fun |d_div_mod_3_0| () Int) +(declare-fun |r_div_mod_3_0| () Int) +(declare-fun |expr_44_1| () Int) +(declare-fun |expr_45_0| () Int) +(declare-fun |expr_46_1| () Bool) + +(assert (and (and (and true true) (and (= expr_38_0 k_8_0) (and (= expr_37_0 y_6_0) (and (= expr_36_0 x_4_0) (and true (and (implies (and true true) expr_30_1) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_1_0)) (and (and (<= 0 r_div_mod_1_0) (or (= expr_27_0 0) (< r_div_mod_1_0 expr_27_0))) (and (= (+ (* d_div_mod_1_0 expr_27_0) r_div_mod_1_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true))))))))))))))))))))))))))))) (= expr_38_0 0))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_4_0)) +(declare-const |EVALEXPR_1| Int) +(assert (= |EVALEXPR_1| y_6_0)) +(declare-const |EVALEXPR_2| Int) +(assert (= |EVALEXPR_2| k_8_0)) +(declare-const |EVALEXPR_3| Int) +(assert (= |EVALEXPR_3| r_34_0)) +(declare-const |EVALEXPR_4| Int) +(assert (= |EVALEXPR_4| expr_38_0)) +(check-sat) +(get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| |EVALEXPR_4| )) +","0x6378f27de46d517b5bafe774eab66c12cb656dbd031cb5e710f07b1bfd6c5f79":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) + +(assert (and (and (and true true) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))) (not expr_22_1))) +(check-sat) +","0x7db419e89d4bb9fbaa4bda7fd522223a515177ff577a6d381e7f2b7f4612582b":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) + +(assert (and (and (and true true) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))) expr_22_1)) +(check-sat) +","0x7de8eaf2518b47eec1afadbebacdfa7d93a1878e0f1b6eef91b8a80f1246937e":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_1_0| () Int) +(declare-fun |r_div_mod_1_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) + +(assert (and (and (and true true) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_1_0)) (and (and (<= 0 r_div_mod_1_0) (or (= expr_27_0 0) (< r_div_mod_1_0 expr_27_0))) (and (= (+ (* d_div_mod_1_0 expr_27_0) r_div_mod_1_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))))))))))) (not expr_30_1))) +(check-sat) +","0x8c4f3faef8ad4a2fe9935f16ec93e2df20f5a3831be51c13afe060774658141c":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_1_0| () Int) +(declare-fun |r_div_mod_1_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) + +(assert (and (and (and true true) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_1_0)) (and (and (<= 0 r_div_mod_1_0) (or (= expr_27_0 0) (< r_div_mod_1_0 expr_27_0))) (and (= (+ (* d_div_mod_1_0 expr_27_0) r_div_mod_1_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))))))))))) expr_30_1)) +(check-sat) +","0xa4666c5d197afd69bd82af703fb694c1d3cdd8926ab480fc42e69231134d9265":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_1_0| () Int) +(declare-fun |r_div_mod_1_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_0| (Int Int Int ) Int) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_abstract_0| () Int) +(declare-fun |expr_36_0| () Int) +(declare-fun |expr_37_0| () Int) +(declare-fun |expr_38_0| () Int) +(declare-fun |d_div_mod_2_0| () Int) +(declare-fun |r_div_mod_2_0| () Int) +(declare-fun |expr_39_1| () Int) +(declare-fun |r_34_1| () Int) +(declare-fun |expr_42_0| () Int) +(declare-fun |expr_43_0| () Int) +(declare-fun |d_div_mod_3_0| () Int) +(declare-fun |r_div_mod_3_0| () Int) +(declare-fun |expr_44_1| () Int) +(declare-fun |expr_45_0| () Int) +(declare-fun |expr_46_1| () Bool) + +(assert (and (and (and true true) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true))))))))))) (= expr_19_0 0))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_4_0)) +(declare-const |EVALEXPR_1| Int) +(assert (= |EVALEXPR_1| y_6_0)) +(declare-const |EVALEXPR_2| Int) +(assert (= |EVALEXPR_2| k_8_0)) +(declare-const |EVALEXPR_3| Int) +(assert (= |EVALEXPR_3| r_34_0)) +(declare-const |EVALEXPR_4| Int) +(assert (= |EVALEXPR_4| expr_19_0)) +(check-sat) +(get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| |EVALEXPR_4| )) +","0xa81ce8317a79fc74d85d9edfe56caf9b0274a7da4556bfb418652051e0bfa679":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) + +(assert (and (and (and true true) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))) expr_14_1)) +(check-sat) +","0xa96a68b7853fcc0a10dd525c3ff838e3bfac425b104c44b240623f7a35aee6f1":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_1_0| () Int) +(declare-fun |r_div_mod_1_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_0| (Int Int Int ) Int) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_abstract_0| () Int) +(declare-fun |expr_36_0| () Int) +(declare-fun |expr_37_0| () Int) +(declare-fun |expr_38_0| () Int) +(declare-fun |d_div_mod_2_0| () Int) +(declare-fun |r_div_mod_2_0| () Int) +(declare-fun |expr_39_1| () Int) +(declare-fun |r_34_1| () Int) +(declare-fun |expr_42_0| () Int) +(declare-fun |expr_43_0| () Int) +(declare-fun |d_div_mod_3_0| () Int) +(declare-fun |r_div_mod_3_0| () Int) +(declare-fun |expr_44_1| () Int) +(declare-fun |expr_45_0| () Int) +(declare-fun |expr_46_1| () Bool) + +(assert (and (and (and true true) (and (= expr_46_1 (= expr_44_1 expr_45_0)) (and (= expr_45_0 0) (and (= expr_44_1 (ite (= expr_43_0 0) 0 r_div_mod_3_0)) (and (and (<= 0 r_div_mod_3_0) (or (= expr_43_0 0) (< r_div_mod_3_0 expr_43_0))) (and (= (+ (* d_div_mod_3_0 expr_43_0) r_div_mod_3_0) expr_42_0) (and (= expr_43_0 k_8_0) (and (= expr_42_0 r_34_1) (and (ite (and true true) (= r_34_1 expr_39_1) (= r_34_1 r_34_0)) (and (= expr_39_1 (ite (= expr_38_0 0) 0 r_div_mod_2_0)) (and (and (<= 0 r_div_mod_2_0) (or (= expr_38_0 0) (< r_div_mod_2_0 expr_38_0))) (and (= (+ (* d_div_mod_2_0 expr_38_0) r_div_mod_2_0) (* expr_36_0 expr_37_0)) (and (= expr_38_0 k_8_0) (and (= expr_37_0 y_6_0) (and (= expr_36_0 x_4_0) (and true (and (implies (and true true) expr_30_1) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_1_0)) (and (and (<= 0 r_div_mod_1_0) (or (= expr_27_0 0) (< r_div_mod_1_0 expr_27_0))) (and (= (+ (* d_div_mod_1_0 expr_27_0) r_div_mod_1_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true)))))))))))))))))))))))))))))))))))))))) (not expr_46_1))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_4_0)) +(declare-const |EVALEXPR_1| Int) +(assert (= |EVALEXPR_1| y_6_0)) +(declare-const |EVALEXPR_2| Int) +(assert (= |EVALEXPR_2| k_8_0)) +(declare-const |EVALEXPR_3| Int) +(assert (= |EVALEXPR_3| r_34_1)) +(check-sat) +(get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| )) +","0xcc1de975d2f5e9b3e4ff9b4f46c8248513ac3b755b60f8a630a46d12b4b11f9a":"(set-option :produce-models true) +(set-option :timeout 1000) +(set-logic ALL) +(declare-fun |error_0| () Int) +(declare-fun |this_0| () Int) +(declare-datatypes ((|state_type| 0)) (((|state_type| (|balances| (Array Int Int)))))) +(declare-fun |state_0| () |state_type|) +(declare-datatypes ((|bytes_tuple| 0)) (((|bytes_tuple| (|bytes_tuple_accessor_array| (Array Int Int)) (|bytes_tuple_accessor_length| Int))))) +(declare-datatypes ((|tx_type| 0)) (((|tx_type| (|block.coinbase| Int) (|block.difficulty| Int) (|block.gaslimit| Int) (|block.number| Int) (|block.timestamp| Int) (|blockhash| (Array Int Int)) (|msg.data| |bytes_tuple|) (|msg.sender| Int) (|msg.sig| Int) (|msg.value| Int) (|tx.gasprice| Int) (|tx.origin| Int))))) +(declare-fun |tx_0| () |tx_type|) +(declare-datatypes ((|ecrecover_input_type| 0)) (((|ecrecover_input_type| (|hash| Int) (|v| Int) (|r| Int) (|s| Int))))) +(declare-datatypes ((|crypto_type| 0)) (((|crypto_type| (|ecrecover| (Array |ecrecover_input_type| Int)) (|keccak256| (Array |bytes_tuple| Int)) (|ripemd160| (Array |bytes_tuple| Int)) (|sha256| (Array |bytes_tuple| Int)))))) +(declare-fun |crypto_0| () |crypto_type|) +(declare-fun |x_4_0| () Int) +(declare-fun |y_6_0| () Int) +(declare-fun |k_8_0| () Int) +(declare-fun |r_34_0| () Int) +(declare-fun |expr_12_0| () Int) +(declare-fun |expr_13_0| () Int) +(declare-fun |expr_14_1| () Bool) +(declare-fun |expr_18_0| () Int) +(declare-fun |expr_19_0| () Int) +(declare-fun |d_div_mod_0_0| () Int) +(declare-fun |r_div_mod_0_0| () Int) +(declare-fun |expr_20_1| () Int) +(declare-fun |expr_21_0| () Int) +(declare-fun |expr_22_1| () Bool) +(declare-fun |expr_26_0| () Int) +(declare-fun |expr_27_0| () Int) +(declare-fun |d_div_mod_1_0| () Int) +(declare-fun |r_div_mod_1_0| () Int) +(declare-fun |expr_28_1| () Int) +(declare-fun |expr_29_0| () Int) +(declare-fun |expr_30_1| () Bool) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_0| (Int Int Int ) Int) +(declare-fun |t_function_mulmod_pure$_t_uint256_$_t_uint256_$_t_uint256_$returns$_t_uint256_$_abstract_0| () Int) +(declare-fun |expr_36_0| () Int) +(declare-fun |expr_37_0| () Int) +(declare-fun |expr_38_0| () Int) +(declare-fun |d_div_mod_2_0| () Int) +(declare-fun |r_div_mod_2_0| () Int) +(declare-fun |expr_39_1| () Int) +(declare-fun |r_34_1| () Int) +(declare-fun |expr_42_0| () Int) +(declare-fun |expr_43_0| () Int) +(declare-fun |d_div_mod_3_0| () Int) +(declare-fun |r_div_mod_3_0| () Int) +(declare-fun |expr_44_1| () Int) +(declare-fun |expr_45_0| () Int) +(declare-fun |expr_46_1| () Bool) + +(assert (and (and (and true true) (and (= expr_43_0 k_8_0) (and (= expr_42_0 r_34_1) (and (ite (and true true) (= r_34_1 expr_39_1) (= r_34_1 r_34_0)) (and (= expr_39_1 (ite (= expr_38_0 0) 0 r_div_mod_2_0)) (and (and (<= 0 r_div_mod_2_0) (or (= expr_38_0 0) (< r_div_mod_2_0 expr_38_0))) (and (= (+ (* d_div_mod_2_0 expr_38_0) r_div_mod_2_0) (* expr_36_0 expr_37_0)) (and (= expr_38_0 k_8_0) (and (= expr_37_0 y_6_0) (and (= expr_36_0 x_4_0) (and true (and (implies (and true true) expr_30_1) (and (= expr_30_1 (= expr_28_1 expr_29_0)) (and (= expr_29_0 0) (and (= expr_28_1 (ite (= expr_27_0 0) 0 r_div_mod_1_0)) (and (and (<= 0 r_div_mod_1_0) (or (= expr_27_0 0) (< r_div_mod_1_0 expr_27_0))) (and (= (+ (* d_div_mod_1_0 expr_27_0) r_div_mod_1_0) expr_26_0) (and (= expr_27_0 k_8_0) (and (= expr_26_0 y_6_0) (and (implies (and true true) expr_22_1) (and (= expr_22_1 (= expr_20_1 expr_21_0)) (and (= expr_21_0 0) (and (= expr_20_1 (ite (= expr_19_0 0) 0 r_div_mod_0_0)) (and (and (<= 0 r_div_mod_0_0) (or (= expr_19_0 0) (< r_div_mod_0_0 expr_19_0))) (and (= (+ (* d_div_mod_0_0 expr_19_0) r_div_mod_0_0) expr_18_0) (and (= expr_19_0 k_8_0) (and (= expr_18_0 x_4_0) (and (implies (and true true) expr_14_1) (and (= expr_14_1 (> expr_12_0 expr_13_0)) (and (= expr_13_0 0) (and (= expr_12_0 k_8_0) (and (and (>= k_8_0 0) (<= k_8_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= y_6_0 0) (<= y_6_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (and (>= x_4_0 0) (<= x_4_0 115792089237316195423570985008687907853269984665640564039457584007913129639935)) (and (= r_34_0 0) true))))))))))))))))))))))))))))))))))) (= expr_43_0 0))) +(declare-const |EVALEXPR_0| Int) +(assert (= |EVALEXPR_0| x_4_0)) +(declare-const |EVALEXPR_1| Int) +(assert (= |EVALEXPR_1| y_6_0)) +(declare-const |EVALEXPR_2| Int) +(assert (= |EVALEXPR_2| k_8_0)) +(declare-const |EVALEXPR_3| Int) +(assert (= |EVALEXPR_3| r_34_1)) +(declare-const |EVALEXPR_4| Int) +(assert (= |EVALEXPR_4| expr_43_0)) +(check-sat) +(get-value (|EVALEXPR_0| |EVALEXPR_1| |EVALEXPR_2| |EVALEXPR_3| |EVALEXPR_4| )) +"}},"errors":[{"component":"general","errorCode":"7812","formattedMessage":"Warning: BMC: Assertion violation might happen here. + --> A:6:85: + | +6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} + | ^^^^^^^^^^^^^^^^^^ +Note: + +","message":"BMC: Assertion violation might happen here.","secondarySourceLocations":[{"message":""}],"severity":"warning","sourceLocation":{"end":258,"file":"A","start":240},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_chc/input.json b/test/cmdlineTests/standard_model_checker_timeout_chc/input.json new file mode 100644 index 000000000000..2e54455fbef7 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_chc/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract test {\nfunction f(uint x, uint y, uint k) public pure {\nrequire(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}}" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "timeout": 1000 + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_timeout_chc/output.json b/test/cmdlineTests/standard_model_checker_timeout_chc/output.json new file mode 100644 index 000000000000..907d2fdebb95 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_chc/output.json @@ -0,0 +1,7 @@ +{"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation might happen here. + --> A:6:85: + | +6 | require(k > 0); require(x % k == 0); require(y % k == 0); uint r = mulmod(x, y, k); assert(r % k == 0);}} + | ^^^^^^^^^^^^^^^^^^ + +","message":"CHC: Assertion violation might happen here.","severity":"warning","sourceLocation":{"end":258,"file":"A","start":240},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_timeout_wrong_key/input.json b/test/cmdlineTests/standard_model_checker_timeout_wrong_key/input.json new file mode 100644 index 000000000000..b0196d3be5c3 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_wrong_key/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "engine": "chc", + "atimeout": 1 + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_timeout_wrong_key/output.json b/test/cmdlineTests/standard_model_checker_timeout_wrong_key/output.json new file mode 100644 index 000000000000..a02329045739 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_wrong_key/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"Unknown key \"atimeout\"","message":"Unknown key \"atimeout\"","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_model_checker_timeout_wrong_value/input.json b/test/cmdlineTests/standard_model_checker_timeout_wrong_value/input.json new file mode 100644 index 000000000000..86a17c1d90cb --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_wrong_value/input.json @@ -0,0 +1,17 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma experimental SMTChecker;\ncontract C { function f(uint x) public pure { assert(x > 0); } }" + } + }, + "settings": + { + "modelChecker": + { + "timeout": "asd" + } + } +} diff --git a/test/cmdlineTests/standard_model_checker_timeout_wrong_value/output.json b/test/cmdlineTests/standard_model_checker_timeout_wrong_value/output.json new file mode 100644 index 000000000000..c3a54e514906 --- /dev/null +++ b/test/cmdlineTests/standard_model_checker_timeout_wrong_value/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"settings.modelChecker.timeout must be an unsigned integer.","message":"settings.modelChecker.timeout must be an unsigned integer.","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_only_ast_requested/output.json b/test/cmdlineTests/standard_only_ast_requested/output.json index c1ffe58929e3..16f8bfddba93 100644 --- a/test/cmdlineTests/standard_only_ast_requested/output.json +++ b/test/cmdlineTests/standard_only_ast_requested/output.json @@ -1 +1 @@ -{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","documentation":null,"fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"documentation":null,"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","overrides":null,"parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} +{"sources":{"A":{"ast":{"absolutePath":"A","exportedSymbols":{"C":[6]},"id":7,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nodeType":"FunctionDefinition","parameters":{"id":2,"nodeType":"ParameterList","parameters":[],"src":"82:2:0"},"returnParameters":{"id":3,"nodeType":"ParameterList","parameters":[],"src":"97:0:0"},"scope":6,"src":"72:27:0","stateMutability":"pure","virtual":false,"visibility":"public"}],"scope":7,"src":"59:42:0"}],"src":"36:65:0"},"id":0}}} diff --git a/test/cmdlineTests/standard_optimizer_generatedSources/input.json b/test/cmdlineTests/standard_optimizer_generatedSources/input.json new file mode 100644 index 000000000000..b38114fadeb5 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_generatedSources/input.json @@ -0,0 +1,21 @@ +{ + "language": "Solidity", + "sources": { + "a.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\npragma abicoder v2; contract A { function f(uint[] memory) public view returns (uint256) { } }" + } + }, + "settings": { + "evmVersion": "petersburg", + "optimizer": { "enabled": true }, + "outputSelection": { + "*": { + "A": [ + "evm.bytecode.object", + "evm.deployedBytecode.generatedSources", + "evm.bytecode.generatedSources" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_generatedSources/output.json b/test/cmdlineTests/standard_optimizer_generatedSources/output.json new file mode 100644 index 000000000000..38a013003179 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_generatedSources/output.json @@ -0,0 +1,44 @@ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:1488:1","statements":[{"nodeType":"YulBlock","src":"6:3:1","statements":[]},{"body":{"nodeType":"YulBlock","src":"109:1063:1","statements":[{"nodeType":"YulVariableDeclaration","src":"119:12:1","value":{"kind":"number","nodeType":"YulLiteral","src":"129:2:1","type":"","value":"32"},"variables":[{"name":"_1","nodeType":"YulTypedName","src":"123:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"176:26:1","statements":[{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"185:6:1"},{"name":"value0","nodeType":"YulIdentifier","src":"193:6:1"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"178:6:1"},"nodeType":"YulFunctionCall","src":"178:22:1"},"nodeType":"YulExpressionStatement","src":"178:22:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"151:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"160:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"147:3:1"},"nodeType":"YulFunctionCall","src":"147:23:1"},{"name":"_1","nodeType":"YulIdentifier","src":"172:2:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"143:3:1"},"nodeType":"YulFunctionCall","src":"143:32:1"},"nodeType":"YulIf","src":"140:2:1"},{"nodeType":"YulVariableDeclaration","src":"211:37:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"238:9:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"225:12:1"},"nodeType":"YulFunctionCall","src":"225:23:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"215:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"257:28:1","value":{"kind":"number","nodeType":"YulLiteral","src":"267:18:1","type":"","value":"0xffffffffffffffff"},"variables":[{"name":"_2","nodeType":"YulTypedName","src":"261:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"312:26:1","statements":[{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"321:6:1"},{"name":"value0","nodeType":"YulIdentifier","src":"329:6:1"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"314:6:1"},"nodeType":"YulFunctionCall","src":"314:22:1"},"nodeType":"YulExpressionStatement","src":"314:22:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"300:6:1"},{"name":"_2","nodeType":"YulIdentifier","src":"308:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"297:2:1"},"nodeType":"YulFunctionCall","src":"297:14:1"},"nodeType":"YulIf","src":"294:2:1"},{"nodeType":"YulVariableDeclaration","src":"347:32:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"361:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"372:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"357:3:1"},"nodeType":"YulFunctionCall","src":"357:22:1"},"variables":[{"name":"_3","nodeType":"YulTypedName","src":"351:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"427:26:1","statements":[{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"436:6:1"},{"name":"value0","nodeType":"YulIdentifier","src":"444:6:1"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"429:6:1"},"nodeType":"YulFunctionCall","src":"429:22:1"},"nodeType":"YulExpressionStatement","src":"429:22:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"406:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"410:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"402:3:1"},"nodeType":"YulFunctionCall","src":"402:13:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"417:7:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"398:3:1"},"nodeType":"YulFunctionCall","src":"398:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"391:6:1"},"nodeType":"YulFunctionCall","src":"391:35:1"},"nodeType":"YulIf","src":"388:2:1"},{"nodeType":"YulVariableDeclaration","src":"462:26:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"485:2:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"472:12:1"},"nodeType":"YulFunctionCall","src":"472:16:1"},"variables":[{"name":"_4","nodeType":"YulTypedName","src":"466:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"511:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"513:16:1"},"nodeType":"YulFunctionCall","src":"513:18:1"},"nodeType":"YulExpressionStatement","src":"513:18:1"}]},"condition":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"503:2:1"},{"name":"_2","nodeType":"YulIdentifier","src":"507:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"500:2:1"},"nodeType":"YulFunctionCall","src":"500:10:1"},"nodeType":"YulIf","src":"497:2:1"},{"nodeType":"YulVariableDeclaration","src":"542:21:1","value":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"556:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"560:2:1"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"552:3:1"},"nodeType":"YulFunctionCall","src":"552:11:1"},"variables":[{"name":"_5","nodeType":"YulTypedName","src":"546:2:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"572:23:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"592:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"586:5:1"},"nodeType":"YulFunctionCall","src":"586:9:1"},"variables":[{"name":"memPtr","nodeType":"YulTypedName","src":"576:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"604:42:1","value":{"arguments":[{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"630:6:1"},{"name":"_5","nodeType":"YulIdentifier","src":"638:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"626:3:1"},"nodeType":"YulFunctionCall","src":"626:15:1"},{"name":"_1","nodeType":"YulIdentifier","src":"643:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"622:3:1"},"nodeType":"YulFunctionCall","src":"622:24:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"608:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"705:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"707:16:1"},"nodeType":"YulFunctionCall","src":"707:18:1"},"nodeType":"YulExpressionStatement","src":"707:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"664:10:1"},{"name":"_2","nodeType":"YulIdentifier","src":"676:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"661:2:1"},"nodeType":"YulFunctionCall","src":"661:18:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"684:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"696:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"681:2:1"},"nodeType":"YulFunctionCall","src":"681:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"658:2:1"},"nodeType":"YulFunctionCall","src":"658:46:1"},"nodeType":"YulIf","src":"655:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"743:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"747:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"736:6:1"},"nodeType":"YulFunctionCall","src":"736:22:1"},"nodeType":"YulExpressionStatement","src":"736:22:1"},{"nodeType":"YulVariableDeclaration","src":"767:17:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"778:6:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"771:3:1","type":""}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"800:6:1"},{"name":"_4","nodeType":"YulIdentifier","src":"808:2:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"793:6:1"},"nodeType":"YulFunctionCall","src":"793:18:1"},"nodeType":"YulExpressionStatement","src":"793:18:1"},{"nodeType":"YulAssignment","src":"820:22:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"831:6:1"},{"name":"_1","nodeType":"YulIdentifier","src":"839:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"827:3:1"},"nodeType":"YulFunctionCall","src":"827:15:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"820:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"851:22:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"866:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"870:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"862:3:1"},"nodeType":"YulFunctionCall","src":"862:11:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"855:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"919:26:1","statements":[{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"928:6:1"},{"name":"value0","nodeType":"YulIdentifier","src":"936:6:1"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"921:6:1"},"nodeType":"YulFunctionCall","src":"921:22:1"},"nodeType":"YulExpressionStatement","src":"921:22:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"896:2:1"},{"name":"_5","nodeType":"YulIdentifier","src":"900:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"892:3:1"},"nodeType":"YulFunctionCall","src":"892:11:1"},{"name":"_1","nodeType":"YulIdentifier","src":"905:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"888:3:1"},"nodeType":"YulFunctionCall","src":"888:20:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"910:7:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"885:2:1"},"nodeType":"YulFunctionCall","src":"885:33:1"},"nodeType":"YulIf","src":"882:2:1"},{"nodeType":"YulVariableDeclaration","src":"954:15:1","value":{"name":"value0","nodeType":"YulIdentifier","src":"963:6:1"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"958:1:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1023:118:1","statements":[{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1044:3:1"},{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1062:3:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1049:12:1"},"nodeType":"YulFunctionCall","src":"1049:17:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1037:6:1"},"nodeType":"YulFunctionCall","src":"1037:30:1"},"nodeType":"YulExpressionStatement","src":"1037:30:1"},{"nodeType":"YulAssignment","src":"1080:19:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1091:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1096:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1087:3:1"},"nodeType":"YulFunctionCall","src":"1087:12:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"1080:3:1"}]},{"nodeType":"YulAssignment","src":"1112:19:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1123:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1128:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1119:3:1"},"nodeType":"YulFunctionCall","src":"1119:12:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"1112:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"989:1:1"},{"name":"_4","nodeType":"YulIdentifier","src":"992:2:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"986:2:1"},"nodeType":"YulFunctionCall","src":"986:9:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"996:18:1","statements":[{"nodeType":"YulAssignment","src":"998:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"1007:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"1010:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1003:3:1"},"nodeType":"YulFunctionCall","src":"1003:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"998:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"982:3:1","statements":[]},"src":"978:163:1"},{"nodeType":"YulAssignment","src":"1150:16:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"1160:6:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1150:6:1"}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"75:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"86:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"98:6:1","type":""}],"src":"14:1158:1"},{"body":{"nodeType":"YulBlock","src":"1278:76:1","statements":[{"nodeType":"YulAssignment","src":"1288:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1300:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1311:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1296:3:1"},"nodeType":"YulFunctionCall","src":"1296:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1288:4:1"}]},{"expression":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1330:9:1"},{"name":"value0","nodeType":"YulIdentifier","src":"1341:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1323:6:1"},"nodeType":"YulFunctionCall","src":"1323:25:1"},"nodeType":"YulExpressionStatement","src":"1323:25:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1247:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1258:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1269:4:1","type":""}],"src":"1177:177:1"},{"body":{"nodeType":"YulBlock","src":"1391:95:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1408:1:1","type":"","value":"0"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1415:3:1","type":"","value":"224"},{"kind":"number","nodeType":"YulLiteral","src":"1420:10:1","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"1411:3:1"},"nodeType":"YulFunctionCall","src":"1411:20:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1401:6:1"},"nodeType":"YulFunctionCall","src":"1401:31:1"},"nodeType":"YulExpressionStatement","src":"1401:31:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1448:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"1451:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1441:6:1"},"nodeType":"YulFunctionCall","src":"1441:15:1"},"nodeType":"YulExpressionStatement","src":"1441:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1472:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1475:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1465:6:1"},"nodeType":"YulFunctionCall","src":"1465:15:1"},"nodeType":"YulExpressionStatement","src":"1465:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"1359:127:1"}]},"contents":"{ + { } + function abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr(headStart, dataEnd) -> value0 + { + let _1 := 32 + if slt(sub(dataEnd, headStart), _1) { revert(value0, value0) } + let offset := calldataload(headStart) + let _2 := 0xffffffffffffffff + if gt(offset, _2) { revert(value0, value0) } + let _3 := add(headStart, offset) + if iszero(slt(add(_3, 0x1f), dataEnd)) { revert(value0, value0) } + let _4 := calldataload(_3) + if gt(_4, _2) { panic_error_0x41() } + let _5 := mul(_4, _1) + let memPtr := mload(64) + let newFreePtr := add(add(memPtr, _5), _1) + if or(gt(newFreePtr, _2), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + let dst := memPtr + mstore(memPtr, _4) + dst := add(memPtr, _1) + let src := add(_3, _1) + if gt(add(add(_3, _5), _1), dataEnd) { revert(value0, value0) } + let i := value0 + for { } lt(i, _4) { i := add(i, 1) } + { + mstore(dst, calldataload(src)) + dst := add(dst, _1) + src := add(src, _1) + } + value0 := memPtr + } + function abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed(headStart, value0) -> tail + { + tail := add(headStart, 32) + mstore(headStart, value0) + } + function panic_error_0x41() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } +}","id":1,"language":"Yul","name":"#utility.yul"}]}}}}},"sources":{"a.sol":{"id":0}}} diff --git a/test/cmdlineTests/standard_secondary_source_location/output.json b/test/cmdlineTests/standard_secondary_source_location/output.json index 45874df276e2..9d09bc194721 100644 --- a/test/cmdlineTests/standard_secondary_source_location/output.json +++ b/test/cmdlineTests/standard_secondary_source_location/output.json @@ -1,10 +1,17 @@ -{"errors":[{"component":"general","errorCode":"3364","formattedMessage":"A:2:105: DeclarationError: Base constructor arguments given twice. -pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} - ^-------------------^ -A:2:74: First constructor call is here: -pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} - ^--^ -A:2:97: Second constructor call is here: -pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} - ^--^ +{"errors":[{"component":"general","errorCode":"3364","formattedMessage":"DeclarationError: Base constructor arguments given twice. + --> A:2:105: + | +2 | pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} + | ^^^^^^^^^^^^^^^^^^^^^ +Note: First constructor call is here: + --> A:2:74: + | +2 | pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} + | ^^^^ +Note: Second constructor call is here: + --> A:2:97: + | +2 | pragma solidity >=0.0; contract A { constructor(uint) {} } contract B is A(2) { } contract C is A(3) {} contract D is B, C {} + | ^^^^ + ","message":"Base constructor arguments given twice.","secondarySourceLocations":[{"end":112,"file":"A","message":"First constructor call is here:","start":108},{"end":135,"file":"A","message":"Second constructor call is here:","start":131}],"severity":"error","sourceLocation":{"end":160,"file":"A","start":139},"type":"DeclarationError"}],"sources":{}} diff --git a/test/cmdlineTests/standard_viair_requested/input.json b/test/cmdlineTests/standard_viair_requested/input.json new file mode 100644 index 000000000000..76ae807a615c --- /dev/null +++ b/test/cmdlineTests/standard_viair_requested/input.json @@ -0,0 +1,21 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C {} contract D { function f() public { C c = new C(); } }" + } + }, + "settings": + { + "optimizer": { + "enabled": true + }, + "outputSelection": + { + "*": { "*": ["ir", "evm.bytecode.object", "evm.bytecode.generatedSources", "evm.deployedBytecode.object"] } + }, + "viaIR": true + } +} diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json new file mode 100644 index 000000000000..45479dad0a23 --- /dev/null +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -0,0 +1,222 @@ +{"contracts":{"A":{"C":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"object":""}},"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"C_3\" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_C_3() + + codecopy(0, dataoffset(\"C_3_deployed\"), datasize(\"C_3_deployed\")) + + return(0, datasize(\"C_3_deployed\")) + + function constructor_C_3() { + + } + + } + object \"C_3_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + + } + +} + +"},"D":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"object":""}},"ir":"/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object \"D_16\" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_D_16() + + codecopy(0, dataoffset(\"D_16_deployed\"), datasize(\"D_16_deployed\")) + + return(0, datasize(\"D_16_deployed\")) + + function constructor_D_16() { + + } + + } + object \"D_16_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + fun_f_15() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple__to__fromStack(memPos ) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function allocateTemporaryMemory() -> memPtr { + memPtr := mload(64) + } + + function fun_f_15() { + + let _1 := allocateTemporaryMemory() + let _2 := add(_1, datasize(\"C_3\")) + if or(gt(_2, 0xffffffffffffffff), lt(_2, _1)) { panic_error_0x41() } + datacopy(_1, dataoffset(\"C_3\"), datasize(\"C_3\")) + _2 := abi_encode_tuple__to__fromStack(_2) + + let expr_12_address := create(0, _1, sub(_2, _1)) + + if iszero(expr_12_address) { revert_forward_1() } + + releaseTemporaryMemory() + let vloc_c_8_address := expr_12_address + + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function releaseTemporaryMemory() { + } + + function revert_forward_1() { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + /******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + object \"C_3\" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_C_3() + + codecopy(0, dataoffset(\"C_3_deployed\"), datasize(\"C_3_deployed\")) + + return(0, datasize(\"C_3_deployed\")) + + function constructor_C_3() { + + } + + } + object \"C_3_deployed\" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + } + + } + + } + + } + +} + +"}}},"errors":[{"component":"general","errorCode":"2072","formattedMessage":"Warning: Unused local variable. + --> A:2:93: + | +2 | pragma solidity >=0.0; pragma abicoder v2; contract C {} contract D { function f() public { C c = new C(); } } + | ^^^ + +","message":"Unused local variable.","severity":"warning","sourceLocation":{"end":131,"file":"A","start":128},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json index 46090f7e3a30..b20db84dd7a7 100644 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses/input.json @@ -2,14 +2,14 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { "outputSelection": { "fileA": { "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] + "": [ "ast" ] } } }, diff --git a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json index 96ca950b1596..fc69602b8470 100644 --- a/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json +++ b/test/cmdlineTests/standard_wrong_type_auxiliary_input_smtlib2responses_member/input.json @@ -2,14 +2,14 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { "outputSelection": { "fileA": { "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] + "": [ "ast" ] } } }, diff --git a/test/cmdlineTests/standard_wrong_type_output_selection/input.json b/test/cmdlineTests/standard_wrong_type_output_selection/input.json index 5f03fbf7bc63..1a3763e92f51 100644 --- a/test/cmdlineTests/standard_wrong_type_output_selection/input.json +++ b/test/cmdlineTests/standard_wrong_type_output_selection/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json b/test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json index 4004b2285b71..73e1e6060a37 100644 --- a/test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json +++ b/test/cmdlineTests/standard_wrong_type_output_selection_contract/input.json @@ -2,14 +2,14 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { "outputSelection": { "fileA": { "A": "it's a contract, but not an array!", - "": [ "legacyAST" ] + "": [ "ast" ] } } } diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_file/input.json b/test/cmdlineTests/standard_wrong_type_output_selection_file/input.json index 9bfdb99ad9bf..25b53f0e64ba 100644 --- a/test/cmdlineTests/standard_wrong_type_output_selection_file/input.json +++ b/test/cmdlineTests/standard_wrong_type_output_selection_file/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { diff --git a/test/cmdlineTests/standard_wrong_type_output_selection_output/input.json b/test/cmdlineTests/standard_wrong_type_output_selection_output/input.json index d5c51fa93854..9a6cdbe5fe6e 100644 --- a/test/cmdlineTests/standard_wrong_type_output_selection_output/input.json +++ b/test/cmdlineTests/standard_wrong_type_output_selection_output/input.json @@ -2,14 +2,14 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { "outputSelection": { "fileA": { "A": [ 1, 2, 3 ,4], - "": [ "legacyAST" ] + "": [ "ast" ] } } } diff --git a/test/cmdlineTests/standard_wrong_type_remappings/input.json b/test/cmdlineTests/standard_wrong_type_remappings/input.json index 4d64b9e47fa9..a2f13d0f7726 100644 --- a/test/cmdlineTests/standard_wrong_type_remappings/input.json +++ b/test/cmdlineTests/standard_wrong_type_remappings/input.json @@ -2,14 +2,14 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { "outputSelection": { "fileA": { "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] + "": [ "ast" ] } }, "remappings": "not an object" diff --git a/test/cmdlineTests/standard_wrong_type_remappings_entry/input.json b/test/cmdlineTests/standard_wrong_type_remappings_entry/input.json index ee7fb52fa15e..0b03d984deb5 100644 --- a/test/cmdlineTests/standard_wrong_type_remappings_entry/input.json +++ b/test/cmdlineTests/standard_wrong_type_remappings_entry/input.json @@ -2,14 +2,14 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { "outputSelection": { "fileA": { "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] + "": [ "ast" ] } }, "remappings": [1, 2 ,3 ,4] diff --git a/test/cmdlineTests/standard_yul/output.json b/test/cmdlineTests/standard_yul/output.json index ac6c8f7cbf2c..e6c6e1390835 100644 --- a/test/cmdlineTests/standard_yul/output.json +++ b/test/cmdlineTests/standard_yul/output.json @@ -14,7 +14,7 @@ sstore /* \"A\":0:42 */ pop -","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"object\" { +","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { code { let x := mload(0) sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_object/output.json b/test/cmdlineTests/standard_yul_object/output.json index 946e5773b68a..d4dcb7acd71d 100644 --- a/test/cmdlineTests/standard_yul_object/output.json +++ b/test/cmdlineTests/standard_yul_object/output.json @@ -13,7 +13,7 @@ pop stop data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 616263 -","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"NamedObject\" { +","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"NamedObject\" { code { let x := dataoffset(\"DataName\") sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_object_invalid_sub/output.json b/test/cmdlineTests/standard_yul_object_invalid_sub/output.json index 5008d4916c23..44e04ecd8686 100644 --- a/test/cmdlineTests/standard_yul_object_invalid_sub/output.json +++ b/test/cmdlineTests/standard_yul_object_invalid_sub/output.json @@ -1,4 +1,7 @@ -{"errors":[{"component":"general","formattedMessage":"A:1:40: TypeError: Unknown data object \"NamedObject.\". -object \"NamedObject\" { code { let x := dataoffset(\"NamedObject.\") sstore(add(x, 0), 0) } object \"OtherObject\" { code { revert(0, 0) } } } - ^--------^ -","message":"Unknown data object \"NamedObject.\".","severity":"error","sourceLocation":{"end":49,"file":"A","start":39},"type":"TypeError"}]} +{"errors":[{"component":"general","formattedMessage":"TypeError: Unknown data object \"NamedObject.\". + --> A:1:51: + | +1 | object \"NamedObject\" { code { let x := dataoffset(\"NamedObject.\") sstore(add(x, 0), 0) } object \"OtherObject\" { code { revert(0, 0) } } } + | ^^^^^^^^^^^^^^ + +","message":"Unknown data object \"NamedObject.\".","severity":"error","sourceLocation":{"end":64,"file":"A","start":50},"type":"TypeError"}]} diff --git a/test/cmdlineTests/standard_yul_object_name/output.json b/test/cmdlineTests/standard_yul_object_name/output.json index 58d78b3a90ca..0abd732e4b92 100644 --- a/test/cmdlineTests/standard_yul_object_name/output.json +++ b/test/cmdlineTests/standard_yul_object_name/output.json @@ -22,7 +22,7 @@ sub_0: assembly { /* \"A\":137:149 */ revert } -","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"},"deployedBytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"NamedObject\" { +","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"NamedObject\" { code { let x := dataoffset(\"DataName\") sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_optimiserSteps/output.json b/test/cmdlineTests/standard_yul_optimiserSteps/output.json index 383ad85571dd..536ee0047511 100644 --- a/test/cmdlineTests/standard_yul_optimiserSteps/output.json +++ b/test/cmdlineTests/standard_yul_optimiserSteps/output.json @@ -13,7 +13,7 @@ /* \"A\":20:40 */ sstore pop -","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"object\" { +","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { code { let x := mload(0) sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_optimized/output.json b/test/cmdlineTests/standard_yul_optimized/output.json index 532ff00d9190..758904e93c07 100644 --- a/test/cmdlineTests/standard_yul_optimized/output.json +++ b/test/cmdlineTests/standard_yul_optimized/output.json @@ -5,7 +5,7 @@ mload /* \"A\":20:40 */ sstore -","bytecode":{"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"sourceMap removed"}},"ir":"object \"object\" { +","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { code { let x := mload(0) sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/storage_layout_bytes/input.json b/test/cmdlineTests/storage_layout_bytes/input.json index cd69dd0edf55..711527f00fdd 100644 --- a/test/cmdlineTests/storage_layout_bytes/input.json +++ b/test/cmdlineTests/storage_layout_bytes/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { bytes s1 = \"test\"; bytes s2; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { bytes s1 = \"test\"; bytes s2; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_bytes/output.json b/test/cmdlineTests/storage_layout_bytes/output.json index 613189a2a869..b589a486f119 100644 --- a/test/cmdlineTests/storage_layout_bytes/output.json +++ b/test/cmdlineTests/storage_layout_bytes/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":4,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":6,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_dyn_array/input.json b/test/cmdlineTests/storage_layout_dyn_array/input.json index 2904a9cefe93..d334360573f8 100644 --- a/test/cmdlineTests/storage_layout_dyn_array/input.json +++ b/test/cmdlineTests/storage_layout_dyn_array/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { uint[] array1; bool[] array2; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { uint[] array1; bool[] array2; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_dyn_array/output.json b/test/cmdlineTests/storage_layout_dyn_array/output.json index 8be650aa54f5..58e129ae7c3b 100644 --- a/test/cmdlineTests/storage_layout_dyn_array/output.json +++ b/test/cmdlineTests/storage_layout_dyn_array/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":6,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":4,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":7,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_many/input.json b/test/cmdlineTests/storage_layout_many/input.json index 90b4e25dd17d..6424f189ce53 100644 --- a/test/cmdlineTests/storage_layout_many/input.json +++ b/test/cmdlineTests/storage_layout_many/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; mapping (uint => mapping (address => bool)) map; uint[] array; string s1; bytes b1; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; mapping (uint => mapping (address => bool)) map; uint[] array; string s1; bytes b1; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_many/output.json b/test/cmdlineTests/storage_layout_many/output.json index a413e1027c1e..33c3005d0469 100644 --- a/test/cmdlineTests/storage_layout_many/output.json +++ b/test/cmdlineTests/storage_layout_many/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":26,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":29,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":31,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":33,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":15,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":17,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":20,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)13_storage"},{"astId":22,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":28,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":31,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":33,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":35,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)13_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":3,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":5,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":9,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":12,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_mapping/input.json b/test/cmdlineTests/storage_layout_mapping/input.json index 66ecff53736c..21787fe99d29 100644 --- a/test/cmdlineTests/storage_layout_mapping/input.json +++ b/test/cmdlineTests/storage_layout_mapping/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_mapping/output.json b/test/cmdlineTests/storage_layout_mapping/output.json index e3a4944fd2dc..e87ec77f1651 100644 --- a/test/cmdlineTests/storage_layout_mapping/output.json +++ b/test/cmdlineTests/storage_layout_mapping/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":10,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":5,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":11,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke/input.json b/test/cmdlineTests/storage_layout_smoke/input.json index 90fd18101b28..5980e6115be7 100644 --- a/test/cmdlineTests/storage_layout_smoke/input.json +++ b/test/cmdlineTests/storage_layout_smoke/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_smoke/output.json b/test/cmdlineTests/storage_layout_smoke/output.json index c5830bb00678..519cb1f0410d 100644 --- a/test/cmdlineTests/storage_layout_smoke/output.json +++ b/test/cmdlineTests/storage_layout_smoke/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json b/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json index 15cb774f5c2b..b7309612b13a 100644 --- a/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json +++ b/test/cmdlineTests/storage_layout_smoke_two_contracts/input.json @@ -2,10 +2,10 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { }" }, "fileB": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { uint x; uint y; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { uint x; uint y; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json index 8ec54ae260af..c94f92eb8089 100644 --- a/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json +++ b/test/cmdlineTests/storage_layout_smoke_two_contracts/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0},"fileB":{"id":1}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"sources":{"fileA":{"id":0},"fileB":{"id":1}}} diff --git a/test/cmdlineTests/storage_layout_string/input.json b/test/cmdlineTests/storage_layout_string/input.json index d069d309830b..d14a71d87639 100644 --- a/test/cmdlineTests/storage_layout_string/input.json +++ b/test/cmdlineTests/storage_layout_string/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { string s1 = \"test\"; string s2; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { string s1 = \"test\"; string s2; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_string/output.json b/test/cmdlineTests/storage_layout_string/output.json index 59312174fa71..a83f5a513d86 100644 --- a/test/cmdlineTests/storage_layout_string/output.json +++ b/test/cmdlineTests/storage_layout_string/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":4,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":6,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct/input.json b/test/cmdlineTests/storage_layout_struct/input.json index 7353386df232..a392cd9e53da 100644 --- a/test/cmdlineTests/storage_layout_struct/input.json +++ b/test/cmdlineTests/storage_layout_struct/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_struct/output.json b/test/cmdlineTests/storage_layout_struct/output.json index e8c2d6fdc498..fed30fd36193 100644 --- a/test/cmdlineTests/storage_layout_struct/output.json +++ b/test/cmdlineTests/storage_layout_struct/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":15,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":17,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":20,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)13_storage"},{"astId":22,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)13_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":3,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":5,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":9,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":12,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_struct_packed/input.json b/test/cmdlineTests/storage_layout_struct_packed/input.json index e562a6a3adb7..32be56207775 100644 --- a/test/cmdlineTests/storage_layout_struct_packed/input.json +++ b/test/cmdlineTests/storage_layout_struct_packed/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_struct_packed/output.json b/test/cmdlineTests/storage_layout_struct_packed/output.json index 7ac8c11eb5e8..5f610719ce60 100644 --- a/test/cmdlineTests/storage_layout_struct_packed/output.json +++ b/test/cmdlineTests/storage_layout_struct_packed/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":15,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":17,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":20,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)13_storage"},{"astId":22,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)13_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":3,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":5,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":9,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":12,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types/input.json b/test/cmdlineTests/storage_layout_value_types/input.json index 3f0dd709c192..96335f45c427 100644 --- a/test/cmdlineTests/storage_layout_value_types/input.json +++ b/test/cmdlineTests/storage_layout_value_types/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { uint x; uint y; address addr; uint[2] array; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { uint x; uint y; address addr; uint[2] array; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_value_types/output.json b/test/cmdlineTests/storage_layout_value_types/output.json index 2d10422a5a55..db690733ee2b 100644 --- a/test/cmdlineTests/storage_layout_value_types/output.json +++ b/test/cmdlineTests/storage_layout_value_types/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":6,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":10,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":5,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":7,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":11,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/storage_layout_value_types_packed/input.json b/test/cmdlineTests/storage_layout_value_types_packed/input.json index 18af8f50f32b..a16ddb5625f3 100644 --- a/test/cmdlineTests/storage_layout_value_types_packed/input.json +++ b/test/cmdlineTests/storage_layout_value_types_packed/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "fileA": { - "content": "//SPDX-License-Identifier: GPL-3.0\ncontract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;\ncontract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }" } }, "settings": { diff --git a/test/cmdlineTests/storage_layout_value_types_packed/output.json b/test/cmdlineTests/storage_layout_value_types_packed/output.json index 634cb9305a8d..1b01899a5068 100644 --- a/test/cmdlineTests/storage_layout_value_types_packed/output.json +++ b/test/cmdlineTests/storage_layout_value_types_packed/output.json @@ -1,2 +1 @@ -{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":4,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":6,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":12,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"errors":[{"component":"general","errorCode":"3420","formattedMessage":"fileA: Warning: Source file does not specify required compiler version! -","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"fileA","start":-1},"type":"Warning"}],"sources":{"fileA":{"id":0}}} +{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":5,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":7,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":9,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":13,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"sources":{"fileA":{"id":0}}} diff --git a/test/cmdlineTests/strict_asm_jump/err b/test/cmdlineTests/strict_asm_jump/err index caefd169096e..48d061fee521 100644 --- a/test/cmdlineTests/strict_asm_jump/err +++ b/test/cmdlineTests/strict_asm_jump/err @@ -1,6 +1,6 @@ Warning: Yul is still experimental. Please use the output with care. Error: Function not found. - --> strict_asm_jump/input.sol:1:3: + --> strict_asm_jump/input.yul:1:3: | 1 | { jump(1) } | ^^^^ diff --git a/test/cmdlineTests/strict_asm_jump/input.sol b/test/cmdlineTests/strict_asm_jump/input.yul similarity index 100% rename from test/cmdlineTests/strict_asm_jump/input.sol rename to test/cmdlineTests/strict_asm_jump/input.yul diff --git a/test/cmdlineTests/too_long_line_multiline/err b/test/cmdlineTests/too_long_line_multiline/err index 6d8114ca8b92..6f5dcb71c9f1 100644 --- a/test/cmdlineTests/too_long_line_multiline/err +++ b/test/cmdlineTests/too_long_line_multiline/err @@ -4,7 +4,7 @@ Warning: SPDX license identifier not provided in source file. Before publishing, Error: No visibility specified. Did you intend to add "public"? --> too_long_line_multiline/input.sol:2:5: | -2 | function f() returns (byte _b, byte ... _b7, bytes22 _b22, bytes32 _b32) { +2 | function f() returns (bytes1 _b, by ... _b7, bytes22 _b22, bytes32 _b32) { | ^ (Relevant source part starts here and spans across multiple lines). Warning: Source file does not specify required compiler version! diff --git a/test/cmdlineTests/too_long_line_multiline/input.sol b/test/cmdlineTests/too_long_line_multiline/input.sol index 6609e1257b8f..107c6964b0b2 100644 --- a/test/cmdlineTests/too_long_line_multiline/input.sol +++ b/test/cmdlineTests/too_long_line_multiline/input.sol @@ -1,5 +1,5 @@ contract C { - function f() returns (byte _b, bytes2 _b2, bytes3 _b3, bytes memory _blit, bytes5 _b5, bytes6 _b6, string memory _str, bytes7 _b7, bytes22 _b22, bytes32 _b32) { + function f() returns (bytes1 _b, bytes2 _b2, bytes3 _b3, bytes memory _blit, bytes5 _b5, bytes6 _b6, string memory _str, bytes7 _b7, bytes22 _b22, bytes32 _b32) { _b = 0x12; _b2 = 0x1223; _b5 = hex"043245"; diff --git a/test/cmdlineTests/viair_abicoder_v1/args b/test/cmdlineTests/viair_abicoder_v1/args new file mode 100644 index 000000000000..e869749f234a --- /dev/null +++ b/test/cmdlineTests/viair_abicoder_v1/args @@ -0,0 +1 @@ +--ir --error-codes diff --git a/test/cmdlineTests/viair_abicoder_v1/err b/test/cmdlineTests/viair_abicoder_v1/err new file mode 100644 index 000000000000..55f8ba4b91b3 --- /dev/null +++ b/test/cmdlineTests/viair_abicoder_v1/err @@ -0,0 +1,5 @@ +Warning (2066): Contract requests the ABI coder v1, which is incompatible with the IR. Using ABI coder v2 instead. + --> viair_abicoder_v1/input.sol:4:1: + | +4 | contract test { + | ^ (Relevant source part starts here and spans across multiple lines). diff --git a/test/cmdlineTests/viair_abicoder_v1/input.sol b/test/cmdlineTests/viair_abicoder_v1/input.sol new file mode 100644 index 000000000000..198e40e16a69 --- /dev/null +++ b/test/cmdlineTests/viair_abicoder_v1/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma abicoder v1; +contract test { + function f() public pure returns (bool) { + return true; + } +} diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output new file mode 100644 index 000000000000..8810db8a5b95 --- /dev/null +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -0,0 +1,110 @@ +IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object "test_11" { + code { + mstore(64, 128) + if callvalue() { revert(0, 0) } + + constructor_test_11() + + codecopy(0, dataoffset("test_11_deployed"), datasize("test_11_deployed")) + + return(0, datasize("test_11_deployed")) + + function constructor_test_11() { + + } + + } + object "test_11_deployed" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + if callvalue() { revert(0, 0) } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := fun_f_10() + let memPos := allocateMemory(0) + let memEnd := abi_encode_tuple_t_bool__to_t_bool__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + if iszero(calldatasize()) { } + revert(0, 0) + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + + } + + function abi_encode_t_bool_to_t_bool_fromStack(value, pos) { + mstore(pos, cleanup_t_bool(value)) + } + + function abi_encode_tuple_t_bool__to_t_bool__fromStack(headStart , value0) -> tail { + tail := add(headStart, 32) + + abi_encode_t_bool_to_t_bool_fromStack(value0, add(headStart, 0)) + + } + + function allocateMemory(size) -> memPtr { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function cleanup_t_bool(value) -> cleaned { + cleaned := iszero(iszero(value)) + } + + function fun_f_10() -> vloc__5 { + let zero_value_for_type_t_bool_1 := zero_value_for_split_t_bool() + vloc__5 := zero_value_for_type_t_bool_1 + + let expr_7 := 0x01 + vloc__5 := expr_7 + leave + + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function zero_value_for_split_t_bool() -> ret { + ret := 0 + } + + } + + } + +} diff --git a/test/cmdlineTests/viair_subobjects/args b/test/cmdlineTests/viair_subobjects/args new file mode 100644 index 000000000000..1a5580b80bb8 --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/args @@ -0,0 +1 @@ +--ir-optimized --experimental-via-ir --optimize --bin --bin-runtime \ No newline at end of file diff --git a/test/cmdlineTests/viair_subobjects/err b/test/cmdlineTests/viair_subobjects/err new file mode 100644 index 000000000000..5046525bef7a --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/err @@ -0,0 +1,5 @@ +Warning: Unused local variable. + --> viair_subobjects/input.sol:8:9: + | +8 | C c = new C(); + | ^^^ diff --git a/test/cmdlineTests/viair_subobjects/input.sol b/test/cmdlineTests/viair_subobjects/input.sol new file mode 100644 index 000000000000..bad0fe74f8f0 --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/input.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.6.0; +pragma abicoder v2; + +contract C {} +contract D { + function f() public { + C c = new C(); + } +} diff --git a/test/cmdlineTests/viair_subobjects/output b/test/cmdlineTests/viair_subobjects/output new file mode 100644 index 000000000000..25c3acdb89f1 --- /dev/null +++ b/test/cmdlineTests/viair_subobjects/output @@ -0,0 +1,118 @@ + +======= viair_subobjects/input.sol:C ======= +Binary: +60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +Binary of the runtime part: + +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "C_3" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("C_3_deployed") + codecopy(0, dataoffset("C_3_deployed"), _1) + return(0, _1) + } + } + object "C_3_deployed" { + code { + { + mstore(64, 128) + revert(0, 0) + } + } + } +} + + +======= viair_subobjects/input.sol:D ======= +Binary: +608060405234156100105760006000fd5b61010680610021600039806000f350fe6080604052600436101515610088576000803560e01c6326121ff0141561008657341561002a578081fd5b806003193601121561003a578081fd5b6028806080016080811067ffffffffffffffff8211171561005e5761005d6100c4565b5b50806100de60803980608083f01515610079573d82833e3d82fd5b508061008482610092565bf35b505b60006000fd6100dc565b6000604051905081810181811067ffffffffffffffff821117156100b9576100b86100c4565b5b80604052505b919050565b634e487b7160e01b600052604160045260246000fd5b565bfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +Binary of the runtime part: + +Optimized IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + +object "D_16" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("D_16_deployed") + codecopy(0, dataoffset("D_16_deployed"), _1) + return(0, _1) + } + } + object "D_16_deployed" { + code { + { + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + if eq(0x26121ff0, shr(224, calldataload(_1))) + { + if callvalue() { revert(_1, _1) } + if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } + let _2 := datasize("C_3") + let _3 := add(128, _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, 128)) { panic_error_0x41() } + datacopy(128, dataoffset("C_3"), _2) + if iszero(create(_1, 128, _2)) + { + returndatacopy(_1, _1, returndatasize()) + revert(_1, returndatasize()) + } + return(allocateMemory(_1), _1) + } + } + revert(0, 0) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + function panic_error_0x41() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + } + object "C_3" { + code { + { + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("C_3_deployed") + codecopy(0, dataoffset("C_3_deployed"), _1) + return(0, _1) + } + } + object "C_3_deployed" { + code { + { + mstore(64, 128) + revert(0, 0) + } + } + } + } + } +} diff --git a/test/cmdlineTests/yul_optimizer_steps/input.sol b/test/cmdlineTests/yul_optimizer_steps/input.sol index c93363e41d4c..260b21cd57f3 100644 --- a/test/cmdlineTests/yul_optimizer_steps/input.sol +++ b/test/cmdlineTests/yul_optimizer_steps/input.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.0; +pragma abicoder v2; contract C { diff --git a/test/cmdlineTests/yul_optimizer_steps/output b/test/cmdlineTests/yul_optimizer_steps/output index 847c731e9388..c06add7c71b0 100644 --- a/test/cmdlineTests/yul_optimizer_steps/output +++ b/test/cmdlineTests/yul_optimizer_steps/output @@ -6,16 +6,16 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *******************************************************/ -object "C_6" { +object "C_7" { code { { mstore(64, 128) if callvalue() { revert(0, 0) } - codecopy(0, dataoffset("C_6_deployed"), datasize("C_6_deployed")) - return(0, datasize("C_6_deployed")) + codecopy(0, dataoffset("C_7_deployed"), datasize("C_7_deployed")) + return(0, datasize("C_7_deployed")) } } - object "C_6_deployed" { + object "C_7_deployed" { code { { mstore(64, 128) diff --git a/test/cmdlineTests/yul_stack_opt_disabled/input.sol b/test/cmdlineTests/yul_stack_opt/input.yul similarity index 79% rename from test/cmdlineTests/yul_stack_opt_disabled/input.sol rename to test/cmdlineTests/yul_stack_opt/input.yul index 772a6d4df9b5..d60620ecf604 100644 --- a/test/cmdlineTests/yul_stack_opt_disabled/input.sol +++ b/test/cmdlineTests/yul_stack_opt/input.yul @@ -17,6 +17,21 @@ sstore(add(a, 10), b) sstore(add(a, 11), b) sstore(add(a, 12), b) + a3 := 1 + b3 := 1 + c3 := 1 + d3 := 1 + e3 := 1 + f3 := 1 + g3 := 1 + h3 := 1 + i3 := 1 + j3 := 1 + k3 := 1 + l3 := 1 + m3 := 1 + o3 := 1 + p3 := 1 } let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun() let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun() diff --git a/test/cmdlineTests/yul_stack_opt/output b/test/cmdlineTests/yul_stack_opt/output index e5731f1e1710..7c0e8ca669de 100644 --- a/test/cmdlineTests/yul_stack_opt/output +++ b/test/cmdlineTests/yul_stack_opt/output @@ -1,15 +1,9 @@ -======= yul_stack_opt/input.sol (EVM) ======= +======= yul_stack_opt/input.yul (EVM) ======= Pretty printed source: object "object" { code { - { - let a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1, p1 := fun() - let a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2, p2 := fun() - sstore(a1, a2) - } - function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3 { let _1 := 1 sstore(_1, _1) @@ -25,180 +19,187 @@ object "object" { sstore(11, _1) sstore(12, _1) sstore(13, _1) + sstore(_1, _1) + sstore(2, _1) + sstore(3, _1) + sstore(4, _1) + sstore(5, _1) + sstore(6, _1) + sstore(7, _1) + sstore(8, _1) + sstore(9, _1) + sstore(10, _1) + sstore(11, _1) + sstore(12, _1) + sstore(13, _1) + sstore(_1, _1) } } } Binary representation: -60056032565b505050505050505050505050505050601a6032565b5050505050505050505050505050508082555050609b565b60006000600060006000600060006000600060006000600060006000600060006001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55505b909192939495969798999a9b9c9d9e9f565b +6001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d5580815550 Text representation: - /* "yul_stack_opt/input.sol":495:500 */ - tag_1 - tag_2 - jump // in -tag_1: - /* "yul_stack_opt/input.sol":425:500 */ - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - /* "yul_stack_opt/input.sol":572:577 */ - tag_3 - tag_2 - jump // in -tag_3: - /* "yul_stack_opt/input.sol":502:577 */ - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - pop - /* "yul_stack_opt/input.sol":590:592 */ + /* "yul_stack_opt/input.yul":98:99 */ + 0x01 dup1 - /* "yul_stack_opt/input.sol":586:588 */ - dup3 - /* "yul_stack_opt/input.sol":579:593 */ + dup2 + /* "yul_stack_opt/input.yul":129:141 */ sstore - pop - pop - /* "yul_stack_opt/input.sol":3:423 */ - jump(tag_4) -tag_2: - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - 0x00 - /* "yul_stack_opt/input.sol":98:99 */ - 0x01 + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":151:160 */ + 0x02 + /* "yul_stack_opt/input.yul":144:164 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":174:183 */ + 0x03 + /* "yul_stack_opt/input.yul":167:187 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":197:206 */ + 0x04 + /* "yul_stack_opt/input.yul":190:210 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":220:229 */ + 0x05 + /* "yul_stack_opt/input.yul":213:233 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":243:252 */ + 0x06 + /* "yul_stack_opt/input.yul":236:256 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":266:275 */ + 0x07 + /* "yul_stack_opt/input.yul":259:279 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":289:298 */ + 0x08 + /* "yul_stack_opt/input.yul":282:302 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":312:321 */ + 0x09 + /* "yul_stack_opt/input.yul":305:325 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":335:344 */ + 0x0a + /* "yul_stack_opt/input.yul":328:348 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":358:368 */ + 0x0b + /* "yul_stack_opt/input.yul":351:372 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":382:392 */ + 0x0c + /* "yul_stack_opt/input.yul":375:396 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + /* "yul_stack_opt/input.yul":406:416 */ + 0x0d + /* "yul_stack_opt/input.yul":399:420 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ dup1 dup2 - /* "yul_stack_opt/input.sol":129:141 */ + /* "yul_stack_opt/input.yul":129:141 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":151:160 */ + /* "yul_stack_opt/input.yul":151:160 */ 0x02 - /* "yul_stack_opt/input.sol":144:164 */ + /* "yul_stack_opt/input.yul":144:164 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":174:183 */ + /* "yul_stack_opt/input.yul":174:183 */ 0x03 - /* "yul_stack_opt/input.sol":167:187 */ + /* "yul_stack_opt/input.yul":167:187 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":197:206 */ + /* "yul_stack_opt/input.yul":197:206 */ 0x04 - /* "yul_stack_opt/input.sol":190:210 */ + /* "yul_stack_opt/input.yul":190:210 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":220:229 */ + /* "yul_stack_opt/input.yul":220:229 */ 0x05 - /* "yul_stack_opt/input.sol":213:233 */ + /* "yul_stack_opt/input.yul":213:233 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":243:252 */ + /* "yul_stack_opt/input.yul":243:252 */ 0x06 - /* "yul_stack_opt/input.sol":236:256 */ + /* "yul_stack_opt/input.yul":236:256 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":266:275 */ + /* "yul_stack_opt/input.yul":266:275 */ 0x07 - /* "yul_stack_opt/input.sol":259:279 */ + /* "yul_stack_opt/input.yul":259:279 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":289:298 */ + /* "yul_stack_opt/input.yul":289:298 */ 0x08 - /* "yul_stack_opt/input.sol":282:302 */ + /* "yul_stack_opt/input.yul":282:302 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":312:321 */ + /* "yul_stack_opt/input.yul":312:321 */ 0x09 - /* "yul_stack_opt/input.sol":305:325 */ + /* "yul_stack_opt/input.yul":305:325 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":335:344 */ + /* "yul_stack_opt/input.yul":335:344 */ 0x0a - /* "yul_stack_opt/input.sol":328:348 */ + /* "yul_stack_opt/input.yul":328:348 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":358:368 */ + /* "yul_stack_opt/input.yul":358:368 */ 0x0b - /* "yul_stack_opt/input.sol":351:372 */ + /* "yul_stack_opt/input.yul":351:372 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":382:392 */ + /* "yul_stack_opt/input.yul":382:392 */ 0x0c - /* "yul_stack_opt/input.sol":375:396 */ + /* "yul_stack_opt/input.yul":375:396 */ sstore - /* "yul_stack_opt/input.sol":98:99 */ + /* "yul_stack_opt/input.yul":98:99 */ dup1 - /* "yul_stack_opt/input.sol":406:416 */ + /* "yul_stack_opt/input.yul":406:416 */ 0x0d - /* "yul_stack_opt/input.sol":399:420 */ + /* "yul_stack_opt/input.yul":399:420 */ + sstore + /* "yul_stack_opt/input.yul":98:99 */ + dup1 + dup2 + /* "yul_stack_opt/input.yul":729:743 */ sstore pop - /* "yul_stack_opt/input.sol":85:423 */ -tag_5: - swap1 - swap2 - swap3 - swap4 - swap5 - swap6 - swap7 - swap8 - swap9 - swap10 - swap11 - swap12 - swap13 - swap14 - swap15 - swap16 - jump // out -tag_4: diff --git a/test/cmdlineTests/yul_stack_opt/input.sol b/test/cmdlineTests/yul_stack_opt_disabled/input.yul similarity index 100% rename from test/cmdlineTests/yul_stack_opt/input.sol rename to test/cmdlineTests/yul_stack_opt_disabled/input.yul diff --git a/test/cmdlineTests/yul_stack_opt_disabled/output b/test/cmdlineTests/yul_stack_opt_disabled/output index f6fa373dff56..c65f6f367779 100644 --- a/test/cmdlineTests/yul_stack_opt_disabled/output +++ b/test/cmdlineTests/yul_stack_opt_disabled/output @@ -1,5 +1,5 @@ -======= yul_stack_opt_disabled/input.sol (EVM) ======= +======= yul_stack_opt_disabled/input.yul (EVM) ======= Pretty printed source: object "object" { diff --git a/test/cmdlineTests/yul_string_format_ascii/input.json b/test/cmdlineTests/yul_string_format_ascii/input.json index 0eba555c953c..7b86b65cdac9 100644 --- a/test/cmdlineTests/yul_string_format_ascii/input.json +++ b/test/cmdlineTests/yul_string_format_ascii/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { function f() external pure returns (string memory) { return \"abcabc\"; } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { function f() external pure returns (string memory) { return \"abcabc\"; } }" } }, "settings": diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 0609704c5c59..33939dd9cf9b 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -6,23 +6,23 @@ *******************************************************/ -object \"C_10\" { +object \"C_11\" { code { mstore(64, 128) if callvalue() { revert(0, 0) } - constructor_C_10() + constructor_C_11() - codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_10_deployed\")) + return(0, datasize(\"C_11_deployed\")) - function constructor_C_10() { + function constructor_C_11() { } } - object \"C_10_deployed\" { + object \"C_11_deployed\" { code { mstore(64, 128) @@ -36,7 +36,7 @@ object \"C_10\" { // f() if callvalue() { revert(0, 0) } abi_decode_tuple_(4, calldatasize()) - let ret_0 := fun_f_9() + let ret_0 := fun_f_10() let memPos := allocateMemory(0) let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) @@ -71,7 +71,7 @@ object \"C_10\" { memPtr := mload(64) let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } @@ -107,15 +107,21 @@ object \"C_10\" { } } - function fun_f_9() -> vloc__4_mpos { + function fun_f_10() -> vloc__5_mpos { let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() - vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__5_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos - vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() + vloc__5_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave } + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + function round_up_to_mul_of_32(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json index 3dbcdb819093..cb9318f33eb4 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { function f() external pure returns (bytes32) { return \"abcabc\"; } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { function f() external pure returns (bytes32) { return \"abcabc\"; } }" } }, "settings": diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 1bc00b338d56..06b71932b1eb 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -6,23 +6,23 @@ *******************************************************/ -object \"C_10\" { +object \"C_11\" { code { mstore(64, 128) if callvalue() { revert(0, 0) } - constructor_C_10() + constructor_C_11() - codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_10_deployed\")) + return(0, datasize(\"C_11_deployed\")) - function constructor_C_10() { + function constructor_C_11() { } } - object \"C_10_deployed\" { + object \"C_11_deployed\" { code { mstore(64, 128) @@ -36,7 +36,7 @@ object \"C_10\" { // f() if callvalue() { revert(0, 0) } abi_decode_tuple_(4, calldatasize()) - let ret_0 := fun_f_9() + let ret_0 := fun_f_10() let memPos := allocateMemory(0) let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) @@ -67,7 +67,7 @@ object \"C_10\" { memPtr := mload(64) let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } @@ -79,15 +79,21 @@ object \"C_10\" { converted := 0x6162636162630000000000000000000000000000000000000000000000000000 } - function fun_f_9() -> vloc__4 { + function fun_f_10() -> vloc__5 { let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() - vloc__4 := zero_value_for_type_t_bytes32_1 + vloc__5 := zero_value_for_type_t_bytes32_1 - vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() + vloc__5 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave } + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + function shift_right_224_unsigned(value) -> newValue { newValue := diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json index 003dddfa7ae7..fa45bb24083b 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { function f() external pure returns (bytes4) { return 0x61626364; } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { function f() external pure returns (bytes4) { return 0x61626364; } }" } }, "settings": diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index fdef7778120b..4e9be627a779 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -6,23 +6,23 @@ *******************************************************/ -object \"C_10\" { +object \"C_11\" { code { mstore(64, 128) if callvalue() { revert(0, 0) } - constructor_C_10() + constructor_C_11() - codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_10_deployed\")) + return(0, datasize(\"C_11_deployed\")) - function constructor_C_10() { + function constructor_C_11() { } } - object \"C_10_deployed\" { + object \"C_11_deployed\" { code { mstore(64, 128) @@ -36,7 +36,7 @@ object \"C_10\" { // f() if callvalue() { revert(0, 0) } abi_decode_tuple_(4, calldatasize()) - let ret_0 := fun_f_9() + let ret_0 := fun_f_10() let memPos := allocateMemory(0) let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) @@ -67,7 +67,7 @@ object \"C_10\" { memPtr := mload(64) let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } @@ -83,16 +83,22 @@ object \"C_10\" { converted := shift_left_224(cleanup_t_rational_1633837924_by_1(value)) } - function fun_f_9() -> vloc__4 { + function fun_f_10() -> vloc__5 { let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() - vloc__4 := zero_value_for_type_t_bytes4_1 + vloc__5 := zero_value_for_type_t_bytes4_1 - let expr_6 := 0x61626364 - vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) + let expr_7 := 0x61626364 + vloc__5 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_7) leave } + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + function shift_left_224(value) -> newValue { newValue := diff --git a/test/cmdlineTests/yul_string_format_ascii_long/input.json b/test/cmdlineTests/yul_string_format_ascii_long/input.json index 5d91e402e416..b55efa9137df 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/input.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { function f() external pure returns (string memory) { return \"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\"; } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { function f() external pure returns (string memory) { return \"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\"; } }" } }, "settings": diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 2a845796da93..ad30827cbf14 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -6,23 +6,23 @@ *******************************************************/ -object \"C_10\" { +object \"C_11\" { code { mstore(64, 128) if callvalue() { revert(0, 0) } - constructor_C_10() + constructor_C_11() - codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_10_deployed\")) + return(0, datasize(\"C_11_deployed\")) - function constructor_C_10() { + function constructor_C_11() { } } - object \"C_10_deployed\" { + object \"C_11_deployed\" { code { mstore(64, 128) @@ -36,7 +36,7 @@ object \"C_10\" { // f() if callvalue() { revert(0, 0) } abi_decode_tuple_(4, calldatasize()) - let ret_0 := fun_f_9() + let ret_0 := fun_f_10() let memPos := allocateMemory(0) let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) @@ -71,7 +71,7 @@ object \"C_10\" { memPtr := mload(64) let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } @@ -111,15 +111,21 @@ object \"C_10\" { } } - function fun_f_9() -> vloc__4_mpos { + function fun_f_10() -> vloc__5_mpos { let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() - vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__5_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos - vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() + vloc__5_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave } + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + function round_up_to_mul_of_32(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/test/cmdlineTests/yul_string_format_hex/input.json b/test/cmdlineTests/yul_string_format_hex/input.json index 9bb3fd138f4a..51d3dcdffe7a 100644 --- a/test/cmdlineTests/yul_string_format_hex/input.json +++ b/test/cmdlineTests/yul_string_format_hex/input.json @@ -4,7 +4,7 @@ { "A": { - "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }" + "content": "//SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }" } }, "settings": diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 784baf2f4655..77bfbee094c2 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -6,23 +6,23 @@ *******************************************************/ -object \"C_10\" { +object \"C_11\" { code { mstore(64, 128) if callvalue() { revert(0, 0) } - constructor_C_10() + constructor_C_11() - codecopy(0, dataoffset(\"C_10_deployed\"), datasize(\"C_10_deployed\")) + codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_10_deployed\")) + return(0, datasize(\"C_11_deployed\")) - function constructor_C_10() { + function constructor_C_11() { } } - object \"C_10_deployed\" { + object \"C_11_deployed\" { code { mstore(64, 128) @@ -36,7 +36,7 @@ object \"C_10\" { // f() if callvalue() { revert(0, 0) } abi_decode_tuple_(4, calldatasize()) - let ret_0 := fun_f_9() + let ret_0 := fun_f_10() let memPos := allocateMemory(0) let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) return(memPos, sub(memEnd, memPos)) @@ -67,7 +67,7 @@ object \"C_10\" { memPtr := mload(64) let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } mstore(64, newFreePtr) } @@ -83,16 +83,22 @@ object \"C_10\" { converted := shift_left_224(cleanup_t_rational_2864434397_by_1(value)) } - function fun_f_9() -> vloc__4 { + function fun_f_10() -> vloc__5 { let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() - vloc__4 := zero_value_for_type_t_bytes4_1 + vloc__5 := zero_value_for_type_t_bytes4_1 - let expr_6 := 0xaabbccdd - vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) + let expr_7 := 0xaabbccdd + vloc__5 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_7) leave } + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + function shift_left_224(value) -> newValue { newValue := diff --git a/test/cmdlineTests/yul_unimplemented/args b/test/cmdlineTests/yul_unimplemented/args new file mode 100644 index 000000000000..e869749f234a --- /dev/null +++ b/test/cmdlineTests/yul_unimplemented/args @@ -0,0 +1 @@ +--ir --error-codes diff --git a/test/cmdlineTests/yul_unimplemented/err b/test/cmdlineTests/yul_unimplemented/err new file mode 100644 index 000000000000..d1b862ce2963 --- /dev/null +++ b/test/cmdlineTests/yul_unimplemented/err @@ -0,0 +1,5 @@ +Error (1834): Unimplemented feature error in + --> yul_unimplemented/input.sol:6:16: + | +6 | return type(test).name; + | ^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/yul_unimplemented/exit b/test/cmdlineTests/yul_unimplemented/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/yul_unimplemented/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/yul_unimplemented/input.sol b/test/cmdlineTests/yul_unimplemented/input.sol new file mode 100644 index 000000000000..c9ea15c48a35 --- /dev/null +++ b/test/cmdlineTests/yul_unimplemented/input.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +pragma abicoder v2; +contract test { + function f() public pure returns (string memory) { + return type(test).name; + } +} \ No newline at end of file diff --git a/test/compilationTests/corion/ico.sol b/test/compilationTests/corion/ico.sol index 150ed8081b4b..b9fc0679fed7 100644 --- a/test/compilationTests/corion/ico.sol +++ b/test/compilationTests/corion/ico.sol @@ -65,7 +65,7 @@ contract ico is safeMath { icoExchangeRate = exchangeRate; icoExchangeRateSetBlock = block.number + exchangeRateDelay; icoEtcPriceAddr = priceSet; - owner = msg.sender; + owner = payable(msg.sender); if ( startBlockNum > 0 ) { require( startBlockNum >= block.number ); startBlock = startBlockNum; @@ -272,7 +272,7 @@ contract ico is safeMath { require( brought[msg.sender].eth > 0 ); uint256 _val = brought[msg.sender].eth * 90 / 100; delete brought[msg.sender]; - require( msg.sender.send(_val) ); + require( payable(msg.sender).send(_val) ); } receive () external payable { @@ -281,7 +281,7 @@ contract ico is safeMath { If they call the contract without any function then this process will be taken place. */ require( isICO() ); - require( buy(msg.sender, address(0x00)) ); + require( buy(payable(msg.sender), address(0x00)) ); } function buy(address payable beneficiaryAddress, address affilateAddress) public payable returns (bool success) { @@ -300,7 +300,7 @@ contract ico is safeMath { @affilateAddress The address of the person who offered who will get the referral reward. It can not be equal with the beneficiaryAddress. */ require( isICO() ); - if ( beneficiaryAddress == address(0x00)) { beneficiaryAddress = msg.sender; } + if ( beneficiaryAddress == address(0x00)) { beneficiaryAddress = payable(msg.sender); } if ( beneficiaryAddress == affilateAddress ) { affilateAddress = address(0x00); } diff --git a/test/compilationTests/corion/module.sol b/test/compilationTests/corion/module.sol index 707929a86b67..f729003832d5 100644 --- a/test/compilationTests/corion/module.sol +++ b/test/compilationTests/corion/module.sol @@ -90,10 +90,10 @@ contract module { @newModuleAddress New module handler address */ require( moduleStatus != status.New && moduleStatus != status.Disconnected); - (bool _success, uint256 _balance) = abstractModuleHandler(moduleHandlerAddress).balanceOf(address(this)); + (bool _success, uint256 _balance) = abstractModuleHandler(moduleHandlerAddress).balanceOf(payable(this)); require( _success ); if ( _balance > 0 ) { - require( abstractModuleHandler(moduleHandlerAddress).transfer(address(this), newModuleAddress, _balance, false) ); + require( abstractModuleHandler(moduleHandlerAddress).transfer(payable(this), newModuleAddress, _balance, false) ); } if ( address(this).balance > 0 ) { require( newModuleAddress.send(address(this).balance) ); diff --git a/test/compilationTests/corion/premium.sol b/test/compilationTests/corion/premium.sol index 7777530bb34b..8603b959006b 100644 --- a/test/compilationTests/corion/premium.sol +++ b/test/compilationTests/corion/premium.sol @@ -19,7 +19,7 @@ contract ptokenDB is tokenDB {} */ contract premium is module, safeMath { function replaceModule(address payable addr) external override returns (bool success) { - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); require( db.replaceOwner(addr) ); super._replaceModule(addr); return true; diff --git a/test/compilationTests/corion/provider.sol b/test/compilationTests/corion/provider.sol index 1e2d308a1ac8..524390b2ed3a 100644 --- a/test/compilationTests/corion/provider.sol +++ b/test/compilationTests/corion/provider.sol @@ -10,7 +10,7 @@ contract provider is module, safeMath, announcementTypes { module callbacks */ function connectModule() external override returns (bool success) { - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); super._connectModule(); (bool _success, uint256 currentSchellingRound) = moduleHandler(moduleHandlerAddress).getCurrentSchellingRoundID(); require( _success ); @@ -26,7 +26,7 @@ contract provider is module, safeMath, announcementTypes { @value amount @bool Was the function successful? */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); transferEvent_(from, value, true); transferEvent_(to, value, false); return true; @@ -41,7 +41,7 @@ contract provider is module, safeMath, announcementTypes { @reward token emission @bool Was the function successful? */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); globalFunds[roundID].reward = reward; globalFunds[roundID].supply = globalFunds[roundID-1].supply; currentSchellingRound = roundID; @@ -133,7 +133,7 @@ contract provider is module, safeMath, announcementTypes { @a Type of the setting @b value */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); if ( a == announcementType.providerPublicFunds ) { minFundsForPublic = b; } else if ( a == announcementType.providerPrivateFunds ) { minFundsForPrivate = b; } else if ( a == announcementType.providerPrivateClientLimit ) { privateProviderLimit = b; } diff --git a/test/compilationTests/corion/publisher.sol b/test/compilationTests/corion/publisher.sol index ec8ce33bc392..311e8d92e725 100644 --- a/test/compilationTests/corion/publisher.sol +++ b/test/compilationTests/corion/publisher.sol @@ -14,7 +14,7 @@ contract publisher is announcementTypes, module, safeMath { Transaction completed. This function is available only for moduleHandler If a transaction is carried out from or to an address which participated in the objection of an announcement, its objection purport is automatically set */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); uint256 announcementID; uint256 a; // need reverse lookup diff --git a/test/compilationTests/corion/schelling.sol b/test/compilationTests/corion/schelling.sol index bbb16e250647..2503748b8b5c 100644 --- a/test/compilationTests/corion/schelling.sol +++ b/test/compilationTests/corion/schelling.sol @@ -136,7 +136,7 @@ contract schelling is module, announcementTypes, schellingVars { module callbacks */ function replaceModule(address payable addr) external override returns (bool) { - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); require( db.replaceOwner(addr) ); super._replaceModule(addr); return true; @@ -151,7 +151,7 @@ contract schelling is module, announcementTypes, schellingVars { @value Amount @bool Was the transaction successful? */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); if ( to == address(this) ) { uint256 currentRound = getCurrentRound(); schellingVars._rounds memory round = getRound(currentRound); @@ -271,7 +271,7 @@ contract schelling is module, announcementTypes, schellingVars { @a Sort of configuration @b Value */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); if ( a == announcementType.schellingRoundBlockDelay ) { roundBlockDelay = b; } else if ( a == announcementType.schellingCheckRounds ) { interestCheckRounds = uint8(b); } else if ( a == announcementType.schellingCheckAboves ) { interestCheckAboves = uint8(b); } diff --git a/test/compilationTests/corion/token.sol b/test/compilationTests/corion/token.sol index 1656091a957e..923e313905a2 100644 --- a/test/compilationTests/corion/token.sol +++ b/test/compilationTests/corion/token.sol @@ -22,7 +22,7 @@ contract token is safeMath, module, announcementTypes { module callbacks */ function replaceModule(address payable addr) external override returns (bool success) { - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); require( db.replaceOwner(addr) ); super._replaceModule(addr); return true; @@ -254,7 +254,7 @@ contract token is safeMath, module, announcementTypes { @success Was the Function successful? */ bytes memory _data; - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); _transfer( from, to, amount, fee); emit Transfer(from, to, amount, _data); return true; @@ -352,7 +352,7 @@ contract token is safeMath, module, announcementTypes { @success Was the Function successful? */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); _processTransactionFee(owner, value); return true; } @@ -411,7 +411,7 @@ contract token is safeMath, module, announcementTypes { @success Was the Function successful? */ - require( super.isModuleHandler(msg.sender) || msg.sender == icoAddr ); + require( super.isModuleHandler(payable(msg.sender)) || msg.sender == icoAddr ); _mint(owner, value); return true; } @@ -440,7 +440,7 @@ contract token is safeMath, module, announcementTypes { @success Was the Function successful? */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); _burn(owner, value); return true; } @@ -501,7 +501,7 @@ contract token is safeMath, module, announcementTypes { @success Was the Function successful? */ - require( super.isModuleHandler(msg.sender) ); + require( super.isModuleHandler(payable(msg.sender)) ); if ( aType == announcementType.transactionFeeRate ) { transactionFeeRate = value; } else if ( aType == announcementType.transactionFeeMin ) { transactionFeeMin = value; } else if ( aType == announcementType.transactionFeeMax ) { transactionFeeMax = value; } diff --git a/test/compilationTests/gnosis/Events/ScalarEvent.sol b/test/compilationTests/gnosis/Events/ScalarEvent.sol index c2b0a90a2527..f728c65e2e3a 100644 --- a/test/compilationTests/gnosis/Events/ScalarEvent.sol +++ b/test/compilationTests/gnosis/Events/ScalarEvent.sol @@ -61,7 +61,7 @@ contract ScalarEvent is Event { convertedWinningOutcome = OUTCOME_RANGE; // Map outcome to outcome range else - convertedWinningOutcome = uint24(OUTCOME_RANGE * (outcome - lowerBound) / (upperBound - lowerBound)); + convertedWinningOutcome = uint24(uint(OUTCOME_RANGE * (outcome - lowerBound) / (upperBound - lowerBound))); uint factorShort = OUTCOME_RANGE - convertedWinningOutcome; uint factorLong = OUTCOME_RANGE - factorShort; uint shortOutcomeTokenCount = outcomeTokens[SHORT].balanceOf(msg.sender); diff --git a/test/compilationTests/gnosis/Tokens/EtherToken.sol b/test/compilationTests/gnosis/Tokens/EtherToken.sol index f98edefd2daf..d61a0b614c40 100644 --- a/test/compilationTests/gnosis/Tokens/EtherToken.sol +++ b/test/compilationTests/gnosis/Tokens/EtherToken.sol @@ -41,7 +41,7 @@ contract EtherToken is StandardToken { // Balance covers value balances[msg.sender] = balances[msg.sender].sub(value); totalTokens = totalTokens.sub(value); - msg.sender.transfer(value); + payable(msg.sender).transfer(value); emit Withdrawal(msg.sender, value); } } diff --git a/test/compilationTests/milestonetracker/RLP.sol b/test/compilationTests/milestonetracker/RLP.sol index b58bbdf90d4d..03aea6eb7dbf 100644 --- a/test/compilationTests/milestonetracker/RLP.sol +++ b/test/compilationTests/milestonetracker/RLP.sol @@ -257,7 +257,7 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return data The decoded string. - function toByte(RLPItem memory self) internal view returns (byte data) { + function toByte(RLPItem memory self) internal view returns (bytes1 data) { if(!isData(self)) revert(); (uint rStartPos, uint len) = _decode(self); @@ -267,7 +267,7 @@ library RLP { assembly { temp := byte(0, mload(rStartPos)) } - return byte(temp); + return bytes1(temp); } /// @dev Decode an RLPItem into an int. This will not work if the diff --git a/test/compilationTests/stringutils/strings.sol b/test/compilationTests/stringutils/strings.sol index 9e0518a7828d..47d8bd3f6fd3 100644 --- a/test/compilationTests/stringutils/strings.sol +++ b/test/compilationTests/stringutils/strings.sol @@ -211,7 +211,7 @@ library strings { } if (a != b) { // Mask out irrelevant bytes and check again - uint256 mask = uint256(-1); // 0xffff... + uint256 mask = type(uint256).max; // 0xffff... if(shortest < 32) { mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); } diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index cde75a844bdf..24a59db52bd8 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -43,7 +43,7 @@ namespace solidity::frontend::test namespace { static char const* registrarCode = R"DELIMITER( -pragma solidity >=0.4.0 <0.8.0; +pragma solidity >=0.7.0 <0.9.0; abstract contract NameRegister { function addr(string memory _name) public virtual view returns (address o_owner); @@ -143,12 +143,12 @@ contract GlobalRegistrar is Registrar, AuctionSystem { { if (block.timestamp < m_toRecord[_name].renewalDate) revert(); - bid(_name, msg.sender, msg.value); + bid(_name, payable(msg.sender), msg.value); } else { Record storage record = m_toRecord[_name]; if (record.owner != 0x0000000000000000000000000000000000000000) revert(); - m_toRecord[_name].owner = msg.sender; + m_toRecord[_name].owner = payable(msg.sender); emit Changed(_name); } } @@ -239,27 +239,27 @@ class AuctionRegistrarTestFramework: public SolidityExecutionFramework { callString("reserve", _name); } - u160 owner(string const& _name) + h160 owner(string const& _name) { return callStringReturnsAddress("owner", _name); } - void setAddress(string const& _name, u160 const& _address, bool _primary) + void setAddress(string const& _name, h160 const& _address, bool _primary) { callStringAddressBool("setAddress", _name, _address, _primary); } - u160 addr(string const& _name) + h160 addr(string const& _name) { return callStringReturnsAddress("addr", _name); } - string name(u160 const& _addr) + string name(h160 const& _addr) { return callAddressReturnsString("name", _addr); } - void setSubRegistrar(string const& _name, u160 const& _address) + void setSubRegistrar(string const& _name, h160 const& _address) { callStringAddress("setSubRegistrar", _name, _address); } - u160 subRegistrar(string const& _name) + h160 subRegistrar(string const& _name) { return callStringReturnsAddress("subRegistrar", _name); } @@ -271,7 +271,7 @@ class AuctionRegistrarTestFramework: public SolidityExecutionFramework { return callStringReturnsBytes32("content", _name); } - void transfer(string const& _name, u160 const& _target) + void transfer(string const& _name, h160 const& _target) { return callStringAddress("transfer", _name, _target); } @@ -304,12 +304,12 @@ BOOST_AUTO_TEST_CASE(reserve) // should not work registrar.reserve(""); - BOOST_CHECK_EQUAL(registrar.owner(""), u160(0)); + BOOST_CHECK_EQUAL(registrar.owner(""), h160{}); for (auto const& name: names) { registrar.reserve(name); - BOOST_CHECK_EQUAL(registrar.owner(name), u160(m_sender)); + BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); } } @@ -346,20 +346,20 @@ BOOST_AUTO_TEST_CASE(properties) // setting by sender works registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), sender); - registrar.setAddress(name, addr, true); - BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr)); - registrar.setSubRegistrar(name, addr + 20); - BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20)); + registrar.setAddress(name, h160(addr), true); + BOOST_CHECK_EQUAL(registrar.addr(name), h160(addr)); + registrar.setSubRegistrar(name, h160(addr + 20)); + BOOST_CHECK_EQUAL(registrar.subRegistrar(name), h160(addr + 20)); registrar.setContent(name, h256(u256(addr + 90))); BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90))); // but not by someone else m_sender = account(count - 1); BOOST_CHECK_EQUAL(registrar.owner(name), sender); - registrar.setAddress(name, addr + 1, true); - BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr)); - registrar.setSubRegistrar(name, addr + 20 + 1); - BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20)); + registrar.setAddress(name, h160(addr + 1), true); + BOOST_CHECK_EQUAL(registrar.addr(name), h160(addr)); + registrar.setSubRegistrar(name, h160(addr + 20 + 1)); + BOOST_CHECK_EQUAL(registrar.subRegistrar(name), h160(addr + 20)); registrar.setContent(name, h256(u256(addr + 90 + 1))); BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90))); count++; @@ -373,8 +373,8 @@ BOOST_AUTO_TEST_CASE(transfer) RegistrarInterface registrar(*this); registrar.reserve(name); registrar.setContent(name, h256(u256(123))); - registrar.transfer(name, u160(555)); - BOOST_CHECK_EQUAL(registrar.owner(name), u160(555)); + registrar.transfer(name, h160(555)); + BOOST_CHECK_EQUAL(registrar.owner(name), h160(555)); BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(123))); } @@ -386,9 +386,9 @@ BOOST_AUTO_TEST_CASE(disown) RegistrarInterface registrar(*this); registrar.reserve(name); registrar.setContent(name, h256(u256(123))); - registrar.setAddress(name, u160(124), true); - registrar.setSubRegistrar(name, u160(125)); - BOOST_CHECK_EQUAL(registrar.name(u160(124)), name); + registrar.setAddress(name, h160(124), true); + registrar.setSubRegistrar(name, h160(125)); + BOOST_CHECK_EQUAL(registrar.name(h160(124)), name); // someone else tries disowning sendEther(account(1), u256(10) * trx); @@ -398,11 +398,11 @@ BOOST_AUTO_TEST_CASE(disown) m_sender = account(0); registrar.disown(name); - BOOST_CHECK_EQUAL(registrar.owner(name), 0); - BOOST_CHECK_EQUAL(registrar.addr(name), 0); - BOOST_CHECK_EQUAL(registrar.subRegistrar(name), 0); + BOOST_CHECK_EQUAL(registrar.owner(name), h160()); + BOOST_CHECK_EQUAL(registrar.addr(name), h160()); + BOOST_CHECK_EQUAL(registrar.subRegistrar(name), h160()); BOOST_CHECK_EQUAL(registrar.content(name), h256()); - BOOST_CHECK_EQUAL(registrar.name(u160(124)), ""); + BOOST_CHECK_EQUAL(registrar.name(h160(124)), ""); } BOOST_AUTO_TEST_CASE(auction_simple) @@ -414,10 +414,10 @@ BOOST_AUTO_TEST_CASE(auction_simple) // initiate auction registrar.setNextValue(8); registrar.reserve(name); - BOOST_CHECK_EQUAL(registrar.owner(name), 0); + BOOST_CHECK_EQUAL(registrar.owner(name), h160()); // "wait" until auction end - m_evmHost->tx_context.block_timestamp += m_biddingTime + 10; + m_evmcHost->tx_context.block_timestamp += m_biddingTime + 10; // trigger auction again registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); @@ -429,27 +429,27 @@ BOOST_AUTO_TEST_CASE(auction_bidding) string name = "x"; unsigned startTime = 0x776347e2; - m_evmHost->tx_context.block_timestamp = startTime; + m_evmcHost->tx_context.block_timestamp = startTime; RegistrarInterface registrar(*this); // initiate auction registrar.setNextValue(8); registrar.reserve(name); - BOOST_CHECK_EQUAL(registrar.owner(name), 0); + BOOST_CHECK_EQUAL(registrar.owner(name), h160()); // overbid self - m_evmHost->tx_context.block_timestamp = startTime + m_biddingTime - 10; + m_evmcHost->tx_context.block_timestamp = startTime + m_biddingTime - 10; registrar.setNextValue(12); registrar.reserve(name); // another bid by someone else sendEther(account(1), 10 * trx); m_sender = account(1); - m_evmHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50; + m_evmcHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50; registrar.setNextValue(13); registrar.reserve(name); - BOOST_CHECK_EQUAL(registrar.owner(name), 0); + BOOST_CHECK_EQUAL(registrar.owner(name), h160()); // end auction by first bidder (which is not highest) trying to overbid again (too late) m_sender = account(0); - m_evmHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime; + m_evmcHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime; registrar.setNextValue(20); registrar.reserve(name); BOOST_CHECK_EQUAL(registrar.owner(name), account(1)); diff --git a/test/contracts/ContractInterface.h b/test/contracts/ContractInterface.h index 1bc1d9e39a74..e50227670e01 100644 --- a/test/contracts/ContractInterface.h +++ b/test/contracts/ContractInterface.h @@ -47,12 +47,12 @@ class ContractInterface BOOST_CHECK(call(_name + "(string)", u256(0x20), _arg.length(), _arg).empty()); } - void callStringAddress(std::string const& _name, std::string const& _arg1, u160 const& _arg2) + void callStringAddress(std::string const& _name, std::string const& _arg1, util::h160 const& _arg2) { BOOST_CHECK(call(_name + "(string,address)", u256(0x40), _arg2, _arg1.length(), _arg1).empty()); } - void callStringAddressBool(std::string const& _name, std::string const& _arg1, u160 const& _arg2, bool _arg3) + void callStringAddressBool(std::string const& _name, std::string const& _arg1, util::h160 const& _arg2, bool _arg3) { BOOST_CHECK(call(_name + "(string,address,bool)", u256(0x60), _arg2, _arg3, _arg1.length(), _arg1).empty()); } @@ -62,15 +62,16 @@ class ContractInterface BOOST_CHECK(call(_name + "(string,bytes32)", u256(0x40), _arg2, _arg1.length(), _arg1).empty()); } - u160 callStringReturnsAddress(std::string const& _name, std::string const& _arg) + util::h160 callStringReturnsAddress(std::string const& _name, std::string const& _arg) { bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg); BOOST_REQUIRE(ret.size() == 0x20); BOOST_CHECK(std::count(ret.begin(), ret.begin() + 12, 0) == 12); - return u160(u256(util::h256(ret))); + bytes const addr{ret.begin() + 12, ret.end()}; + return util::h160(addr); } - std::string callAddressReturnsString(std::string const& _name, u160 const& _arg) + std::string callAddressReturnsString(std::string const& _name, util::h160 const& _arg) { bytesConstRef const ret(&call(_name + "(address)", _arg)); BOOST_REQUIRE(ret.size() >= 0x40); diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 7585362c0c73..2a3613f7ab57 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -55,7 +55,7 @@ static char const* registrarCode = R"DELIMITER( // @authors: // Gav Wood -pragma solidity >=0.4.0 <0.8.0; +pragma solidity >=0.4.0 <0.9.0; abstract contract Registrar { event Changed(string indexed name); @@ -160,11 +160,11 @@ BOOST_AUTO_TEST_CASE(reserve) deployRegistrar(); string name[] = {"abc", "def", "ghi"}; BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name[0])) == encodeArgs()); - BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[0])) == encodeArgs(h256(account(0), h256::AlignRight))); + BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[0])) == encodeArgs(account(0))); BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee + 1, encodeDyn(name[1])) == encodeArgs()); - BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[1])) == encodeArgs(h256(account(0), h256::AlignRight))); + BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[1])) == encodeArgs(account(0))); BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee - 1, encodeDyn(name[2])) == encodeArgs()); - BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[2])) == encodeArgs(h256{})); + BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name[2])) == encodeArgs(h160{})); } BOOST_AUTO_TEST_CASE(double_reserve) @@ -173,12 +173,12 @@ BOOST_AUTO_TEST_CASE(double_reserve) deployRegistrar(); string name = "abc"; BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); - BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(account(0), h256::AlignRight))); + BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(account(0))); sendEther(account(1), 100 * trx); m_sender = account(1); BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); - BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(account(0), h256::AlignRight))); + BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(account(0))); } BOOST_AUTO_TEST_CASE(properties) @@ -194,10 +194,10 @@ BOOST_AUTO_TEST_CASE(properties) m_sender = account(0); sendEther(account(count), 100 * trx); m_sender = account(count); - Address owner = m_sender; + h160 owner = m_sender; // setting by sender works BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); - BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(owner, h256::AlignRight))); + BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(owner)); BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), u256(addr), u256(name.length()), name) == encodeArgs()); BOOST_CHECK(callContractFunction("addr(string)", encodeDyn(name)) == encodeArgs(addr)); BOOST_CHECK(callContractFunction("setSubRegistrar(string,address)", u256(0x40), addr + 20, u256(name.length()), name) == encodeArgs()); @@ -209,7 +209,7 @@ BOOST_AUTO_TEST_CASE(properties) m_sender = account(0); sendEther(account(count), 100 * trx); m_sender = account(count); - BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(h256(owner, h256::AlignRight))); + BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(owner)); BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), addr + 1, u256(name.length()), name) == encodeArgs()); BOOST_CHECK(callContractFunction("addr(string)", encodeDyn(name)) == encodeArgs(addr)); BOOST_CHECK(callContractFunction("setSubRegistrar(string,address)", u256(0x40), addr + 20 + 1, u256(name.length()), name) == encodeArgs()); @@ -225,10 +225,10 @@ BOOST_AUTO_TEST_CASE(transfer) deployRegistrar(); string name = "abc"; BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); - BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), h256(account(0), h256::AlignRight), u256(name.length()), name) == encodeArgs()); + BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), account(0), u256(name.length()), name) == encodeArgs()); BOOST_CHECK(callContractFunction("transfer(string,address)", u256(0x40), u256(555), u256(name.length()), name) == encodeArgs()); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(u256(555))); - BOOST_CHECK(callContractFunction("content(string)", encodeDyn(name)) == encodeArgs(h256(account(0), h256::AlignRight))); + BOOST_CHECK(callContractFunction("content(string)", encodeDyn(name)) == encodeArgs(account(0))); } BOOST_AUTO_TEST_CASE(disown) @@ -236,13 +236,13 @@ BOOST_AUTO_TEST_CASE(disown) deployRegistrar(); string name = "abc"; BOOST_REQUIRE(callContractFunctionWithValue("reserve(string)", m_fee, encodeDyn(name)) == encodeArgs()); - BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), h256(account(0), h256::AlignRight), u256(name.length()), name) == encodeArgs()); + BOOST_CHECK(callContractFunction("setContent(string,bytes32)", u256(0x40), account(0), u256(name.length()), name) == encodeArgs()); BOOST_CHECK(callContractFunction("setAddr(string,address)", u256(0x40), u256(124), u256(name.length()), name) == encodeArgs()); BOOST_CHECK(callContractFunction("setSubRegistrar(string,address)", u256(0x40), u256(125), u256(name.length()), name) == encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(Address(0x124)), 0); + BOOST_CHECK_EQUAL(balanceAt(h160(0x124)), 0); BOOST_CHECK(callContractFunction("disown(string,address)", u256(0x40), u256(0x124), name.size(), name) == encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(Address(0x124)), m_fee); + BOOST_CHECK_EQUAL(balanceAt(h160(0x124)), m_fee); BOOST_CHECK(callContractFunction("owner(string)", encodeDyn(name)) == encodeArgs(u256(0))); BOOST_CHECK(callContractFunction("content(string)", encodeDyn(name)) == encodeArgs(u256(0))); diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 336aad0124db..b1f353d12514 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -56,7 +56,7 @@ static char const* walletCode = R"DELIMITER( // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the // interior is executed. -pragma solidity >=0.4.0 <0.8.0; +pragma solidity >=0.4.0 <0.9.0; contract multiowned { @@ -103,19 +103,19 @@ contract multiowned { // as well as the selection of addresses capable of confirming them. constructor(address[] memory _owners, uint _required) { m_numOwners = _owners.length + 1; - m_owners[1] = uint(msg.sender); - m_ownerIndex[uint(msg.sender)] = 1; + m_owners[1] = uint160(msg.sender); + m_ownerIndex[uint160(msg.sender)] = 1; for (uint i = 0; i < _owners.length; ++i) { - m_owners[2 + i] = uint(_owners[i]); - m_ownerIndex[uint(_owners[i])] = 2 + i; + m_owners[2 + i] = uint160(_owners[i]); + m_ownerIndex[uint160(_owners[i])] = 2 + i; } m_required = _required; } // Revokes a prior confirmation of the given operation function revoke(bytes32 _operation) external { - uint ownerIndex = m_ownerIndex[uint(msg.sender)]; + uint ownerIndex = m_ownerIndex[uint160(msg.sender)]; // make sure they're an owner if (ownerIndex == 0) return; uint ownerIndexBit = 2**ownerIndex; @@ -130,13 +130,13 @@ contract multiowned { // Replaces an owner `_from` with another `_to`. function changeOwner(address _from, address _to) onlymanyowners(keccak256(msg.data)) public virtual { if (isOwner(_to)) return; - uint ownerIndex = m_ownerIndex[uint(_from)]; + uint ownerIndex = m_ownerIndex[uint160(_from)]; if (ownerIndex == 0) return; clearPending(); - m_owners[ownerIndex] = uint(_to); - m_ownerIndex[uint(_from)] = 0; - m_ownerIndex[uint(_to)] = ownerIndex; + m_owners[ownerIndex] = uint160(_to); + m_ownerIndex[uint160(_from)] = 0; + m_ownerIndex[uint160(_to)] = ownerIndex; emit OwnerChanged(_from, _to); } @@ -149,18 +149,18 @@ contract multiowned { if (m_numOwners >= c_maxOwners) return; m_numOwners++; - m_owners[m_numOwners] = uint(_owner); - m_ownerIndex[uint(_owner)] = m_numOwners; + m_owners[m_numOwners] = uint160(_owner); + m_ownerIndex[uint160(_owner)] = m_numOwners; emit OwnerAdded(_owner); } function removeOwner(address _owner) onlymanyowners(keccak256(msg.data)) external { - uint ownerIndex = m_ownerIndex[uint(_owner)]; + uint ownerIndex = m_ownerIndex[uint160(_owner)]; if (ownerIndex == 0) return; if (m_required > m_numOwners - 1) return; m_owners[ownerIndex] = 0; - m_ownerIndex[uint(_owner)] = 0; + m_ownerIndex[uint160(_owner)] = 0; clearPending(); reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot emit OwnerRemoved(_owner); @@ -174,12 +174,12 @@ contract multiowned { } function isOwner(address _addr) public returns (bool) { - return m_ownerIndex[uint(_addr)] > 0; + return m_ownerIndex[uint160(_addr)] > 0; } function hasConfirmed(bytes32 _operation, address _owner) public view returns (bool) { PendingState storage pending = m_pending[_operation]; - uint ownerIndex = m_ownerIndex[uint(_owner)]; + uint ownerIndex = m_ownerIndex[uint160(_owner)]; // make sure they're an owner if (ownerIndex == 0) return false; @@ -197,7 +197,7 @@ contract multiowned { function confirmAndCheck(bytes32 _operation) internal returns (bool) { // determine what index the present sender is: - uint ownerIndex = m_ownerIndex[uint(msg.sender)]; + uint ownerIndex = m_ownerIndex[uint160(msg.sender)]; // make sure they're an owner if (ownerIndex == 0) return false; @@ -445,7 +445,7 @@ class WalletTestFramework: public SolidityExecutionFramework protected: void deployWallet( u256 const& _value = 0, - vector const& _owners = vector{}, + vector const& _owners = vector{}, u256 _required = 1, u256 _dailyLimit = 0 ) @@ -467,42 +467,42 @@ BOOST_FIXTURE_TEST_SUITE(SolidityWallet, WalletTestFramework) BOOST_AUTO_TEST_CASE(creation) { deployWallet(200); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true)); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; + BOOST_REQUIRE(callContractFunction("isOwner(address)", m_sender) == encodeArgs(true)); + bool v2 = !solidity::test::CommonOptions::get().useABIEncoderV1; BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(~0)) == (v2 ? encodeArgs() : encodeArgs(false))); } BOOST_AUTO_TEST_CASE(add_owners) { deployWallet(200); - Address originalOwner = m_sender; - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs(true)); + h160 originalOwner = m_sender; + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(1)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", account(1)) == encodeArgs(true)); // now let the new owner add someone sendEther(account(1), 10 * trx); m_sender = account(1); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x13)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x13)) == encodeArgs(true)); // and check that a non-owner cannot add a new owner m_sender = account(0); sendEther(account(2), 10 * trx); m_sender = account(2); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x20)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x20)) == encodeArgs(false)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x20)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x20)) == encodeArgs(false)); // finally check that all the owners are there - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(originalOwner, h256::AlignRight)) == encodeArgs(true)); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs(true)); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("isOwner(address)", originalOwner) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("isOwner(address)", account(1)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x13)) == encodeArgs(true)); } BOOST_AUTO_TEST_CASE(change_owners) { deployWallet(200); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true)); - BOOST_REQUIRE(callContractFunction("changeOwner(address,address)", h256(0x12), h256(0x13)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(false)); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x12)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x12)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("changeOwner(address,address)", h160(0x12), h160(0x13)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x12)) == encodeArgs(false)); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x13)) == encodeArgs(true)); } BOOST_AUTO_TEST_CASE(remove_owner) @@ -511,43 +511,43 @@ BOOST_AUTO_TEST_CASE(remove_owner) // add 10 owners for (unsigned i = 0; i < 10; ++i) { - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x12 + i)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x12 + i)) == encodeArgs(true)); } // check they are there again for (unsigned i = 0; i < 10; ++i) - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x12 + i)) == encodeArgs(true)); // remove the odd owners for (unsigned i = 0; i < 10; ++i) if (i % 2 == 1) - BOOST_REQUIRE(callContractFunction("removeOwner(address)", h256(0x12 + i)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("removeOwner(address)", h160(0x12 + i)) == encodeArgs()); // check the result for (unsigned i = 0; i < 10; ++i) - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(i % 2 == 0)); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x12 + i)) == encodeArgs(i % 2 == 0)); // add them again for (unsigned i = 0; i < 10; ++i) if (i % 2 == 1) - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x12 + i)) == encodeArgs()); // check everyone is there for (unsigned i = 0; i < 10; ++i) - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x12 + i)) == encodeArgs(true)); } BOOST_AUTO_TEST_CASE(initial_owners) { - vector owners{ - u256("0x00000000000000000000000042c56279432962a17176998a4747d1b4d6ed4367"), - u256("0x000000000000000000000000d4d4669f5ba9f4c27d38ef02a358c339b5560c47"), - u256("0x000000000000000000000000e6716f9544a56c530d868e4bfbacb172315bdead"), - u256("0x000000000000000000000000775e18be7a50a0abb8a4e82b1bd697d79f31fe04"), - u256("0x000000000000000000000000f4dd5c3794f1fd0cdc0327a83aa472609c806e99"), - u256("0x0000000000000000000000004c9113886af165b2de069d6e99430647e94a9fff"), - u256("0x0000000000000000000000003fb1cd2cd96c6d5c0b5eb3322d807b34482481d4") + vector owners{ + h160("0x42c56279432962a17176998a4747d1b4d6ed4367"), + h160("0xd4d4669f5ba9f4c27d38ef02a358c339b5560c47"), + h160("0xe6716f9544a56c530d868e4bfbacb172315bdead"), + h160("0x775e18be7a50a0abb8a4e82b1bd697d79f31fe04"), + h160("0xf4dd5c3794f1fd0cdc0327a83aa472609c806e99"), + h160("0x4c9113886af165b2de069d6e99430647e94a9fff"), + h160("0x3fb1cd2cd96c6d5c0b5eb3322d807b34482481d4") }; deployWallet(0, owners, 4, 2); BOOST_CHECK(callContractFunction("m_numOwners()") == encodeArgs(u256(8))); - BOOST_CHECK(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true)); - for (u256 const& owner: owners) + BOOST_CHECK(callContractFunction("isOwner(address)", m_sender) == encodeArgs(true)); + for (h160 const& owner: owners) { BOOST_CHECK(callContractFunction("isOwner(address)", owner) == encodeArgs(true)); } @@ -556,17 +556,17 @@ BOOST_AUTO_TEST_CASE(initial_owners) BOOST_AUTO_TEST_CASE(multisig_value_transfer) { deployWallet(200); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(2), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(3), h256::AlignRight)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(1)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(2)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(3)) == encodeArgs()); // 4 owners, set required to 3 BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); - Address destination = Address("0x5c6d6026d3fb35cd7175fd0054ae8df50d8f8b41"); + h160 destination = h160("0x5c6d6026d3fb35cd7175fd0054ae8df50d8f8b41"); BOOST_CHECK_EQUAL(balanceAt(destination), 0); m_sender = account(0); sendEther(account(1), 10 * trx); m_sender = account(1); - auto ophash = callContractFunction("execute(address,uint256,bytes)", h256(destination, h256::AlignRight), 100, 0x60, 0x00); + auto ophash = callContractFunction("execute(address,uint256,bytes)", destination, 100, 0x60, 0x00); BOOST_CHECK_EQUAL(balanceAt(destination), 0); m_sender = account(0); sendEther(account(2), 10 * trx); @@ -584,52 +584,52 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer) BOOST_AUTO_TEST_CASE(revoke_addOwner) { deployWallet(); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(2), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(3), h256::AlignRight)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(1)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(2)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(3)) == encodeArgs()); // 4 owners, set required to 3 BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); // add a new owner - Address deployer = m_sender; + h160 deployer = m_sender; h256 opHash = util::keccak256(FixedHash<4>(util::keccak256("addOwner(address)")).asBytes() + h256(0x33).asBytes()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x33)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x33)) == encodeArgs(false)); m_sender = account(0); sendEther(account(1), 10 * trx); m_sender = account(1); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x33)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x33)) == encodeArgs(false)); // revoke one confirmation m_sender = deployer; BOOST_REQUIRE(callContractFunction("revoke(bytes32)", opHash) == encodeArgs()); m_sender = account(0); sendEther(account(2), 10 * trx); m_sender = account(2); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(false)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x33)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x33)) == encodeArgs(false)); m_sender = account(0); sendEther(account(3), 10 * trx); m_sender = account(3); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x33)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x33)) == encodeArgs(true)); + BOOST_REQUIRE(callContractFunction("addOwner(address)", h160(0x33)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("isOwner(address)", h160(0x33)) == encodeArgs(true)); } BOOST_AUTO_TEST_CASE(revoke_transaction) { deployWallet(200); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(2), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(3), h256::AlignRight)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(1)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(2)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(3)) == encodeArgs()); // 4 owners, set required to 3 BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); // create a transaction - Address deployer = m_sender; - Address destination = Address("0x5c6d6026d3fb35cd7175fd0054ae8df50d8f8b41"); + h160 deployer = m_sender; + h160 destination = h160("0x5c6d6026d3fb35cd7175fd0054ae8df50d8f8b41"); BOOST_CHECK_EQUAL(balanceAt(destination), 0); m_sender = account(0); sendEther(account(1), 10 * trx); m_sender = account(1); - auto opHash = callContractFunction("execute(address,uint256,bytes)", h256(destination, h256::AlignRight), 100, 0x60, 0x00); + auto opHash = callContractFunction("execute(address,uint256,bytes)", destination, 100, 0x60, 0x00); BOOST_CHECK_EQUAL(balanceAt(destination), 0); m_sender = account(0); sendEther(account(2), 10 * trx); @@ -655,21 +655,21 @@ BOOST_AUTO_TEST_CASE(daylimit) { deployWallet(200); BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(0))); - BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(100)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", u256(100)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(100))); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(1), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(2), h256::AlignRight)) == encodeArgs()); - BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(account(3), h256::AlignRight)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(1)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(2)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("addOwner(address)", account(3)) == encodeArgs()); // 4 owners, set required to 3 BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); // try to send tx over daylimit - Address destination = Address("0x5c6d6026d3fb35cd7175fd0054ae8df50d8f8b41"); + h160 destination = h160("0x5c6d6026d3fb35cd7175fd0054ae8df50d8f8b41"); BOOST_CHECK_EQUAL(balanceAt(destination), 0); sendEther(account(1), 10 * trx); m_sender = account(1); BOOST_REQUIRE( - callContractFunction("execute(address,uint256,bytes)", h256(destination, h256::AlignRight), 150, 0x60, 0x00) != + callContractFunction("execute(address,uint256,bytes)", destination, 150, 0x60, 0x00) != encodeArgs(u256(0)) ); BOOST_CHECK_EQUAL(balanceAt(destination), 0); @@ -678,7 +678,7 @@ BOOST_AUTO_TEST_CASE(daylimit) sendEther(account(4), 10 * trx); m_sender = account(4); BOOST_REQUIRE( - callContractFunction("execute(address,uint256,bytes)", h256(destination, h256::AlignRight), 90, 0x60, 0x00) == + callContractFunction("execute(address,uint256,bytes)", destination, 90, 0x60, 0x00) == encodeArgs(u256(0)) ); BOOST_CHECK_EQUAL(balanceAt(destination), 0); @@ -686,7 +686,7 @@ BOOST_AUTO_TEST_CASE(daylimit) m_sender = account(0); sendEther(account(1), 10 * trx); BOOST_REQUIRE( - callContractFunction("execute(address,uint256,bytes)", h256(destination, h256::AlignRight), 90, 0x60, 0x00) == + callContractFunction("execute(address,uint256,bytes)", destination, 90, 0x60, 0x00) == encodeArgs(u256(0)) ); BOOST_CHECK_EQUAL(balanceAt(destination), 90); @@ -696,7 +696,7 @@ BOOST_AUTO_TEST_CASE(daylimit_constructor) { deployWallet(200, {}, 1, 20); BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(20))); - BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(30)) == encodeArgs()); + BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", u256(30)) == encodeArgs()); BOOST_REQUIRE(callContractFunction("m_dailyLimit()") == encodeArgs(u256(30))); } diff --git a/test/evmc/CMakeLists.txt b/test/evmc/CMakeLists.txt index bd64e862548a..b49b85a29a25 100644 --- a/test/evmc/CMakeLists.txt +++ b/test/evmc/CMakeLists.txt @@ -19,4 +19,4 @@ target_sources(evmc INTERFACE ${PROJECT_SOURCE_DIR}/test/evmc/utils.h ) target_include_directories(evmc INTERFACE ${PROJECT_SOURCE_DIR}/test/) -target_link_libraries(evmc INTERFACE evmc_loader) \ No newline at end of file +target_link_libraries(evmc INTERFACE evmc_loader) diff --git a/test/externalTests.sh b/test/externalTests.sh index d7ba829a728a..28af6f835a45 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -42,9 +42,11 @@ source test/externalTests/common.sh printTask "Running external tests..." -$REPO_ROOT/externalTests/zeppelin.sh "$SOLJSON" -$REPO_ROOT/externalTests/gnosis.sh "$SOLJSON" -$REPO_ROOT/externalTests/colony.sh "$SOLJSON" +"$REPO_ROOT/externalTests/zeppelin.sh" "$SOLJSON" +"$REPO_ROOT/externalTests/gnosis.sh" "$SOLJSON" +"$REPO_ROOT/externalTests/gnosis-v2.sh" "$SOLJSON" +"$REPO_ROOT/externalTests/colony.sh" "$SOLJSON" +"$REPO_ROOT/externalTests/ens.sh" "$SOLJSON" # Disabled temporarily as it needs to be updated to latest Truffle first. #test_truffle Gnosis https://github.com/axic/pm-contracts.git solidity-050 diff --git a/test/externalTests/README.md b/test/externalTests/README.md new file mode 100644 index 000000000000..a6c160846a2a --- /dev/null +++ b/test/externalTests/README.md @@ -0,0 +1,92 @@ +## Solidity external tests +This directory contains scripts for compiling some of the popular open-source projects using the +current version of the compiler and running their test suites. + +Since projects often do not use the latest compiler, we keep a fork of each of these projects +at https://github.com/solidity-external-tests/. If changes are needed to make a project work with the +latest version of the compiler, they are maintained as a branch on top of the upstream master branch. +This is especially important for testing our `breaking` branch because we can not realistically expect +external projects to be instantly compatible with a compiler version that has not been released yet. +Applying necessary changes ourselves gives us confidence that breaking changes are sane and that +these projects *can* be upgraded at all. + +### Recommended workflow + +#### Adding a new external project +1. Create a fork of the upstream repository in https://github.com/solidity-external-tests/. If the + project consists of several repositories, fork them all. +2. Remove all the branches except for main one (`master`, `develop`, `main`, etc). This branch is + going to be always kept up to date with the upstream repository and should not contain any extra + commits. + - If the project is not up to date with the latest compiler version but has a branch that is, + try to use that branch instead. +3. Create a new branch named after the main branch and the compiler version from our `develop` + branch. E.g. if the latest Solidity version is 0.7.5 and the main branch of the external project + is called `master`, create `master_070`. This is where we will be adding our own commits. +4. Create a script for compiling/testing the project and put it in `test/externalTests/` in the + Solidity repository. + - The script should apply workarounds necessary to make the project actually use the compiler + binary it receives as a parameter and possibly add generic workarounds that should + work across different versions of the upstream project. + - Very specific workarounds that may easily break with every upstream change are better done as + commits in the newly added branch in the fork instead. +5. List the script in `test/externalTests.sh`. +6. Add the script to CircleCI configuration. Make sure to add both a compilation-only run and one that + also executes the test suite. If the latter takes a significant amount of time (say, more than + 15 minutes) make it run nightly rather than on every PR. +7. Make sure that tests pass both on `develop` and on `breaking`. If the compiler from `breaking` + branch will not work without additional changes, add another branch, called after it in turn, + and add necessary workarounds there. Continuing the example above, the new branch would be + called `master_080` and should be rebased on top of `master_070`. + - The fewer commits in these branches, the better. Ideally, any changes needed to make the compiler + work should be submitted upstream and our branches should just be tracking the main upstream + branch without any extra commits. + +#### Updating external projects for a PR that introduces breaking changes in the compiler +If a PR to our `breaking` branch introduces changes that will make an external project no longer +compile or pass its tests, the fork needs to be modified: +- If a branch specific to the compiler version from `breaking` does not exist yet: + 1. Create the branch. It should be based on the version-specific branch used on `develop`. + 2. Make your PR modify the project script in `test/externalScripts/` to use the new branch. + 3. You are free to add any changes you need in the new branch since it will not interfere with + tests on `breaking`. + 4. Work on your PR until it is approved and merged into `breaking`. +- If the branch already exists and our CI depends on it: + 1. If the external project after your changes can still work with `breaking` even without your PR or + if you know that the PR is straightforward and will be merged immediately without interfering + with tests on `breaking` for a significant amount of time, you can just push your modifications + to the branch directly and skip straight to steps 4. and 6. + 2. Create a PR in the fork, targeting the existing version-specific branch. + 3. In your PR to `breaking`, modify the corresponding script in `test/externalScripts/` to + use the branch from your PR in the fork. + 4. Work on your PR until it is approved and ready to merge. + 5. Merge the PR in the fork. + 6. Discard your changes to the script and merge your PR into `breaking`. + +#### Pulling upstream changes into a fork +1. Pull changes directly into the main branch in the fork. This should be straightforward thanks to + it not containing any of our customizations. +2. If the project has been updated to a newer Solidity version, abandon the current version-specific + branch used on `develop` (but do not delete it) and create a new one corresponding to the newer + version. Then update project script in `test/externalTests/` to use the new branch. E.g. if `develop` uses + `master_050` and the project has been updated to use Solidity 0.7.3, create `master_070`. +3. Otherwise, rebase the current version-specific branch on the main branch of the fork. This may require + tweaking some of the commits to apply our fixes in new places. +4. If we have a separate branch for `breaking`, rebase it on top of the one used on `develop`. + +The above is the workflow to use when the update is straightforward and looks safe. In that case it is +fine to just modify the branches directly. If this is not the case, it is recommended to first perform the +operation on copies of these version-specific branches and test them by creating PRs on `develop` and +`breaking` to see if tests pass. The PRs should just modify project scripts in `test/externalScripts/` +to use the updated copies of the branches and can be discarded aferwards without being merged. + +#### Changes needed after a breaking release of the compiler +When a non-backwards-compatible version becomes the most recent release, `breaking` branch +gets merged into `develop` which automatically results in a switch to the newer version-specific +branches if they exist. If no changes on our part were necessary, it is completely fine to keep using +e.g. the `master_060` of an external project in in Solidity 0.8.x. + +Since each project is handled separately, this approach may result in a mix of version-specific branches +between different external projects. For example, in one project we could could have `master_050` on +both `develop` and `breaking` and in another `breaking` could use `master_080` while `develop` still +uses `master_060`. diff --git a/test/externalTests/colony.sh b/test/externalTests/colony.sh index 6a246b2b9d7b..c807f9022b8d 100755 --- a/test/externalTests/colony.sh +++ b/test/externalTests/colony.sh @@ -31,18 +31,17 @@ function test_fn { yarn run test:contracts; } function colony_test { OPTIMIZER_LEVEL=3 - FORCE_ABIv2=false CONFIG="truffle.js" - truffle_setup https://github.com/solidity-external-tests/colonyNetwork.git develop_070 - run_install install_fn + truffle_setup "$SOLJSON" https://github.com/solidity-external-tests/colonyNetwork.git develop_080 + run_install "$SOLJSON" install_fn cd lib rm -Rf dappsys - git clone https://github.com/solidity-external-tests/dappsys-monolithic.git -b master_060 dappsys + git clone https://github.com/solidity-external-tests/dappsys-monolithic.git -b master_080 dappsys cd .. - truffle_run_test compile_fn test_fn + truffle_run_test "$SOLJSON" compile_fn test_fn } external_test ColonyNetworks colony_test diff --git a/test/externalTests/common.sh b/test/externalTests/common.sh index fe25274434e3..804d9a529226 100644 --- a/test/externalTests/common.sh +++ b/test/externalTests/common.sh @@ -40,9 +40,10 @@ function verify_version_input function setup { - local branch="$1" + local soljson="$1" + local branch="$2" - setup_solcjs "$DIR" "$SOLJSON" "$branch" "solc" + setup_solcjs "$DIR" "$soljson" "$branch" "solc" cd solc } @@ -80,22 +81,23 @@ function download_project printLog "Cloning $branch of $repo..." git clone --depth 1 "$repo" -b "$branch" "$dir/ext" cd ext - echo "Current commit hash: `git rev-parse HEAD`" + echo "Current commit hash: $(git rev-parse HEAD)" } function force_truffle_version { - local repo="$1" + local version="$1" - sed -i 's/"truffle":\s*".*"/"truffle": "^5.0.42"/g' package.json + sed -i 's/"truffle":\s*".*"/"truffle": "'"$version"'"/g' package.json } function truffle_setup { - local repo="$1" - local branch="$2" + local soljson="$1" + local repo="$2" + local branch="$3" - setup_solcjs "$DIR" "$SOLJSON" "master" "solc" + setup_solcjs "$DIR" "$soljson" "master" "solc" download_project "$repo" "$branch" "$DIR" } @@ -104,32 +106,13 @@ function replace_version_pragmas # Replace fixed-version pragmas (part of Consensys best practice). # Include all directories to also cover node dependencies. printLog "Replacing fixed-version pragmas..." - find . test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity [\^0-9\.]*/pragma solidity >=0.0/' + find . test -name '*.sol' -type f -print0 | xargs -0 sed -i -E -e 's/pragma solidity [^;]+;/pragma solidity >=0.0;/' } -function replace_libsolc_call +function force_truffle_solc_modules { - # Change "compileStandard" to "compile" (needed for pre-5.x Truffle) - printLog "Replacing libsolc compile call in Truffle..." - sed -i s/solc.compileStandard/solc.compile/ "node_modules/truffle/build/cli.bundled.js" -} + local soljson="$1" -function find_truffle_config -{ - local config_file="truffle.js" - local alt_config_file="truffle-config.js" - - if [ ! -f "$config_file" ] && [ ! -f "$alt_config_file" ]; then - printError "No matching Truffle config found." - fi - if [ ! -f "$config_file" ]; then - config_file=alt_config_file - fi - echo "$config_file" -} - -function force_solc_truffle_modules -{ # Replace solc package by v0.5.0 and then overwrite with current version. printLog "Forcing solc version for all Truffle modules..." for d in node_modules node_modules/truffle/node_modules @@ -139,57 +122,34 @@ function force_solc_truffle_modules cd $d rm -rf solc git clone --depth 1 -b master https://github.com/ethereum/solc-js.git solc - cp "$1" solc/soljson.js + cp "$soljson" solc/soljson.js + + cd solc + npm install fi ) done } -function force_solc -{ - local config_file="$1" - local dir="$2" - local soljson="$3" - - force_solc_truffle_modules "$soljson" - - printLog "Forcing solc version..." - cat >> "$config_file" <> "$config_file" - echo "module.exports['compilers']['solc']['settings'] = { optimizer: $settings, evmVersion: \"$evmVersion\" };" >> "$config_file" -} - -function force_abi_v2 -{ - # Add "pragma experimental ABIEncoderV2" to all files. - printLog "Forcibly enabling ABIEncoderV2..." - find contracts test -name '*.sol' -type f -print0 | \ - while IFS= read -r -d '' file - do - # Only add the pragma if it is not already there. - if grep -q -v 'pragma experimental ABIEncoderV2' "$file"; then - sed -i -e '1 i pragma experimental ABIEncoderV2;' "$file" - fi - done + echo "module.exports['compilers'] = $(truffle_compiler_settings "$solc_path" "$level" "$evm_version");" >> "$config_file" } function verify_compiler_version @@ -207,11 +167,13 @@ function clean function run_install { - local init_fn="$1" + local soljson="$1" + local init_fn="$2" printLog "Running install function..." replace_version_pragmas - force_solc "$CONFIG" "$DIR" "$SOLJSON" + force_truffle_solc_modules "$soljson" + force_truffle_compiler_settings "$CONFIG" "${DIR}/solc" "$OPTIMIZER_LEVEL" istanbul $init_fn } @@ -230,37 +192,49 @@ function run_test $test_fn } +function optimizer_settings_for_level { + local level="$1" + + case "$level" in + 1) echo "{enabled: false}" ;; + 2) echo "{enabled: true}" ;; + 3) echo "{enabled: true, details: {yul: true}}" ;; + *) + printError "Optimizer level not found. Please define OPTIMIZER_LEVEL=[1, 2, 3]" + exit 1 + ;; + esac +} + +function truffle_compiler_settings { + local solc_path="$1" + local level="$2" + local evm_version="$3" + + echo "{" + echo " solc: {" + echo " version: \"${solc_path}\"," + echo " settings: {" + echo " optimizer: $(optimizer_settings_for_level "$level")," + echo " evmVersion: \"${evm_version}\"" + echo " }" + echo " }" + echo "}" +} + function truffle_run_test { - local compile_fn="$1" - local test_fn="$2" + local soljson="$1" + local compile_fn="$2" + local test_fn="$3" replace_version_pragmas - force_solc "$CONFIG" "$DIR" "$SOLJSON" - - printLog "Checking optimizer level..." - if [ -z "$OPTIMIZER_LEVEL" ]; then - printError "Optimizer level not found. Please define OPTIMIZER_LEVEL=[1, 2, 3]" - exit 1 - fi - if [[ "$OPTIMIZER_LEVEL" == 1 ]]; then - declare -a optimizer_settings=("{ enabled: false }" "{ enabled: true }" "{ enabled: true, details: { yul: true } }") - fi - if [[ "$OPTIMIZER_LEVEL" == 2 ]]; then - declare -a optimizer_settings=("{ enabled: true }" "{ enabled: true, details: { yul: true } }") - fi - if [[ "$OPTIMIZER_LEVEL" == 3 ]]; then - declare -a optimizer_settings=("{ enabled: true, details: { yul: true } }") - fi + force_truffle_solc_modules "$soljson" - for optimize in "${optimizer_settings[@]}" + for level in $(seq "$OPTIMIZER_LEVEL" 3) do clean - force_solc_settings "$CONFIG" "$optimize" "istanbul" - # Force ABIEncoderV2 in the last step. Has to be the last because code is modified. - if [ "$FORCE_ABIv2" = true ]; then - [[ "$optimize" =~ yul ]] && force_abi_v2 - fi + force_truffle_compiler_settings "$CONFIG" "${DIR}/solc" "$level" istanbul printLog "Running compile function..." $compile_fn diff --git a/scripts/release_emscripten.sh b/test/externalTests/ens.sh similarity index 51% rename from scripts/release_emscripten.sh rename to test/externalTests/ens.sh index 8db9a04947d0..a19ce859d6e2 100755 --- a/scripts/release_emscripten.sh +++ b/test/externalTests/ens.sh @@ -1,14 +1,5 @@ #!/usr/bin/env bash -#------------------------------------------------------------------------------ -# Bash script which calls the Emscripten "publish binary" script on Ubuntu -# and does nothing on macOS. We should probably merge these two scripts in -# the near future. -# -# The documentation for solidity is hosted at: -# -# https://solidity.readthedocs.org -# # ------------------------------------------------------------------------------ # This file is part of solidity. # @@ -25,9 +16,34 @@ # You should have received a copy of the GNU General Public License # along with solidity. If not, see # -# (c) 2016 solidity contributors. +# (c) 2019 solidity contributors. #------------------------------------------------------------------------------ +source scripts/common.sh +source test/externalTests/common.sh + +verify_input "$1" +export SOLJSON="$1" + +function install_fn { npm install; } +function compile_fn { npx truffle compile; } +function test_fn { npm run test; } + +function ens_test +{ + export OPTIMIZER_LEVEL=1 + export CONFIG="truffle-config.js" + + truffle_setup "$SOLJSON" https://github.com/solidity-external-tests/ens.git master_080 + + # Use latest Truffle. Older versions crash on the output from 0.8.0. + force_truffle_version ^5.1.55 + + # Remove the lock file (if it exists) to prevent it from overriding our changes in package.json + rm -f package-lock.json + + run_install "$SOLJSON" install_fn + + truffle_run_test "$SOLJSON" compile_fn test_fn +} -if [[ "$OSTYPE" != "darwin"* ]]; then - ./scripts/travis-emscripten/publish_binary.sh -fi +external_test Ens ens_test diff --git a/test/externalTests/gnosis-v2.sh b/test/externalTests/gnosis-v2.sh new file mode 100755 index 000000000000..19ff3edcb8ec --- /dev/null +++ b/test/externalTests/gnosis-v2.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2020 solidity contributors. +#------------------------------------------------------------------------------ +source scripts/common.sh +source test/externalTests/common.sh + +verify_input "$1" +SOLJSON="$1" + +function install_fn { npm install --package-lock; } +function compile_fn { npx truffle compile; } +function test_fn { npm test; } + +function gnosis_safe_test +{ + OPTIMIZER_LEVEL=2 + CONFIG="truffle-config.js" + + truffle_setup "$SOLJSON" https://github.com/solidity-external-tests/safe-contracts.git v2_080 + + sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_080|g' package.json + sed -i -E 's|"@gnosis.pm/util-contracts": "[^"]+"|"@gnosis.pm/util-contracts": "github:solidity-external-tests/util-contracts#solc-7_080"|g' package.json + + # Remove the lock file (if it exists) to prevent it from overriding our changes in package.json + rm -f package-lock.json + + run_install "$SOLJSON" install_fn + + truffle_run_test "$SOLJSON" compile_fn test_fn +} + +external_test Gnosis-Safe gnosis_safe_test diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index 07c34ca46add..f8ca999d627f 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -30,20 +30,19 @@ function test_fn { npm test; } function gnosis_safe_test { - OPTIMIZER_LEVEL=1 - CONFIG="truffle.js" + OPTIMIZER_LEVEL=2 + CONFIG="truffle-config.js" - truffle_setup https://github.com/solidity-external-tests/safe-contracts.git development_070 + truffle_setup "$SOLJSON" https://github.com/solidity-external-tests/safe-contracts.git development_080 - force_truffle_version - sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_070|g' package.json + sed -i 's|github:gnosis/mock-contract#sol_0_5_0|github:solidity-external-tests/mock-contract#master_080|g' package.json + + # Remove the lock file (if it exists) to prevent it from overriding our changes in package.json rm -f package-lock.json - rm -rf node_modules/ - run_install install_fn - replace_libsolc_call + run_install "$SOLJSON" install_fn - truffle_run_test compile_fn test_fn + truffle_run_test "$SOLJSON" compile_fn test_fn } external_test Gnosis-Safe gnosis_safe_test diff --git a/test/externalTests/solc-js/DAO/DAO.sol b/test/externalTests/solc-js/DAO/DAO.sol index 56e50d23d676..45778a3fcddc 100644 --- a/test/externalTests/solc-js/DAO/DAO.sol +++ b/test/externalTests/solc-js/DAO/DAO.sol @@ -391,7 +391,7 @@ contract DAO is DAOInterface, Token, TokenCreation { receive() external payable override(DAOInterface, TokenCreation) { if (block.timestamp < closingTime + creationGracePeriod && msg.sender != address(extraBalance)) - createTokenProxy(msg.sender); + createTokenProxy(payable(msg.sender)); else receiveEther(); } @@ -459,7 +459,7 @@ contract DAO is DAOInterface, Token, TokenCreation { p.newCurator = _newCurator; if (_newCurator) p.splitData.push(); - p.creator = msg.sender; + p.creator = payable(msg.sender); p.proposalDeposit = msg.value; sumOfProposalDeposits += msg.value; @@ -663,7 +663,7 @@ contract DAO is DAOInterface, Token, TokenCreation { uint fundsToBeMoved = (balances[msg.sender] * p.splitData[0].splitBalance) / p.splitData[0].totalSupply; - if (p.splitData[0].newDAO.createTokenProxy{value: fundsToBeMoved}(msg.sender) == false) + if (p.splitData[0].newDAO.createTokenProxy{value: fundsToBeMoved}(payable(msg.sender)) == false) revert(); @@ -687,7 +687,7 @@ contract DAO is DAOInterface, Token, TokenCreation { // Burn DAO Tokens emit Transfer(msg.sender, 0x0000000000000000000000000000000000000000, balances[msg.sender]); - withdrawRewardFor(msg.sender); // be nice, and get his rewards + withdrawRewardFor(payable(msg.sender)); // be nice, and get his rewards totalSupply -= balances[msg.sender]; balances[msg.sender] = 0; paidOut[msg.sender] = 0; @@ -711,7 +711,7 @@ contract DAO is DAOInterface, Token, TokenCreation { function retrieveDAOReward(bool _toMembers) external override returns (bool _success) { - DAO dao = DAO(msg.sender); + DAO dao = DAO(payable(msg.sender)); if ((rewardToken[msg.sender] * DAOrewardAccount.accumulatedInput()) / totalRewardToken < DAOpaidOut[msg.sender]) @@ -724,11 +724,11 @@ contract DAO is DAOInterface, Token, TokenCreation { reward = address(DAOrewardAccount).balance < reward ? address(DAOrewardAccount).balance : reward; if(_toMembers) { - if (!DAOrewardAccount.payOut(address(dao.rewardAccount()), reward)) + if (!DAOrewardAccount.payOut(payable(dao.rewardAccount()), reward)) revert(); } else { - if (!DAOrewardAccount.payOut(address(dao), reward)) + if (!DAOrewardAccount.payOut(payable(dao), reward)) revert(); } DAOpaidOut[msg.sender] += reward; @@ -736,7 +736,7 @@ contract DAO is DAOInterface, Token, TokenCreation { } function getMyReward() public override returns (bool _success) { - return withdrawRewardFor(msg.sender); + return withdrawRewardFor(payable(msg.sender)); } diff --git a/test/externalTests/solc-js/DAO/TokenCreation.sol b/test/externalTests/solc-js/DAO/TokenCreation.sol index cf0d27a537dd..64abf86ca1fe 100644 --- a/test/externalTests/solc-js/DAO/TokenCreation.sol +++ b/test/externalTests/solc-js/DAO/TokenCreation.sol @@ -124,7 +124,7 @@ override returns (bool success) { if (block.timestamp > closingTime && !isFueled) { // Get extraBalance - will only succeed when called for the first time if (address(extraBalance).balance >= extraBalance.accumulatedInput()) - extraBalance.payOut(address(this), extraBalance.accumulatedInput()); + extraBalance.payOut(payable(this), extraBalance.accumulatedInput()); // Execute refund (bool success,) = msg.sender.call{value: weiGiven[msg.sender]}(""); diff --git a/test/externalTests/solc-js/solc-js.sh b/test/externalTests/solc-js/solc-js.sh index 907061f5c03e..9cc753959257 100755 --- a/test/externalTests/solc-js/solc-js.sh +++ b/test/externalTests/solc-js/solc-js.sh @@ -35,23 +35,24 @@ function solcjs_test SOLCJS_INPUT_DIR="$TEST_DIR"/test/externalTests/solc-js # set up solc-js on the branch specified - setup master + setup "$SOLJSON" master printLog "Updating index.js file..." echo "require('./determinism.js');" >> test/index.js printLog "Copying determinism.js..." - cp -f $SOLCJS_INPUT_DIR/determinism.js test/ + cp -f "$SOLCJS_INPUT_DIR/determinism.js" test/ printLog "Copying contracts..." - cp -Rf $SOLCJS_INPUT_DIR/DAO test/ + cp -Rf "$SOLCJS_INPUT_DIR/DAO" test/ printLog "Copying SMTChecker tests..." cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests test/ + rm -rf test/smtCheckerTests/imports # Update version (needed for some tests) echo "Updating package.json to version $VERSION" - npm version --allow-same-version --no-git-tag-version $VERSION + npm version --allow-same-version --no-git-tag-version "$VERSION" run_test compile_fn test_fn } diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index 51051f3344e5..8d8cb4845779 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -33,10 +33,10 @@ function zeppelin_test OPTIMIZER_LEVEL=1 CONFIG="truffle-config.js" - truffle_setup https://github.com/solidity-external-tests/openzeppelin-contracts.git upgrade-0.7.0 - run_install install_fn + truffle_setup "$SOLJSON" https://github.com/solidity-external-tests/openzeppelin-contracts.git master_080 + run_install "$SOLJSON" install_fn - truffle_run_test compile_fn test_fn + truffle_run_test "$SOLJSON" compile_fn test_fn } external_test Zeppelin zeppelin_test diff --git a/test/formal/combine_byte_shr_2.py b/test/formal/combine_byte_shr_2.py index d74114e855ab..4f9be05a4c33 100644 --- a/test/formal/combine_byte_shr_2.py +++ b/test/formal/combine_byte_shr_2.py @@ -3,7 +3,7 @@ """ byte(A, shr(B, X)) -given B % 8 == 0 && A < n_bits/8 && B <= n_bits && A < B / 8 +given A < B / 8 -> 0 """ @@ -22,9 +22,6 @@ # Optimized result opt = 0 -rule.require(B % 8 == 0) -rule.require(ULT(A, n_bits/8)) -rule.require(ULE(B, n_bits)) rule.require(ULT(A, DIV(B,8))) rule.check(nonopt, opt) diff --git a/test/formal/exp_neg_one.py b/test/formal/exp_neg_one.py new file mode 100644 index 000000000000..88416496eda8 --- /dev/null +++ b/test/formal/exp_neg_one.py @@ -0,0 +1,16 @@ +from rule import Rule +from opcodes import * +from util import * + +""" +Checking conversion of exp(-1, X) to sub(isZero(and(X, 1)), and(X, 1)) +""" + +rule = Rule() +n_bits = 256 + +X = BitVec('X', n_bits) + +exp_neg_one = If(MOD(X, 2) == 0, BitVecVal(1, n_bits), BVUnsignedMax(n_bits, n_bits)) + +rule.check(SUB(ISZERO(AND(X, 1)), AND(X, 1)), exp_neg_one) diff --git a/test/formal/exp_to_shl.py b/test/formal/exp_to_shl.py new file mode 100644 index 000000000000..064d1af3e746 --- /dev/null +++ b/test/formal/exp_to_shl.py @@ -0,0 +1,34 @@ +from rule import Rule +from opcodes import * +from util import * + +""" +Checking conversion of exp(2, X) to shl(X, 1) +""" + +rule = Rule() +n_bits = 256 + +# Proof of exp(2, X) = shl(X, 1) by induction: +# +# Base case: X = 0, exp(2, 0) = 1 = 1 = shl(0, 1) +# Inductive step: assuming exp(2, X) = shl(X, 1) for X <= N +# to prove: exp(2, N + 1) = shl(N + 1, 1) +# +# Notice that exp(2, N + 1) = 2 * exp(2, N) mod 2**256 +# since exp(2, N) = shl(N, 1), it is enough to show that +# 2 * shl(N, 1) mod 2**256 = shl(N + 1, 1) +# +# Also note that N + 1 < 2**256 + +N = BitVec('N', n_bits) +inductive_step = 2 * SHL(N, 1) + +rule.check( + inductive_step, + If( + N == 2**256 - 1, + 0, + SHL(N + 1, 1) + ) +) diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index c93d7b58d35a..a79e773b9938 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) // PushDeployTimeAddress _assembly.append(PushDeployTimeAddress); // AssignImmutable. - // Note that since there is no reference to "someOtherImmutable", this will compile to a simple POP in the hex output. + // Note that since there is no reference to "someOtherImmutable", this will just compile to two POPs in the hex output. _assembly.appendImmutableAssignment("someOtherImmutable"); _assembly.append(u256(2)); _assembly.appendImmutableAssignment("someImmutable"); @@ -105,8 +105,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) BOOST_CHECK_EQUAL( _assembly.assemble().toHex(), "5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__" - "60005660676022604573000000000000000000000000000000000000000050" - "60028060015250" + "6000566067602260457300000000000000000000000000000000000000005050" + "600260010152" "00fe" "7f0000000000000000000000000000000000000000000000000000000000000000" "fe010203044266eeaa" @@ -184,8 +184,10 @@ BOOST_AUTO_TEST_CASE(immutable) shared_ptr _subAsmPtr = make_shared(_subAsm); _assembly.append(u256(42)); + _assembly.append(u256(0)); _assembly.appendImmutableAssignment("someImmutable"); _assembly.append(u256(23)); + _assembly.append(u256(0)); _assembly.appendImmutableAssignment("someOtherImmutable"); auto sub = _assembly.appendSubroutine(_subAsmPtr); @@ -198,21 +200,22 @@ BOOST_AUTO_TEST_CASE(immutable) // root.asm // assign "someImmutable" "602a" // PUSH1 42 - value for someImmutable - "80" // DUP1 + "6000" // PUSH1 0 - offset of code into which to insert the immutable + "8181" // DUP2 DUP2 "6001" // PUSH1 1 - offset of first someImmutable in sub_0 + "01" // ADD - add offset of immutable to offset of code "52" // MSTORE - "80" // DUP1 "6043" // PUSH1 67 - offset of second someImmutable in sub_0 + "01" // ADD - add offset of immutable to offset of code "52" // MSTORE - "50" // POP // assign "someOtherImmutable" "6017" // PUSH1 23 - value for someOtherImmutable - "80" // DUP1 + "6000" // PUSH1 0 - offset of code into which to insert the immutable "6022" // PUSH1 34 - offset of someOtherImmutable in sub_0 + "01" // ADD - add offset of immutable to offset of code "52" // MSTORE - "50" // POP "6063" // PUSH1 0x63 - dataSize(sub_0) - "6017" // PUSH1 0x17 - dataOffset(sub_0) + "601b" // PUSH1 0x23 - dataOffset(sub_0) "fe" // INVALID // end of root.asm // sub.asm @@ -224,8 +227,10 @@ BOOST_AUTO_TEST_CASE(immutable) _assembly.assemblyString(), " /* \"root.asm\":1:3 */\n" " 0x2a\n" + " 0x00\n" " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " 0x17\n" + " 0x00\n" " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" " dataSize(sub_0)\n" " dataOffset(sub_0)\n" @@ -242,8 +247,10 @@ BOOST_AUTO_TEST_CASE(immutable) util::jsonCompactPrint(_assembly.assemblyJSON(indices)), "{\".code\":[" "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"17\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"0\"}," "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}" diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 741dc7523ff1..155f17bdfe16 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -1282,7 +1282,7 @@ BOOST_AUTO_TEST_CASE(cse_remove_redundant_shift_masking) if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) return; - for (size_t i = 1; i < 256; i++) + for (unsigned i = 1; i < 256; i++) { checkCSE({ u256(boost::multiprecision::pow(u256(2), i) - 1), @@ -1310,7 +1310,7 @@ BOOST_AUTO_TEST_CASE(cse_remove_redundant_shift_masking) } // Check that opt. does NOT trigger - for (size_t i = 1; i < 255; i++) + for (unsigned i = 1; i < 255; i++) { checkCSE({ u256(boost::multiprecision::pow(u256(2), i) - 1), diff --git a/test/liblangutil/CharStream.cpp b/test/liblangutil/CharStream.cpp index d1189c3e99c7..9321b14e0db5 100644 --- a/test/liblangutil/CharStream.cpp +++ b/test/liblangutil/CharStream.cpp @@ -31,7 +31,7 @@ namespace solidity::langutil::test { -BOOST_AUTO_TEST_SUITE(CharStreamtest) +BOOST_AUTO_TEST_SUITE(CharStreamTest) BOOST_AUTO_TEST_CASE(test_fail) { diff --git a/test/libsolidity/SolidityScanner.cpp b/test/liblangutil/Scanner.cpp similarity index 74% rename from test/libsolidity/SolidityScanner.cpp rename to test/liblangutil/Scanner.cpp index f818c3074d22..563981cb2837 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/liblangutil/Scanner.cpp @@ -21,15 +21,16 @@ */ #include + #include using namespace std; using namespace solidity::langutil; -namespace solidity::frontend::test +namespace solidity::langutil::test { -BOOST_AUTO_TEST_SUITE(SolidityScanner) +BOOST_AUTO_TEST_SUITE(ScannerTest) BOOST_AUTO_TEST_CASE(test_empty) { @@ -130,10 +131,29 @@ BOOST_AUTO_TEST_CASE(string_escapes) BOOST_AUTO_TEST_CASE(string_escapes_all) { - Scanner scanner(CharStream(" { \"a\\x61\\b\\f\\n\\r\\t\\v\"", "")); + Scanner scanner(CharStream(" { \"a\\x61\\n\\r\\t\"", "")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); - BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa\b\f\n\r\t\v"); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa\n\r\t"); +} + +BOOST_AUTO_TEST_CASE(string_escapes_legal_before_080) +{ + Scanner scanner(CharStream(" { \"a\\b", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); + scanner.reset(CharStream(" { \"a\\f", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); + scanner.reset(CharStream(" { \"a\\v", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalEscapeSequence); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), ""); } BOOST_AUTO_TEST_CASE(string_escapes_with_zero) @@ -214,7 +234,7 @@ BOOST_AUTO_TEST_CASE(leading_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); scanner.reset(CharStream("function .a(", "")); - scanner.supportPeriodInIdentifier(true); + scanner.setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Period); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); @@ -233,7 +253,7 @@ BOOST_AUTO_TEST_CASE(middle_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); scanner.reset(CharStream("function a...a(", "")); - scanner.supportPeriodInIdentifier(true); + scanner.setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -249,7 +269,7 @@ BOOST_AUTO_TEST_CASE(trailing_dot_in_identifier) BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); scanner.reset(CharStream("function a.(", "")); - scanner.supportPeriodInIdentifier(true); + scanner.setScannerMode(ScannerKind::Yul); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); @@ -405,7 +425,7 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_CHECK_EQUAL(scanner.next(), Token::AssignAdd); BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); BOOST_CHECK_EQUAL(scanner.next(), Token::Inc); - BOOST_CHECK_EQUAL(scanner.next(), Token::Arrow); + BOOST_CHECK_EQUAL(scanner.next(), Token::DoubleArrow); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); BOOST_CHECK_EQUAL(scanner.next(), Token::SAR); BOOST_CHECK_EQUAL(scanner.next(), Token::AssignSar); @@ -587,6 +607,42 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) // Unicode string literal +BOOST_AUTO_TEST_CASE(unicode_prefix_only) +{ + Scanner scanner(CharStream("{ unicode", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ unicode", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); +} + +BOOST_AUTO_TEST_CASE(unicode_invalid_space) +{ + Scanner scanner(CharStream("{ unicode ", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(unicode_invalid_token) +{ + Scanner scanner(CharStream("{ unicode test", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ unicode test", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "unicode"); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "test"); +} + BOOST_AUTO_TEST_CASE(valid_unicode_literal) { Scanner scanner(CharStream("{ unicode\"Hello 😃\"", "")); @@ -604,7 +660,40 @@ BOOST_AUTO_TEST_CASE(valid_nonprintable_in_unicode_literal) BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("Hello \x07\xf0\x9f\x98\x83", 11)); } -// HEX STRING LITERAL +// Hex string literal + +BOOST_AUTO_TEST_CASE(hex_prefix_only) +{ + Scanner scanner(CharStream("{ hex", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ hex", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(hex_invalid_space) +{ + Scanner scanner(CharStream("{ hex ", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} + +BOOST_AUTO_TEST_CASE(hex_invalid_token) +{ + Scanner scanner(CharStream("{ hex test", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); + scanner.reset(CharStream("{ hex test", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalToken); +} BOOST_AUTO_TEST_CASE(valid_hex_literal) { @@ -646,7 +735,7 @@ BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) BOOST_CHECK_EQUAL(scanner.currentError(), ScannerError::IllegalHexString); } -// COMMENTS +// Comments BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { @@ -769,6 +858,137 @@ BOOST_AUTO_TEST_CASE(irregular_line_breaks_in_strings) } } +BOOST_AUTO_TEST_CASE(solidity_keywords) +{ + // These are tokens which have a different meaning in Yul. + string keywords = "return byte bool address var in true false leave switch case default"; + Scanner scanner(CharStream(keywords, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Return); + BOOST_CHECK_EQUAL(scanner.next(), Token::Byte); + BOOST_CHECK_EQUAL(scanner.next(), Token::Bool); + BOOST_CHECK_EQUAL(scanner.next(), Token::Address); + BOOST_CHECK_EQUAL(scanner.next(), Token::Var); + BOOST_CHECK_EQUAL(scanner.next(), Token::In); + BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Switch); + BOOST_CHECK_EQUAL(scanner.next(), Token::Case); + BOOST_CHECK_EQUAL(scanner.next(), Token::Default); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(keywords, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::TrueLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::FalseLiteral); + BOOST_CHECK_EQUAL(scanner.next(), Token::Leave); + BOOST_CHECK_EQUAL(scanner.next(), Token::Switch); + BOOST_CHECK_EQUAL(scanner.next(), Token::Case); + BOOST_CHECK_EQUAL(scanner.next(), Token::Default); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_keyword_like) +{ + Scanner scanner(CharStream("leave.function", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Period); + BOOST_CHECK_EQUAL(scanner.next(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("leave.function", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_identifier_with_dots) +{ + Scanner scanner(CharStream("mystorage.slot := 1", "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Period); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("mystorage.slot := 1", "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssemblyAssign); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_function) +{ + string sig = "function f(a, b) -> x, y"; + Scanner scanner(CharStream(sig, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::RightArrow); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(sig, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::RightArrow); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(yul_function_with_whitespace) +{ + string sig = "function f (a, b) - > x, y"; + Scanner scanner(CharStream(sig, "")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(sig, "")); + scanner.setScannerMode(ScannerKind::Yul); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Function); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::LParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::RParen); + BOOST_CHECK_EQUAL(scanner.next(), Token::Sub); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Comma); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp index 611bc7cd36a3..b910949d8581 100644 --- a/test/libsolidity/ABIDecoderTests.cpp +++ b/test/libsolidity/ABIDecoderTests.cpp @@ -37,17 +37,6 @@ namespace solidity::frontend::test BOOST_FIXTURE_TEST_SUITE(ABIDecoderTest, SolidityExecutionFramework) -BOOST_AUTO_TEST_CASE(both_encoders_macro) -{ - // This tests that the "both decoders macro" at least runs twice and - // modifies the source. - string sourceCode; - int runs = 0; - BOTH_ENCODERS(runs++;) - BOOST_CHECK(sourceCode == NewEncoderPragma); - BOOST_CHECK_EQUAL(runs, 2); -} - BOOST_AUTO_TEST_CASE(value_types) { string sourceCode = R"( @@ -68,251 +57,11 @@ BOOST_AUTO_TEST_CASE(value_types) compileAndRun(sourceCode); ABI_CHECK(callContractFunction( "f(uint256,uint16,uint24,int24,bytes3,bool,address)", - 1, 2, 3, 4, string("abc"), true, u160(m_contractAddress) + 1, 2, 3, 4, string("abc"), true, m_contractAddress ), encodeArgs(u256(20))); ) } -BOOST_AUTO_TEST_CASE(enums) -{ - string sourceCode = R"( - contract C { - enum E { A, B } - function f(E e) public pure returns (uint x) { - assembly { x := e } - } - } - )"; - bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; - BOTH_ENCODERS( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(uint8)", 0), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(uint8)", 1), encodeArgs(u256(1))); - // The old decoder was not as strict about enums - ABI_CHECK(callContractFunction("f(uint8)", 2), (newDecoder ? encodeArgs() : encodeArgs(2))); - ABI_CHECK(callContractFunction("f(uint8)", u256(-1)), (newDecoder? encodeArgs() : encodeArgs(u256(0xff)))); - newDecoder = true; - ) -} - -BOOST_AUTO_TEST_CASE(cleanup) -{ - string sourceCode = R"( - contract C { - function f(uint16 a, int16 b, address c, bytes3 d, bool e) - public pure returns (uint v, uint w, uint x, uint y, uint z) { - assembly { v := a w := b x := c y := d z := e} - } - } - )"; - bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; - BOTH_ENCODERS( - compileAndRun(sourceCode); - ABI_CHECK( - callContractFunction("f(uint16,int16,address,bytes3,bool)", 1, 2, 3, "a", true), - encodeArgs(u256(1), u256(2), u256(3), string("a"), true) - ); - ABI_CHECK( - callContractFunction( - "f(uint16,int16,address,bytes3,bool)", - u256(0xffffff), u256(0x1ffff), u256(-1), string("abcd"), u256(1) - ), - newDecoder ? bytes{} : encodeArgs(u256(0xffff), u256(-1), (u256(1) << 160) - 1, string("abc"), true) - ); - ABI_CHECK( - callContractFunction( - "f(uint16,int16,address,bytes3,bool)", - u256(0xffffff), u256(0), u256(0), string("bcd"), u256(1) - ), - newDecoder ? bytes{} : encodeArgs(u256(0xffff), u256(0), 0, string("bcd"), true) - ); - ABI_CHECK( - callContractFunction( - "f(uint16,int16,address,bytes3,bool)", - u256(0), u256(0x1ffff), u256(0), string("ab"), u256(1) - ), - newDecoder ? bytes{} : encodeArgs(u256(0), u256(-1), 0, string("ab"), true) - ); - ABI_CHECK( - callContractFunction( - "f(uint16,int16,address,bytes3,bool)", - u256(0), u256(0), u256(-1), string("ad"), u256(1) - ), - newDecoder ? bytes{} : encodeArgs(u256(0), u256(0), (u256(1) << 160) - 1, string("ad"), true) - ); - ABI_CHECK( - callContractFunction( - "f(uint16,int16,address,bytes3,bool)", - u256(0), u256(0), u256(0), string("abcd"), u256(1) - ), - newDecoder ? bytes{} : encodeArgs(u256(0), u256(0), 0, string("abc"), true) - ); - ABI_CHECK( - callContractFunction( - "f(uint16,int16,address,bytes3,bool)", - u256(0), u256(0), u256(0), string("abc"), u256(2) - ), - newDecoder ? bytes{} : encodeArgs(u256(0), u256(0), 0, string("abc"), true) - ); - newDecoder = true; - ) -} - -BOOST_AUTO_TEST_CASE(fixed_arrays) -{ - string sourceCode = R"( - contract C { - function f(uint16[3] memory a, uint16[2][3] memory b, uint i, uint j, uint k) - public pure returns (uint, uint) { - return (a[i], b[j][k]); - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode); - bytes args = encodeArgs( - 1, 2, 3, - 11, 12, - 21, 22, - 31, 32, - 1, 2, 1 - ); - ABI_CHECK( - callContractFunction("f(uint16[3],uint16[2][3],uint256,uint256,uint256)", args), - encodeArgs(u256(2), u256(32)) - ); - ) -} - -BOOST_AUTO_TEST_CASE(dynamic_arrays) -{ - string sourceCode = R"( - contract C { - function f(uint a, uint16[] memory b, uint c) - public pure returns (uint, uint, uint) { - return (b.length, b[a], c); - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode); - bytes args = encodeArgs( - 6, 0x60, 9, - 7, - 11, 12, 13, 14, 15, 16, 17 - ); - ABI_CHECK( - callContractFunction("f(uint256,uint16[],uint256)", args), - encodeArgs(u256(7), u256(17), u256(9)) - ); - ) -} - -BOOST_AUTO_TEST_CASE(dynamic_nested_arrays) -{ - string sourceCode = R"( - contract C { - function f(uint a, uint16[][] memory b, uint[2][][3] memory c, uint d) - public pure returns (uint, uint, uint, uint, uint, uint, uint) { - return (a, b.length, b[1].length, b[1][1], c[1].length, c[1][1][1], d); - } - function test() public view returns (uint, uint, uint, uint, uint, uint, uint) { - uint16[][] memory b = new uint16[][](3); - b[0] = new uint16[](2); - b[0][0] = 0x55; - b[0][1] = 0x56; - b[1] = new uint16[](4); - b[1][0] = 0x65; - b[1][1] = 0x66; - b[1][2] = 0x67; - b[1][3] = 0x68; - - uint[2][][3] memory c; - c[0] = new uint[2][](1); - c[0][0][1] = 0x75; - c[1] = new uint[2][](5); - c[1][1][1] = 0x85; - - return this.f(0x12, b, c, 0x13); - } - } - )"; - NEW_ENCODER( - compileAndRun(sourceCode); - bytes args = encodeArgs( - 0x12, 4 * 0x20, 17 * 0x20, 0x13, - // b - 3, 3 * 0x20, 6 * 0x20, 11 * 0x20, - 2, 85, 86, - 4, 101, 102, 103, 104, - 0, - // c - 3 * 0x20, 6 * 0x20, 17 * 0x20, - 1, 0, 117, - 5, 0, 0, 0, 133, 0, 0, 0, 0, 0, 0, - 0 - ); - - bytes expectation = encodeArgs(0x12, 3, 4, 0x66, 5, 0x85, 0x13); - ABI_CHECK(callContractFunction("test()"), expectation); - ABI_CHECK(callContractFunction("f(uint256,uint16[][],uint256[2][][3],uint256)", args), expectation); - ) -} - -BOOST_AUTO_TEST_CASE(byte_arrays) -{ - string sourceCode = R"( - contract C { - function f(uint a, bytes memory b, uint c) - public pure returns (uint, uint, byte, uint) { - return (a, b.length, b[3], c); - } - - function f_external(uint a, bytes calldata b, uint c) - external pure returns (uint, uint, byte, uint) { - return (a, b.length, b[3], c); - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode); - bytes args = encodeArgs( - 6, 0x60, 9, - 7, "abcdefg" - ); - ABI_CHECK( - callContractFunction("f(uint256,bytes,uint256)", args), - encodeArgs(u256(6), u256(7), "d", 9) - ); - ABI_CHECK( - callContractFunction("f_external(uint256,bytes,uint256)", args), - encodeArgs(u256(6), u256(7), "d", 9) - ); - ) -} - -BOOST_AUTO_TEST_CASE(calldata_arrays_too_large) -{ - string sourceCode = R"( - contract C { - function f(uint a, uint[] calldata b, uint c) external pure returns (uint) { - return 7; - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode); - bytes args = encodeArgs( - 6, 0x60, 9, - (u256(1) << 255) + 2, 1, 2 - ); - ABI_CHECK( - callContractFunction("f(uint256,uint256[],uint256)", args), - encodeArgs() - ); - ) -} - BOOST_AUTO_TEST_CASE(decode_from_memory_simple) { string sourceCode = R"( @@ -574,7 +323,7 @@ BOOST_AUTO_TEST_CASE(validation_function_type) function i(function () external[] calldata a) external pure returns (uint r) { a[0]; r = 4; } } )"; - bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; + bool newDecoder = false; string validFun{"01234567890123456789abcd"}; string invalidFun{"01234567890123456789abcdX"}; BOTH_ENCODERS( @@ -592,121 +341,6 @@ BOOST_AUTO_TEST_CASE(validation_function_type) ) } -BOOST_AUTO_TEST_CASE(validation_function_type_inside_struct) -{ - string sourceCode = R"( - contract C { - struct S { function () external x; } - function f(S memory) public pure returns (uint r) { r = 1; } - function g(S calldata) external pure returns (uint r) { r = 2; } - function h(S calldata s) external pure returns (uint r) { s.x; r = 3; } - } - )"; - string validFun{"01234567890123456789abcd"}; - string invalidFun{"01234567890123456789abcdX"}; - NEW_ENCODER( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f((function))", validFun), encodeArgs(1)); - // Error because we copy to memory - ABI_CHECK(callContractFunction("f((function))", invalidFun), encodeArgs()); - ABI_CHECK(callContractFunction("g((function))", validFun), encodeArgs(2)); - // No error because x is not accessed. - ABI_CHECK(callContractFunction("g((function))", invalidFun), encodeArgs(2)); - ABI_CHECK(callContractFunction("h((function))", validFun), encodeArgs(3)); - // Error on access. - ABI_CHECK(callContractFunction("h((function))", invalidFun), encodeArgs()); - ) -} - -BOOST_AUTO_TEST_CASE(storage_ptr) -{ - string sourceCode = R"( - library L { - struct S { uint x; uint y; } - function f(uint[] storage r, S storage s) public returns (uint, uint, uint, uint) { - r[2] = 8; - s.x = 7; - return (r[0], r[1], s.x, s.y); - } - } - contract C { - uint8 x = 3; - L.S s; - uint[] r; - function f() public returns (uint, uint, uint, uint, uint, uint) { - r = new uint[](6); - r[0] = 1; - r[1] = 2; - r[2] = 3; - s.x = 11; - s.y = 12; - (uint a, uint b, uint c, uint d) = L.f(r, s); - return (r[2], s.x, a, b, c, d); - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode, 0, "L"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(8, 7, 1, 2, 7, 12)); - ) -} - -BOOST_AUTO_TEST_CASE(struct_simple) -{ - string sourceCode = R"( - contract C { - struct S { uint a; uint8 b; uint8 c; bytes2 d; } - function f(S memory s) public pure returns (uint a, uint b, uint c, uint d) { - a = s.a; - b = s.b; - c = s.c; - d = uint16(s.d); - } - } - )"; - NEW_ENCODER( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f((uint256,uint8,uint8,bytes2))", 1, 2, 3, "ab"), encodeArgs(1, 2, 3, 'a' * 0x100 + 'b')); - ) -} - -BOOST_AUTO_TEST_CASE(struct_validation) -{ - string sourceCode = R"( - contract C { - struct S { int16 a; uint8 b; bytes2 c; } - function f(S memory s) public pure returns (uint a, uint b, uint c) { - assembly { - a := mload(s) - b := mload(add(s, 0x20)) - c := mload(add(s, 0x40)) - } - } - } - )"; - u256 largeNeg("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01"); - NEW_ENCODER( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK( - callContractFunction("f((int16,uint8,bytes2))", largeNeg, 0xff, "ab"), - encodeArgs(largeNeg, 0xff, "ab") - ); - ABI_CHECK( - callContractFunction("f((int16,uint8,bytes2))", 0xff010, 0xff, "ab"), - encodeArgs() - ); - ABI_CHECK( - callContractFunction("f((int16,uint8,bytes2))", largeNeg, 0xff0002, "ab"), - encodeArgs() - ); - ABI_CHECK( - callContractFunction("f((int16,uint8,bytes2))", largeNeg, 0xff, "abcd"), - encodeArgs() - ); - ) -} - BOOST_AUTO_TEST_CASE(struct_short) { string sourceCode = R"( @@ -734,73 +368,6 @@ BOOST_AUTO_TEST_CASE(struct_short) ) } -BOOST_AUTO_TEST_CASE(struct_function) -{ - string sourceCode = R"( - contract C { - struct S { function () external returns (uint) f; uint b; } - function f(S memory s) public returns (uint, uint) { - return (s.f(), s.b); - } - function test() public returns (uint, uint) { - return this.f(S(this.g, 3)); - } - function g() public returns (uint) { return 7; } - } - )"; - NEW_ENCODER( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("test()"), encodeArgs(7, 3)); - ) -} - -BOOST_AUTO_TEST_CASE(mediocre_struct) -{ - string sourceCode = R"( - contract C { - struct S { C c; } - function f(uint a, S[2] memory s1, uint b) public returns (uint r1, C r2, uint r3) { - r1 = a; - r2 = s1[0].c; - r3 = b; - } - } - )"; - NEW_ENCODER( - compileAndRun(sourceCode, 0, "C"); - string sig = "f(uint256,(address)[2],uint256)"; - ABI_CHECK(callContractFunction(sig, - 7, u256(u160(m_contractAddress)), 0, 8 - ), encodeArgs(7, u256(u160(m_contractAddress)), 8)); - ) -} - -BOOST_AUTO_TEST_CASE(mediocre2_struct) -{ - string sourceCode = R"( - contract C { - struct S { C c; uint[] x; } - function f(uint a, S[2] memory s1, uint b) public returns (uint r1, C r2, uint r3) { - r1 = a; - r2 = s1[0].c; - r3 = b; - } - } - )"; - NEW_ENCODER( - compileAndRun(sourceCode, 0, "C"); - string sig = "f(uint256,(address,uint256[])[2],uint256)"; - ABI_CHECK(callContractFunction(sig, - 7, 0x60, 8, - 0x40, 7 * 0x20, - u256(u160(m_contractAddress)), 0x40, - 2, 0x11, 0x12, - 0x99, 0x40, - 4, 0x31, 0x32, 0x34, 0x35 - ), encodeArgs(7, u256(u160(m_contractAddress)), 8)); - ) -} - BOOST_AUTO_TEST_CASE(complex_struct) { string sourceCode = R"( @@ -830,7 +397,7 @@ BOOST_AUTO_TEST_CASE(complex_struct) 0x40, 0x100, // S s1[0] - u256(u160(m_contractAddress)), + m_contractAddress, 0x40, // T s1[0].t 1, // length @@ -853,115 +420,13 @@ BOOST_AUTO_TEST_CASE(complex_struct) 0x21, 2, 0x22, 0, 0, 0 ); - ABI_CHECK(callContractFunction(sig, args), encodeArgs(7, u256(u160(m_contractAddress)), 8, 2, 0x1234, 3, 2, 0x22)); + ABI_CHECK(callContractFunction(sig, args), encodeArgs(7, m_contractAddress, 8, 2, 0x1234, 3, 2, 0x22)); // invalid enum value args.data()[0x20 * 28] = 3; ABI_CHECK(callContractFunction(sig, args), encodeArgs()); ) } -BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_simple) -{ - if (m_evmVersion == langutil::EVMVersion::homestead()) - return; - - string sourceCode = R"( - contract C { - function dyn() public returns (bytes memory) { - return "1234567890123456789012345678901234567890"; - } - function f() public returns (bytes memory) { - return this.dyn(); - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x20, 40, string("1234567890123456789012345678901234567890"))); - ) -} - -BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_advanced) -{ - if (m_evmVersion == langutil::EVMVersion::homestead()) - return; - - string sourceCode = R"( - contract C { - function dyn() public returns (bytes memory a, uint b, bytes20[] memory c, uint d) { - a = "1234567890123456789012345678901234567890"; - b = uint(-1); - c = new bytes20[](4); - c[0] = bytes20(uint160(1234)); - c[3] = bytes20(uint160(6789)); - d = 0x1234; - } - function f() public returns (bytes memory, uint, bytes20[] memory, uint) { - return this.dyn(); - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x80, u256(-1), 0xe0, 0x1234, - 40, string("1234567890123456789012345678901234567890"), - 4, u256(1234) << (8 * (32 - 20)), 0, 0, u256(6789) << (8 * (32 - 20)) - )); - ) -} - -BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_out_of_range) -{ - string sourceCode = R"( - contract C { - function dyn(uint x) public returns (bytes memory a) { - assembly { - mstore(0, 0x20) - mstore(0x20, 0x21) - return(0, x) - } - } - function f(uint x) public returns (bool) { - this.dyn(x); - return true; - } - } - )"; - BOTH_ENCODERS( - compileAndRun(sourceCode, 0, "C"); - if (m_evmVersion == langutil::EVMVersion::homestead()) - { - ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs(true)); - ABI_CHECK(callContractFunction("f(uint256)", 0x7f), encodeArgs(true)); - } - else - { - ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs()); - ABI_CHECK(callContractFunction("f(uint256)", 0x61), encodeArgs(true)); - } - ABI_CHECK(callContractFunction("f(uint256)", 0x80), encodeArgs(true)); - ) -} - -BOOST_AUTO_TEST_CASE(out_of_bounds_bool_value) -{ - string sourceCode = R"( - contract C { - function f(bool b) public pure returns (bool) { return b; } - } - )"; - bool newDecoder = solidity::test::CommonOptions::get().useABIEncoderV2; - BOTH_ENCODERS( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(true)); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(false)); - ABI_CHECK(callContractFunctionNoEncoding("f(bool)", bytes(32, 0)), encodeArgs(0)); - ABI_CHECK(callContractFunctionNoEncoding("f(bool)", bytes(32, 0xff)), newDecoder ? encodeArgs() : encodeArgs(1)); - newDecoder = true; - ) -} - BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 6c614e75b3c8..6cb5240441a7 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -48,17 +48,6 @@ namespace solidity::frontend::test BOOST_FIXTURE_TEST_SUITE(ABIEncoderTest, SolidityExecutionFramework) -BOOST_AUTO_TEST_CASE(both_encoders_macro) -{ - // This tests that the "both encoders macro" at least runs twice and - // modifies the source. - string sourceCode; - int runs = 0; - BOTH_ENCODERS(runs++;) - BOOST_CHECK(sourceCode == NewEncoderPragma); - BOOST_CHECK_EQUAL(runs, 2); -} - BOOST_AUTO_TEST_CASE(value_types) { string sourceCode = R"( @@ -70,7 +59,7 @@ BOOST_AUTO_TEST_CASE(value_types) assembly { b := 7 } C c; assembly { c := sub(0, 5) } - emit E(10, uint16(uint256(-2)), uint24(0x12121212), int24(int256(-1)), bytes3(x), b, c); + emit E(10, uint16(type(uint).max - 1), uint24(uint(0x12121212)), int24(int256(-1)), bytes3(x), b, c); } } )"; @@ -78,7 +67,7 @@ BOOST_AUTO_TEST_CASE(value_types) compileAndRun(sourceCode); callContractFunction("f()"); REQUIRE_LOG_DATA(encodeArgs( - 10, u256(65534), u256(0x121212), u256(-1), string("\x1b\xab\xab"), true, u160(u256(-5)) + 10, u256(65534), u256(0x121212), u256(-1), string("\x1b\xab\xab"), true, h160("fffffffffffffffffffffffffffffffffffffffb") )); ) } @@ -119,7 +108,7 @@ BOOST_AUTO_TEST_CASE(enum_type_cleanup) compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("f(uint256)", 0) == encodeArgs(0)); BOOST_CHECK(callContractFunction("f(uint256)", 1) == encodeArgs(1)); - BOOST_CHECK(callContractFunction("f(uint256)", 2) == encodeArgs()); + BOOST_CHECK(callContractFunction("f(uint256)", 2) == panicData(PanicCode::EnumConversionError)); ) } @@ -166,7 +155,7 @@ BOOST_AUTO_TEST_CASE(memory_array_one_dim) } )"; - if (!solidity::test::CommonOptions::get().useABIEncoderV2) + if (solidity::test::CommonOptions::get().useABIEncoderV1) { compileAndRun(sourceCode); callContractFunction("f()"); @@ -174,9 +163,11 @@ BOOST_AUTO_TEST_CASE(memory_array_one_dim) REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256("0xfffffffe"), u256("0xffffffff"), u256("0x100000000"))); } - compileAndRun(NewEncoderPragma + sourceCode); - callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256(-2), u256(-1), u256(0))); + NEW_ENCODER( + compileAndRun(sourceCode); + callContractFunction("f()"); + REQUIRE_LOG_DATA(encodeArgs(10, 0x60, 11, 3, u256(-2), u256(-1), u256(0))); + ) } BOOST_AUTO_TEST_CASE(memory_array_two_dim) @@ -189,7 +180,7 @@ BOOST_AUTO_TEST_CASE(memory_array_two_dim) x[0] = new int16[](3); x[1] = new int16[](2); x[0][0] = 7; - x[0][1] = int16(0x010203040506); + x[0][1] = int16(int(0x010203040506)); x[0][2] = -1; x[1][0] = 4; x[1][1] = 5; @@ -273,7 +264,11 @@ BOOST_AUTO_TEST_CASE(storage_array) BOTH_ENCODERS( compileAndRun(sourceCode); callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs(u160(-1), u160(-2), u160(-3))); + REQUIRE_LOG_DATA(encodeArgs( + h160("ffffffffffffffffffffffffffffffffffffffff"), + h160("fffffffffffffffffffffffffffffffffffffffe"), + h160("fffffffffffffffffffffffffffffffffffffffd") + )); ) } @@ -294,7 +289,13 @@ BOOST_AUTO_TEST_CASE(storage_array_dyn) BOTH_ENCODERS( compileAndRun(sourceCode); callContractFunction("f()"); - REQUIRE_LOG_DATA(encodeArgs(0x20, 3, u160(1), u160(2), u160(3))); + REQUIRE_LOG_DATA(encodeArgs( + 0x20, + 3, + h160("0000000000000000000000000000000000000001"), + h160("0000000000000000000000000000000000000002"), + h160("0000000000000000000000000000000000000003") + )); ) } @@ -471,7 +472,7 @@ BOOST_AUTO_TEST_CASE(structs2) s1[0].t[0].e = E.B; s1[0].t[0].y = 0x12; s2 = new S[](2); - s2[1].c = C(0x1234); + s2[1].c = C(address(0x1234)); s2[1].t = new T[](3); s2[1].t[1].x = 0x21; s2[1].t[1].e = E.C; @@ -488,7 +489,7 @@ BOOST_AUTO_TEST_CASE(structs2) 0x40, 0x100, // S s1[0] - u256(u160(m_contractAddress)), + m_contractAddress, 0x40, // T s1[0].t 1, // length diff --git a/test/libsolidity/ABIJson/event_structs.sol b/test/libsolidity/ABIJson/event_structs.sol index af4d3b0bd701..83431907a7dd 100644 --- a/test/libsolidity/ABIJson/event_structs.sol +++ b/test/libsolidity/ABIJson/event_structs.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; T[] sub; bytes b; } struct T { uint[2] x; } diff --git a/test/libsolidity/ABIJson/global_struct.sol b/test/libsolidity/ABIJson/global_struct.sol index baffe7cc498b..11ce8246feab 100644 --- a/test/libsolidity/ABIJson/global_struct.sol +++ b/test/libsolidity/ABIJson/global_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; struct S { uint a; } contract C { function f(S calldata s) external view {} diff --git a/test/libsolidity/ABIJson/return_structs.sol b/test/libsolidity/ABIJson/return_structs.sol index 818eb2bbb7ef..9a1e493d5087 100644 --- a/test/libsolidity/ABIJson/return_structs.sol +++ b/test/libsolidity/ABIJson/return_structs.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; T[] sub; } struct T { uint[2] x; } diff --git a/test/libsolidity/ABIJson/return_structs_with_contracts.sol b/test/libsolidity/ABIJson/return_structs_with_contracts.sol index 12887d5e2354..135c3295f766 100644 --- a/test/libsolidity/ABIJson/return_structs_with_contracts.sol +++ b/test/libsolidity/ABIJson/return_structs_with_contracts.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { C[] x; C y; } function f() public returns (S memory s, C c) { diff --git a/test/libsolidity/ABIJson/structs_in_libraries.sol b/test/libsolidity/ABIJson/structs_in_libraries.sol index cb63d4a8b2b3..3f278337f0bc 100644 --- a/test/libsolidity/ABIJson/structs_in_libraries.sol +++ b/test/libsolidity/ABIJson/structs_in_libraries.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; library L { struct S { uint a; T[] sub; bytes b; } struct T { uint[2] x; } diff --git a/test/libsolidity/ABITestsCommon.h b/test/libsolidity/ABITestsCommon.h index c5eea01ca4ea..11ee83998ad8 100644 --- a/test/libsolidity/ABITestsCommon.h +++ b/test/libsolidity/ABITestsCommon.h @@ -21,17 +21,25 @@ namespace solidity::frontend::test { -static std::string const NewEncoderPragma = "pragma experimental ABIEncoderV2;\n"; - #define NEW_ENCODER(CODE) \ { \ - sourceCode = NewEncoderPragma + sourceCode; \ + string sourceCodeTmp = sourceCode; \ + sourceCode = "pragma abicoder v2;\n" + sourceCode; \ { CODE } \ + sourceCode = sourceCodeTmp; \ } -#define BOTH_ENCODERS(CODE) \ +#define OLD_ENCODER(CODE) \ { \ + string sourceCodeTmp = sourceCode; \ + sourceCode = "pragma abicoder v1;\n" + sourceCode; \ { CODE } \ + sourceCode = sourceCodeTmp; \ +} + +#define BOTH_ENCODERS(CODE) \ +{ \ + OLD_ENCODER(CODE) \ NEW_ENCODER(CODE) \ } diff --git a/test/libsolidity/ASTJSON/abstract_contract_legacy.json b/test/libsolidity/ASTJSON/abstract_contract_legacy.json deleted file mode 100644 index c13abca7f0af..000000000000 --- a/test/libsolidity/ASTJSON/abstract_contract_legacy.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 5 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": true, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "C", - "scope": 6 - }, - "children": - [ - { - "attributes": - { - "implemented": true, - "isConstructor": true, - "kind": "constructor", - "modifiers": - [ - null - ], - "name": "", - "scope": 5, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "34:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "37:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "37:4:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "23:18:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:43:1" - } - ], - "id": 6, - "name": "SourceUnit", - "src": "0:44:1" -} diff --git a/test/libsolidity/ASTJSON/abstract_contract_parseOnly.json b/test/libsolidity/ASTJSON/abstract_contract_parseOnly.json new file mode 100644 index 000000000000..1ab66ec27684 --- /dev/null +++ b/test/libsolidity/ASTJSON/abstract_contract_parseOnly.json @@ -0,0 +1,54 @@ +{ + "absolutePath": "a", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": true, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "37:4:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "34:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "37:0:1" + }, + "src": "23:18:1", + "stateMutability": "nonpayable", + "virtual": false + } + ], + "src": "0:43:1" + } + ], + "src": "0:44:1" +} diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index 3365ec436de6..8d8e1f255348 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -445,8 +445,8 @@ "tryCall": false, "typeDescriptions": { - "typeIdentifier": "t_address_payable", - "typeString": "address payable" + "typeIdentifier": "t_address", + "typeString": "address" } }, "src": "232:17:1", diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json deleted file mode 100644 index bda50fc5758d..000000000000 --- a/test/libsolidity/ASTJSON/address_payable_legacy.json +++ /dev/null @@ -1,611 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 39 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 39 - ], - "name": "C", - "scope": 40 - }, - "children": - [ - { - "attributes": - { - "constant": false, - "functionSelector": "97682884", - "mutability": "mutable", - "name": "m", - "scope": 39, - "stateVariable": true, - "storageLocation": "default", - "type": "mapping(address => address payable)", - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "type": "mapping(address => address payable)" - }, - "children": - [ - { - "attributes": - { - "name": "address", - "type": "address" - }, - "id": 1, - "name": "ElementaryTypeName", - "src": "25:7:1" - }, - { - "attributes": - { - "name": "address", - "stateMutability": "payable", - "type": "address payable" - }, - "id": 2, - "name": "ElementaryTypeName", - "src": "36:15:1" - } - ], - "id": 3, - "name": "Mapping", - "src": "17:35:1" - } - ], - "id": 4, - "name": "VariableDeclaration", - "src": "17:44:1" - }, - { - "attributes": - { - "functionSelector": "fc68521a", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 39, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "arg", - "scope": 38, - "stateVariable": false, - "storageLocation": "default", - "type": "address payable", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "address", - "stateMutability": "payable", - "type": "address payable" - }, - "id": 5, - "name": "ElementaryTypeName", - "src": "78:15:1" - } - ], - "id": 6, - "name": "VariableDeclaration", - "src": "78:19:1" - } - ], - "id": 7, - "name": "ParameterList", - "src": "77:21:1" - }, - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "r", - "scope": 38, - "stateVariable": false, - "storageLocation": "default", - "type": "address payable", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "address", - "stateMutability": "payable", - "type": "address payable" - }, - "id": 8, - "name": "ElementaryTypeName", - "src": "115:15:1" - } - ], - "id": 9, - "name": "VariableDeclaration", - "src": "115:17:1" - } - ], - "id": 10, - "name": "ParameterList", - "src": "114:19:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 12 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "a", - "scope": 37, - "stateVariable": false, - "storageLocation": "default", - "type": "address payable", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "address", - "stateMutability": "payable", - "type": "address payable" - }, - "id": 11, - "name": "ElementaryTypeName", - "src": "144:15:1" - } - ], - "id": 12, - "name": "VariableDeclaration", - "src": "144:17:1" - }, - { - "attributes": - { - "isConstant": false, - "isLValue": true, - "isPure": false, - "lValueRequested": false, - "type": "address payable" - }, - "children": - [ - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 4, - "type": "mapping(address => address payable)", - "value": "m" - }, - "id": 13, - "name": "Identifier", - "src": "164:1:1" - }, - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 6, - "type": "address payable", - "value": "arg" - }, - "id": 14, - "name": "Identifier", - "src": "166:3:1" - } - ], - "id": 15, - "name": "IndexAccess", - "src": "164:6:1" - } - ], - "id": 16, - "name": "VariableDeclarationStatement", - "src": "144:26:1" - }, - { - "children": - [ - { - "attributes": - { - "isConstant": false, - "isLValue": false, - "isPure": false, - "lValueRequested": false, - "operator": "=", - "type": "address payable" - }, - "children": - [ - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 9, - "type": "address payable", - "value": "r" - }, - "id": 17, - "name": "Identifier", - "src": "180:1:1" - }, - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 6, - "type": "address payable", - "value": "arg" - }, - "id": 18, - "name": "Identifier", - "src": "184:3:1" - } - ], - "id": 19, - "name": "Assignment", - "src": "180:7:1" - } - ], - "id": 20, - "name": "ExpressionStatement", - "src": "180:7:1" - }, - { - "attributes": - { - "assignments": - [ - 22 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "c", - "scope": 37, - "stateVariable": false, - "storageLocation": "default", - "type": "address", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "address", - "stateMutability": "nonpayable", - "type": "address" - }, - "id": 21, - "name": "ElementaryTypeName", - "src": "197:7:1" - } - ], - "id": 22, - "name": "VariableDeclaration", - "src": "197:9:1" - }, - { - "attributes": - { - "isConstant": false, - "isLValue": false, - "isPure": false, - "isStructConstructorCall": false, - "lValueRequested": false, - "names": - [ - null - ], - "tryCall": false, - "type": "address", - "type_conversion": true - }, - "children": - [ - { - "attributes": - { - "argumentTypes": - [ - { - "typeIdentifier": "t_contract$_C_$39", - "typeString": "contract C" - } - ], - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "type": "type(address)" - }, - "children": - [ - { - "attributes": - { - "name": "address" - }, - "id": 23, - "name": "ElementaryTypeName", - "src": "209:7:1" - } - ], - "id": 24, - "name": "ElementaryTypeNameExpression", - "src": "209:7:1" - }, - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": -28, - "type": "contract C", - "value": "this" - }, - "id": 25, - "name": "Identifier", - "src": "217:4:1" - } - ], - "id": 26, - "name": "FunctionCall", - "src": "209:13:1" - } - ], - "id": 27, - "name": "VariableDeclarationStatement", - "src": "197:25:1" - }, - { - "children": - [ - { - "attributes": - { - "isConstant": false, - "isLValue": false, - "isPure": false, - "lValueRequested": false, - "operator": "=", - "type": "address payable" - }, - "children": - [ - { - "attributes": - { - "isConstant": false, - "isLValue": true, - "isPure": false, - "lValueRequested": true, - "type": "address payable" - }, - "children": - [ - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 4, - "type": "mapping(address => address payable)", - "value": "m" - }, - "id": 28, - "name": "Identifier", - "src": "232:1:1" - }, - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 22, - "type": "address", - "value": "c" - }, - "id": 29, - "name": "Identifier", - "src": "234:1:1" - } - ], - "id": 30, - "name": "IndexAccess", - "src": "232:4:1" - }, - { - "attributes": - { - "isConstant": false, - "isLValue": false, - "isPure": true, - "isStructConstructorCall": false, - "lValueRequested": false, - "names": - [ - null - ], - "tryCall": false, - "type": "address payable", - "type_conversion": true - }, - "children": - [ - { - "attributes": - { - "argumentTypes": - [ - { - "typeIdentifier": "t_rational_0_by_1", - "typeString": "int_const 0" - } - ], - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "type": "type(address)" - }, - "children": - [ - { - "attributes": - { - "name": "address" - }, - "id": 31, - "name": "ElementaryTypeName", - "src": "239:7:1" - } - ], - "id": 32, - "name": "ElementaryTypeNameExpression", - "src": "239:7:1" - }, - { - "attributes": - { - "hexvalue": "30", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 0", - "value": "0" - }, - "id": 33, - "name": "Literal", - "src": "247:1:1" - } - ], - "id": 34, - "name": "FunctionCall", - "src": "239:10:1" - } - ], - "id": 35, - "name": "Assignment", - "src": "232:17:1" - } - ], - "id": 36, - "name": "ExpressionStatement", - "src": "232:17:1" - } - ], - "id": 37, - "name": "Block", - "src": "134:122:1" - } - ], - "id": 38, - "name": "FunctionDefinition", - "src": "67:189:1" - } - ], - "id": 39, - "name": "ContractDefinition", - "src": "0:258:1" - } - ], - "id": 40, - "name": "SourceUnit", - "src": "0:259:1" -} diff --git a/test/libsolidity/ASTJSON/address_payable_parseOnly.json b/test/libsolidity/ASTJSON/address_payable_parseOnly.json new file mode 100644 index 000000000000..4b769e307018 --- /dev/null +++ b/test/libsolidity/ASTJSON/address_payable_parseOnly.json @@ -0,0 +1,370 @@ +{ + "absolutePath": "a", + "id": 40, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 39, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "m", + "nodeType": "VariableDeclaration", + "src": "17:44:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 3, + "keyType": + { + "id": 1, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "25:7:1", + "typeDescriptions": {} + }, + "nodeType": "Mapping", + "src": "17:35:1", + "typeDescriptions": {}, + "valueType": + { + "id": 2, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "36:15:1", + "stateMutability": "payable", + "typeDescriptions": {} + } + }, + "visibility": "public" + }, + { + "body": + { + "id": 37, + "nodeType": "Block", + "src": "134:122:1", + "statements": + [ + { + "assignments": + [ + 12 + ], + "declarations": + [ + { + "constant": false, + "id": 12, + "mutability": "mutable", + "name": "a", + "nodeType": "VariableDeclaration", + "src": "144:17:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 11, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "144:15:1", + "stateMutability": "payable", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 16, + "initialValue": + { + "baseExpression": + { + "id": 13, + "name": "m", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "164:1:1", + "typeDescriptions": {} + }, + "id": 15, + "indexExpression": + { + "id": 14, + "name": "arg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "166:3:1", + "typeDescriptions": {} + }, + "nodeType": "IndexAccess", + "src": "164:6:1", + "typeDescriptions": {} + }, + "nodeType": "VariableDeclarationStatement", + "src": "144:26:1" + }, + { + "expression": + { + "id": 19, + "leftHandSide": + { + "id": 17, + "name": "r", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "180:1:1", + "typeDescriptions": {} + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": + { + "id": 18, + "name": "arg", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "184:3:1", + "typeDescriptions": {} + }, + "src": "180:7:1", + "typeDescriptions": {} + }, + "id": 20, + "nodeType": "ExpressionStatement", + "src": "180:7:1" + }, + { + "assignments": + [ + 22 + ], + "declarations": + [ + { + "constant": false, + "id": 22, + "mutability": "mutable", + "name": "c", + "nodeType": "VariableDeclaration", + "src": "197:9:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 21, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "197:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 27, + "initialValue": + { + "arguments": + [ + { + "id": 25, + "name": "this", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "217:4:1", + "typeDescriptions": {} + } + ], + "expression": + { + "id": 24, + "nodeType": "ElementaryTypeNameExpression", + "src": "209:7:1", + "typeDescriptions": {}, + "typeName": + { + "id": 23, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "209:7:1", + "typeDescriptions": {} + } + }, + "id": 26, + "names": [], + "nodeType": "FunctionCall", + "src": "209:13:1", + "tryCall": false, + "typeDescriptions": {} + }, + "nodeType": "VariableDeclarationStatement", + "src": "197:25:1" + }, + { + "expression": + { + "id": 35, + "leftHandSide": + { + "baseExpression": + { + "id": 28, + "name": "m", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "232:1:1", + "typeDescriptions": {} + }, + "id": 30, + "indexExpression": + { + "id": 29, + "name": "c", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "234:1:1", + "typeDescriptions": {} + }, + "nodeType": "IndexAccess", + "src": "232:4:1", + "typeDescriptions": {} + }, + "nodeType": "Assignment", + "operator": "=", + "rightHandSide": + { + "arguments": + [ + { + "hexValue": "30", + "id": 33, + "kind": "number", + "nodeType": "Literal", + "src": "247:1:1", + "typeDescriptions": {}, + "value": "0" + } + ], + "expression": + { + "id": 32, + "nodeType": "ElementaryTypeNameExpression", + "src": "239:7:1", + "typeDescriptions": {}, + "typeName": + { + "id": 31, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "239:7:1", + "typeDescriptions": {} + } + }, + "id": 34, + "names": [], + "nodeType": "FunctionCall", + "src": "239:10:1", + "tryCall": false, + "typeDescriptions": {} + }, + "src": "232:17:1", + "typeDescriptions": {} + }, + "id": 36, + "nodeType": "ExpressionStatement", + "src": "232:17:1" + } + ] + }, + "id": 38, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 6, + "mutability": "mutable", + "name": "arg", + "nodeType": "VariableDeclaration", + "src": "78:19:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 5, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "78:15:1", + "stateMutability": "payable", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "77:21:1" + }, + "returnParameters": + { + "id": 10, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 9, + "mutability": "mutable", + "name": "r", + "nodeType": "VariableDeclaration", + "src": "115:17:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 8, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "115:15:1", + "stateMutability": "payable", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "114:19:1" + }, + "src": "67:189:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:258:1" + } + ], + "src": "0:259:1" +} diff --git a/test/libsolidity/ASTJSON/array_type_name_legacy.json b/test/libsolidity/ASTJSON/array_type_name_legacy.json deleted file mode 100644 index 304d59da86f1..000000000000 --- a/test/libsolidity/ASTJSON/array_type_name_legacy.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 4 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 4 - ], - "name": "C", - "scope": 5 - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "i", - "scope": 4, - "stateVariable": true, - "storageLocation": "default", - "type": "uint256[]", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "uint256[]" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 1, - "name": "ElementaryTypeName", - "src": "13:4:1" - } - ], - "id": 2, - "name": "ArrayTypeName", - "src": "13:6:1" - } - ], - "id": 3, - "name": "VariableDeclaration", - "src": "13:8:1" - } - ], - "id": 4, - "name": "ContractDefinition", - "src": "0:24:1" - } - ], - "id": 5, - "name": "SourceUnit", - "src": "0:25:1" -} diff --git a/test/libsolidity/ASTJSON/array_type_name_parseOnly.json b/test/libsolidity/ASTJSON/array_type_name_parseOnly.json new file mode 100644 index 000000000000..18260b99e57f --- /dev/null +++ b/test/libsolidity/ASTJSON/array_type_name_parseOnly.json @@ -0,0 +1,49 @@ +{ + "absolutePath": "a", + "id": 5, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 4, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 3, + "mutability": "mutable", + "name": "i", + "nodeType": "VariableDeclaration", + "src": "13:8:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "baseType": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "13:4:1", + "typeDescriptions": {} + }, + "id": 2, + "nodeType": "ArrayTypeName", + "src": "13:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "0:24:1" + } + ], + "src": "0:25:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/call_parseOnly.json b/test/libsolidity/ASTJSON/assembly/call_parseOnly.json new file mode 100644 index 000000000000..ed8c40197887 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/call_parseOnly.json @@ -0,0 +1,152 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "37:59:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "56:34:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "67:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "70:1:1", + "type": "", + "value": "1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "73:1:1", + "type": "", + "value": "2" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "76:1:1", + "type": "", + "value": "3" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "79:1:1", + "type": "", + "value": "4" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "82:1:1", + "type": "", + "value": "5" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "85:1:1", + "type": "", + "value": "6" + } + ], + "functionName": + { + "name": "call", + "nodeType": "YulIdentifier", + "src": "62:4:1" + }, + "nodeType": "YulFunctionCall", + "src": "62:25:1" + } + ], + "functionName": + { + "name": "pop", + "nodeType": "YulIdentifier", + "src": "58:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "58:30:1" + }, + "nodeType": "YulExpressionStatement", + "src": "58:30:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "47:43:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "j", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "37:0:1" + }, + "src": "17:79:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:98:1" + } + ], + "src": "0:99:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/empty_block_parseOnly.json b/test/libsolidity/ASTJSON/assembly/empty_block_parseOnly.json new file mode 100644 index 000000000000..a11bb9893a42 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/empty_block_parseOnly.json @@ -0,0 +1,77 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "42:31:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "61:6:1", + "statements": + [ + { + "nodeType": "YulBlock", + "src": "63:2:1", + "statements": [] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "52:15:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "g", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "17:56:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:75:1" + } + ], + "src": "0:76:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/function_parseOnly.json b/test/libsolidity/ASTJSON/assembly/function_parseOnly.json new file mode 100644 index 000000000000..b7e19658850d --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/function_parseOnly.json @@ -0,0 +1,139 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "42:68:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "61:43:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "76:22:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "92:2:1", + "type": "", + "value": "20" + } + ], + "functionName": + { + "name": "blockhash", + "nodeType": "YulIdentifier", + "src": "82:9:1" + }, + "nodeType": "YulFunctionCall", + "src": "82:13:1" + } + ], + "functionName": + { + "name": "pop", + "nodeType": "YulIdentifier", + "src": "78:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "78:18:1" + }, + "nodeType": "YulExpressionStatement", + "src": "78:18:1" + } + ] + }, + "name": "g", + "nodeType": "YulFunctionDefinition", + "src": "63:35:1" + }, + { + "expression": + { + "arguments": [], + "functionName": + { + "name": "g", + "nodeType": "YulIdentifier", + "src": "99:1:1" + }, + "nodeType": "YulFunctionCall", + "src": "99:3:1" + }, + "nodeType": "YulExpressionStatement", + "src": "99:3:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "52:52:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "h", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "17:93:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:112:1" + } + ], + "src": "0:113:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/leave_parseOnly.json b/test/libsolidity/ASTJSON/assembly/leave_parseOnly.json new file mode 100644 index 000000000000..6501818985c5 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/leave_parseOnly.json @@ -0,0 +1,89 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "37:51:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "56:26:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "71:9:1", + "statements": + [ + { + "nodeType": "YulLeave", + "src": "73:5:1" + } + ] + }, + "name": "f", + "nodeType": "YulFunctionDefinition", + "src": "58:22:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "47:35:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "l", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "37:0:1" + }, + "src": "17:71:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:90:1" + } + ], + "src": "0:91:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/loop_parseOnly.json b/test/libsolidity/ASTJSON/assembly/loop_parseOnly.json new file mode 100644 index 000000000000..ea9544f6ad5f --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/loop_parseOnly.json @@ -0,0 +1,152 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "42:74:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "61:49:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "90:18:1", + "statements": + [ + { + "nodeType": "YulBreak", + "src": "92:5:1" + }, + { + "nodeType": "YulContinue", + "src": "98:8:1" + } + ] + }, + "condition": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "70:1:1", + "type": "", + "value": "1" + }, + "nodeType": "YulForLoop", + "post": + { + "nodeType": "YulBlock", + "src": "72:17:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "84:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "sload", + "nodeType": "YulIdentifier", + "src": "78:5:1" + }, + "nodeType": "YulFunctionCall", + "src": "78:8:1" + } + ], + "functionName": + { + "name": "pop", + "nodeType": "YulIdentifier", + "src": "74:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "74:13:1" + }, + "nodeType": "YulExpressionStatement", + "src": "74:13:1" + } + ] + }, + "pre": + { + "nodeType": "YulBlock", + "src": "67:2:1", + "statements": [] + }, + "src": "63:45:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "52:58:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "g", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "17:99:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:118:1" + } + ], + "src": "0:119:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions.json b/test/libsolidity/ASTJSON/assembly/nested_functions.json index f945494bad5a..687304c40142 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions.json @@ -16,7 +16,6 @@ "baseContracts": [], "contractDependencies": [], "contractKind": "contract", - "fullyImplemented": true, "id": 8, "linearizedBaseContracts": [ @@ -95,7 +94,6 @@ } ] }, - "functionSelector": "26121ff0", "id": 7, "implemented": true, "kind": "function", diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json index 393646221434..4f6f98fed8ca 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions_legacy.json @@ -25,7 +25,6 @@ null ], "contractKind": "contract", - "fullyImplemented": true, "linearizedBaseContracts": [ 8 @@ -38,7 +37,6 @@ { "attributes": { - "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, "kind": "function", diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions_parseOnly.json b/test/libsolidity/ASTJSON/assembly/nested_functions_parseOnly.json new file mode 100644 index 000000000000..3ce63897975f --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/nested_functions_parseOnly.json @@ -0,0 +1,138 @@ +{ + "absolutePath": "a", + "id": 9, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 8, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 6, + "nodeType": "Block", + "src": "57:97:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "72:78:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "94:50:1", + "statements": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "118:3:1", + "statements": [] + }, + "name": "f2", + "nodeType": "YulFunctionDefinition", + "src": "104:17:1" + }, + { + "nodeType": "YulAssignment", + "src": "130:6:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "135:1:1", + "type": "", + "value": "2" + }, + "variableNames": + [ + { + "name": "x", + "nodeType": "YulIdentifier", + "src": "130:1:1" + } + ] + } + ] + }, + "name": "f1", + "nodeType": "YulFunctionDefinition", + "src": "80:64:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 5, + "nodeType": "InlineAssembly", + "src": "63:87:1" + } + ] + }, + "id": 7, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "25:2:1" + }, + "returnParameters": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 3, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "49:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "49:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "48:8:1" + }, + "src": "15:139:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:156:1" + } + ], + "src": "0:157:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset.json b/test/libsolidity/ASTJSON/assembly/slot_offset.json index 0858fe6170d2..b8be3afe2104 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset.json @@ -4,10 +4,10 @@ { "C": [ - 11 + 12 ] }, - "id": 12, + "id": 13, "nodeType": "SourceUnit", "nodes": [ @@ -17,10 +17,10 @@ "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, - "id": 11, + "id": 12, "linearizedBaseContracts": [ - 11 + 12 ], "name": "C", "nodeType": "ContractDefinition", @@ -63,17 +63,17 @@ ], "name": "S", "nodeType": "StructDefinition", - "scope": 11, + "scope": 12, "src": "17:20:1", "visibility": "public" }, { "constant": false, - "id": 5, + "id": 6, "mutability": "mutable", "name": "s", "nodeType": "VariableDeclaration", - "scope": 11, + "scope": 12, "src": "42:3:1", "stateVariable": true, "storageLocation": "default", @@ -84,9 +84,16 @@ }, "typeName": { - "id": 4, - "name": "S", + "id": 5, "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 4, + "name": "S", + "nodeType": "IdentifierPath", + "referencedDeclaration": 3, + "src": "42:1:1" + }, "referencedDeclaration": 3, "src": "42:1:1", "typeDescriptions": @@ -100,7 +107,7 @@ { "body": { - "id": 9, + "id": 10, "nodeType": "Block", "src": "76:70:1", "statements": @@ -176,28 +183,30 @@ "externalReferences": [ { - "declaration": 5, + "declaration": 6, "isOffset": true, "isSlot": false, "src": "106:8:1", + "suffix": "offset", "valueSize": 1 }, { - "declaration": 5, + "declaration": 6, "isOffset": false, "isSlot": true, "src": "128:6:1", + "suffix": "slot", "valueSize": 1 } ], - "id": 8, + "id": 9, "nodeType": "InlineAssembly", "src": "86:54:1" } ] }, "functionSelector": "ffae15ba", - "id": 10, + "id": 11, "implemented": true, "kind": "function", "modifiers": [], @@ -205,26 +214,26 @@ "nodeType": "FunctionDefinition", "parameters": { - "id": 6, + "id": 7, "nodeType": "ParameterList", "parameters": [], "src": "61:2:1" }, "returnParameters": { - "id": 7, + "id": 8, "nodeType": "ParameterList", "parameters": [], "src": "76:0:1" }, - "scope": 11, + "scope": 12, "src": "51:95:1", "stateMutability": "pure", "virtual": false, "visibility": "public" } ], - "scope": 12, + "scope": 13, "src": "0:148:1" } ], diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json index 50747baaac47..330bb07be515 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset_legacy.json @@ -6,7 +6,7 @@ { "C": [ - 11 + 12 ] } }, @@ -28,10 +28,10 @@ "fullyImplemented": true, "linearizedBaseContracts": [ - 11 + 12 ], "name": "C", - "scope": 12 + "scope": 13 }, "children": [ @@ -40,7 +40,7 @@ { "canonicalName": "C.S", "name": "S", - "scope": 11, + "scope": 12, "visibility": "public" }, "children": @@ -85,7 +85,7 @@ "constant": false, "mutability": "mutable", "name": "s", - "scope": 11, + "scope": 12, "stateVariable": true, "storageLocation": "default", "type": "struct C.S", @@ -96,16 +96,28 @@ { "attributes": { - "name": "S", "referencedDeclaration": 3, "type": "struct C.S" }, - "id": 4, + "children": + [ + { + "attributes": + { + "name": "S", + "referencedDeclaration": 3 + }, + "id": 4, + "name": "IdentifierPath", + "src": "42:1:1" + } + ], + "id": 5, "name": "UserDefinedTypeName", "src": "42:1:1" } ], - "id": 5, + "id": 6, "name": "VariableDeclaration", "src": "42:3:1" }, @@ -121,7 +133,7 @@ null ], "name": "e", - "scope": 11, + "scope": 12, "stateMutability": "pure", "virtual": false, "visibility": "public" @@ -137,7 +149,7 @@ ] }, "children": [], - "id": 6, + "id": 7, "name": "ParameterList", "src": "61:2:1" }, @@ -150,7 +162,7 @@ ] }, "children": [], - "id": 7, + "id": 8, "name": "ParameterList", "src": "76:0:1" }, @@ -164,44 +176,46 @@ "externalReferences": [ { - "declaration": 5, + "declaration": 6, "isOffset": true, "isSlot": false, "src": "106:8:1", + "suffix": "offset", "valueSize": 1 }, { - "declaration": 5, + "declaration": 6, "isOffset": false, "isSlot": true, "src": "128:6:1", + "suffix": "slot", "valueSize": 1 } ], "operations": "{\n let x := s.offset\n let y := mul(s.slot, 2)\n}" }, "children": [], - "id": 8, + "id": 9, "name": "InlineAssembly", "src": "86:54:1" } ], - "id": 9, + "id": 10, "name": "Block", "src": "76:70:1" } ], - "id": 10, + "id": 11, "name": "FunctionDefinition", "src": "51:95:1" } ], - "id": 11, + "id": 12, "name": "ContractDefinition", "src": "0:148:1" } ], - "id": 12, + "id": 13, "name": "SourceUnit", "src": "0:149:1" } diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset_parseOnly.json b/test/libsolidity/ASTJSON/assembly/slot_offset_parseOnly.json new file mode 100644 index 000000000000..3829ea62a3d5 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/slot_offset_parseOnly.json @@ -0,0 +1,186 @@ +{ + "absolutePath": "a", + "id": 13, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 12, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "id": 3, + "members": + [ + { + "constant": false, + "id": 2, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "28:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "28:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "name": "S", + "nodeType": "StructDefinition", + "src": "17:20:1", + "visibility": "public" + }, + { + "constant": false, + "id": 6, + "mutability": "mutable", + "name": "s", + "nodeType": "VariableDeclaration", + "src": "42:3:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 5, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 4, + "name": "S", + "nodeType": "IdentifierPath", + "src": "42:1:1" + }, + "src": "42:1:1", + "typeDescriptions": {} + }, + "visibility": "internal" + }, + { + "body": + { + "id": 10, + "nodeType": "Block", + "src": "76:70:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "95:45:1", + "statements": + [ + { + "nodeType": "YulVariableDeclaration", + "src": "97:17:1", + "value": + { + "name": "s.offset", + "nodeType": "YulIdentifier", + "src": "106:8:1" + }, + "variables": + [ + { + "name": "x", + "nodeType": "YulTypedName", + "src": "101:1:1", + "type": "" + } + ] + }, + { + "nodeType": "YulVariableDeclaration", + "src": "115:23:1", + "value": + { + "arguments": + [ + { + "name": "s.slot", + "nodeType": "YulIdentifier", + "src": "128:6:1" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "136:1:1", + "type": "", + "value": "2" + } + ], + "functionName": + { + "name": "mul", + "nodeType": "YulIdentifier", + "src": "124:3:1" + }, + "nodeType": "YulFunctionCall", + "src": "124:14:1" + }, + "variables": + [ + { + "name": "y", + "nodeType": "YulTypedName", + "src": "119:1:1", + "type": "" + } + ] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 9, + "nodeType": "InlineAssembly", + "src": "86:54:1" + } + ] + }, + "id": 11, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "e", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "61:2:1" + }, + "returnParameters": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": [], + "src": "76:0:1" + }, + "src": "51:95:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:148:1" + } + ], + "src": "0:149:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json b/test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json new file mode 100644 index 000000000000..de4762668558 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json @@ -0,0 +1,93 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "37:43:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "56:18:1", + "statements": + [ + { + "nodeType": "YulVariableDeclaration", + "src": "58:14:1", + "value": + { + "kind": "string", + "nodeType": "YulLiteral", + "src": "67:5:1", + "type": "", + "value": "abc" + }, + "variables": + [ + { + "name": "x", + "nodeType": "YulTypedName", + "src": "62:1:1", + "type": "" + } + ] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "47:27:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "m", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "37:0:1" + }, + "src": "17:63:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:82:1" + } + ], + "src": "0:83:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/switch.json b/test/libsolidity/ASTJSON/assembly/switch.json index 9ae71f15c4d0..bc82ad6a2cf7 100644 --- a/test/libsolidity/ASTJSON/assembly/switch.json +++ b/test/libsolidity/ASTJSON/assembly/switch.json @@ -16,7 +16,6 @@ "baseContracts": [], "contractDependencies": [], "contractKind": "contract", - "fullyImplemented": true, "id": 6, "linearizedBaseContracts": [ @@ -180,7 +179,6 @@ } ] }, - "functionSelector": "26121ff0", "id": 5, "implemented": true, "kind": "function", diff --git a/test/libsolidity/ASTJSON/assembly/switch_default_parseOnly.json b/test/libsolidity/ASTJSON/assembly/switch_default_parseOnly.json new file mode 100644 index 000000000000..a515a33842fa --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/switch_default_parseOnly.json @@ -0,0 +1,116 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "42:58:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "61:33:1", + "statements": + [ + { + "cases": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "79:2:1", + "statements": [] + }, + "nodeType": "YulCase", + "src": "72:9:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "77:1:1", + "type": "", + "value": "0" + } + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "90:2:1", + "statements": [] + }, + "nodeType": "YulCase", + "src": "82:10:1", + "value": "default" + } + ], + "expression": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "70:1:1", + "type": "", + "value": "0" + }, + "nodeType": "YulSwitch", + "src": "63:29:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "52:42:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "g", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "17:83:1", + "stateMutability": "view", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:102:1" + } + ], + "src": "0:103:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/switch_legacy.json b/test/libsolidity/ASTJSON/assembly/switch_legacy.json index 92e2e2c61cd9..de1a1478b5bd 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_legacy.json +++ b/test/libsolidity/ASTJSON/assembly/switch_legacy.json @@ -25,7 +25,6 @@ null ], "contractKind": "contract", - "fullyImplemented": true, "linearizedBaseContracts": [ 6 @@ -38,7 +37,6 @@ { "attributes": { - "functionSelector": "26121ff0", "implemented": true, "isConstructor": false, "kind": "function", diff --git a/test/libsolidity/ASTJSON/assembly/switch_parseOnly.json b/test/libsolidity/ASTJSON/assembly/switch_parseOnly.json new file mode 100644 index 000000000000..2f09261b1f45 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/switch_parseOnly.json @@ -0,0 +1,185 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "42:154:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "61:129:1", + "statements": + [ + { + "nodeType": "YulVariableDeclaration", + "src": "75:10:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "84:1:1", + "type": "", + "value": "0" + }, + "variables": + [ + { + "name": "f", + "nodeType": "YulTypedName", + "src": "79:1:1", + "type": "" + } + ] + }, + { + "cases": + [ + { + "body": + { + "nodeType": "YulBlock", + "src": "139:10:1", + "statements": + [ + { + "nodeType": "YulAssignment", + "src": "141:6:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "146:1:1", + "type": "", + "value": "1" + }, + "variableNames": + [ + { + "name": "f", + "nodeType": "YulIdentifier", + "src": "141:1:1" + } + ] + } + ] + }, + "nodeType": "YulCase", + "src": "132:17:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "137:1:1", + "type": "", + "value": "0" + } + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "170:10:1", + "statements": + [ + { + "nodeType": "YulAssignment", + "src": "172:6:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "177:1:1", + "type": "", + "value": "2" + }, + "variableNames": + [ + { + "name": "f", + "nodeType": "YulIdentifier", + "src": "172:1:1" + } + ] + } + ] + }, + "nodeType": "YulCase", + "src": "162:18:1", + "value": "default" + } + ], + "expression": + { + "arguments": [], + "functionName": + { + "name": "calldatasize", + "nodeType": "YulIdentifier", + "src": "105:12:1" + }, + "nodeType": "YulFunctionCall", + "src": "105:14:1" + }, + "nodeType": "YulSwitch", + "src": "98:82:1" + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "52:138:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "17:179:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:198:1" + } + ], + "src": "0:199:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/var_access_parseOnly.json b/test/libsolidity/ASTJSON/assembly/var_access_parseOnly.json new file mode 100644 index 000000000000..99119f4dccf0 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/var_access_parseOnly.json @@ -0,0 +1,124 @@ +{ + "absolutePath": "a", + "id": 10, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 9, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 7, + "nodeType": "Block", + "src": "42:51:1", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "52:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "52:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 5, + "nodeType": "VariableDeclarationStatement", + "src": "52:6:1" + }, + { + "AST": + { + "nodeType": "YulBlock", + "src": "77:10:1", + "statements": + [ + { + "nodeType": "YulAssignment", + "src": "79:6:1", + "value": + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "84:1:1", + "type": "", + "value": "7" + }, + "variableNames": + [ + { + "name": "x", + "nodeType": "YulIdentifier", + "src": "79:1:1" + } + ] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 6, + "nodeType": "InlineAssembly", + "src": "68:19:1" + } + ] + }, + "id": 8, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "17:76:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:95:1" + } + ], + "src": "0:96:1" +} diff --git a/test/libsolidity/ASTJSON/constructor_legacy.json b/test/libsolidity/ASTJSON/constructor_legacy.json deleted file mode 100644 index 68a648bc9d54..000000000000 --- a/test/libsolidity/ASTJSON/constructor_legacy.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 5 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "C", - "scope": 6 - }, - "children": - [ - { - "attributes": - { - "implemented": true, - "isConstructor": true, - "kind": "constructor", - "modifiers": - [ - null - ], - "name": "", - "scope": 5, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "25:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "28:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "28:4:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "14:18:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:34:1" - } - ], - "id": 6, - "name": "SourceUnit", - "src": "0:35:1" -} diff --git a/test/libsolidity/ASTJSON/constructor_parseOnly.json b/test/libsolidity/ASTJSON/constructor_parseOnly.json new file mode 100644 index 000000000000..275f538aed09 --- /dev/null +++ b/test/libsolidity/ASTJSON/constructor_parseOnly.json @@ -0,0 +1,54 @@ +{ + "absolutePath": "a", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "28:4:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "25:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "28:0:1" + }, + "src": "14:18:1", + "stateMutability": "nonpayable", + "virtual": false + } + ], + "src": "0:34:1" + } + ], + "src": "0:35:1" +} diff --git a/test/libsolidity/ASTJSON/contract_dep_order.json b/test/libsolidity/ASTJSON/contract_dep_order.json index ff1247548972..0d41123cd0aa 100644 --- a/test/libsolidity/ASTJSON/contract_dep_order.json +++ b/test/libsolidity/ASTJSON/contract_dep_order.json @@ -53,14 +53,9 @@ { "id": 2, "name": "A", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 1, - "src": "29:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_A_$1", - "typeString": "contract A" - } + "src": "29:1:1" }, "id": 3, "nodeType": "InheritanceSpecifier", @@ -94,14 +89,9 @@ { "id": 5, "name": "B", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 4, - "src": "49:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_B_$4", - "typeString": "contract B" - } + "src": "49:1:1" }, "id": 6, "nodeType": "InheritanceSpecifier", @@ -137,14 +127,9 @@ { "id": 8, "name": "C", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 7, - "src": "69:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_C_$7", - "typeString": "contract C" - } + "src": "69:1:1" }, "id": 9, "nodeType": "InheritanceSpecifier", @@ -182,14 +167,9 @@ { "id": 11, "name": "D", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 10, - "src": "89:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_D_$10", - "typeString": "contract D" - } + "src": "89:1:1" }, "id": 12, "nodeType": "InheritanceSpecifier", diff --git a/test/libsolidity/ASTJSON/contract_dep_order_legacy.json b/test/libsolidity/ASTJSON/contract_dep_order_legacy.json deleted file mode 100644 index 20f40cb906c9..000000000000 --- a/test/libsolidity/ASTJSON/contract_dep_order_legacy.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "A": - [ - 1 - ], - "B": - [ - 4 - ], - "C": - [ - 7 - ], - "D": - [ - 10 - ], - "E": - [ - 13 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 1 - ], - "name": "A", - "nodes": - [ - null - ], - "scope": 14 - }, - "id": 1, - "name": "ContractDefinition", - "src": "0:14:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 1 - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 4, - 1 - ], - "name": "B", - "nodes": - [ - null - ], - "scope": 14 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "A", - "referencedDeclaration": 1, - "type": "contract A" - }, - "id": 2, - "name": "UserDefinedTypeName", - "src": "29:1:1" - } - ], - "id": 3, - "name": "InheritanceSpecifier", - "src": "29:1:1" - } - ], - "id": 4, - "name": "ContractDefinition", - "src": "15:19:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 1, - 4 - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 7, - 4, - 1 - ], - "name": "C", - "nodes": - [ - null - ], - "scope": 14 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "B", - "referencedDeclaration": 4, - "type": "contract B" - }, - "id": 5, - "name": "UserDefinedTypeName", - "src": "49:1:1" - } - ], - "id": 6, - "name": "InheritanceSpecifier", - "src": "49:1:1" - } - ], - "id": 7, - "name": "ContractDefinition", - "src": "35:19:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 1, - 4, - 7 - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 10, - 7, - 4, - 1 - ], - "name": "D", - "nodes": - [ - null - ], - "scope": 14 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "C", - "referencedDeclaration": 7, - "type": "contract C" - }, - "id": 8, - "name": "UserDefinedTypeName", - "src": "69:1:1" - } - ], - "id": 9, - "name": "InheritanceSpecifier", - "src": "69:1:1" - } - ], - "id": 10, - "name": "ContractDefinition", - "src": "55:19:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 1, - 4, - 7, - 10 - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 13, - 10, - 7, - 4, - 1 - ], - "name": "E", - "nodes": - [ - null - ], - "scope": 14 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "D", - "referencedDeclaration": 10, - "type": "contract D" - }, - "id": 11, - "name": "UserDefinedTypeName", - "src": "89:1:1" - } - ], - "id": 12, - "name": "InheritanceSpecifier", - "src": "89:1:1" - } - ], - "id": 13, - "name": "ContractDefinition", - "src": "75:19:1" - } - ], - "id": 14, - "name": "SourceUnit", - "src": "0:95:1" -} diff --git a/test/libsolidity/ASTJSON/contract_dep_order_parseOnly.json b/test/libsolidity/ASTJSON/contract_dep_order_parseOnly.json new file mode 100644 index 000000000000..1ee7586b0572 --- /dev/null +++ b/test/libsolidity/ASTJSON/contract_dep_order_parseOnly.json @@ -0,0 +1,120 @@ +{ + "absolutePath": "a", + "id": 14, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 1, + "name": "A", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "0:14:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 2, + "name": "A", + "nodeType": "IdentifierPath", + "src": "29:1:1" + }, + "id": 3, + "nodeType": "InheritanceSpecifier", + "src": "29:1:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 4, + "name": "B", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "15:19:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 5, + "name": "B", + "nodeType": "IdentifierPath", + "src": "49:1:1" + }, + "id": 6, + "nodeType": "InheritanceSpecifier", + "src": "49:1:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 7, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "35:19:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 8, + "name": "C", + "nodeType": "IdentifierPath", + "src": "69:1:1" + }, + "id": 9, + "nodeType": "InheritanceSpecifier", + "src": "69:1:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 10, + "name": "D", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "55:19:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 11, + "name": "D", + "nodeType": "IdentifierPath", + "src": "89:1:1" + }, + "id": 12, + "nodeType": "InheritanceSpecifier", + "src": "89:1:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 13, + "name": "E", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "75:19:1" + } + ], + "src": "0:95:1" +} diff --git a/test/libsolidity/ASTJSON/documentation.sol b/test/libsolidity/ASTJSON/documentation.sol index 089f81e0b581..4d354dd26a1d 100644 --- a/test/libsolidity/ASTJSON/documentation.sol +++ b/test/libsolidity/ASTJSON/documentation.sol @@ -1,14 +1,14 @@ -// ---- SOURCE: a +==== Source: a ==== /**This contract is empty*/ contract C {} -// ---- SOURCE: b +==== Source: b ==== /**This contract is empty and has a line-breaking comment.*/ contract C {} -// ---- SOURCE: c +==== Source: c ==== contract C { /** Some comment on state var.*/ uint public state; diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json deleted file mode 100644 index 4284c4f8f6c0..000000000000 --- a/test/libsolidity/ASTJSON/documentation_legacy.json +++ /dev/null @@ -1,242 +0,0 @@ -{ - "attributes": - { - "absolutePath": "c", - "exportedSymbols": - { - "C": - [ - 23 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 23 - ], - "name": "C", - "scope": 24 - }, - "children": - [ - { - "attributes": - { - "constant": false, - "functionSelector": "c19d93fb", - "mutability": "mutable", - "name": "state", - "scope": 23, - "stateVariable": true, - "storageLocation": "default", - "type": "uint256", - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 8, - "name": "ElementaryTypeName", - "src": "48:4:3" - }, - { - "attributes": - { - "text": "Some comment on state var." - }, - "id": 7, - "name": "StructuredDocumentation", - "src": "15:32:3" - } - ], - "id": 9, - "name": "VariableDeclaration", - "src": "48:17:3" - }, - { - "attributes": - { - "anonymous": false, - "name": "Evt" - }, - "children": - [ - { - "attributes": - { - "text": "Some comment on Evt." - }, - "id": 10, - "name": "StructuredDocumentation", - "src": "69:26:3" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 11, - "name": "ParameterList", - "src": "105:2:3" - } - ], - "id": 12, - "name": "EventDefinition", - "src": "96:12:3" - }, - { - "attributes": - { - "name": "mod", - "virtual": false, - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "text": "Some comment on mod." - }, - "id": 13, - "name": "StructuredDocumentation", - "src": "111:26:3" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 14, - "name": "ParameterList", - "src": "150:2:3" - }, - { - "children": - [ - { - "id": 15, - "name": "PlaceholderStatement", - "src": "155:1:3" - } - ], - "id": 16, - "name": "Block", - "src": "153:6:3" - } - ], - "id": 17, - "name": "ModifierDefinition", - "src": "138:21:3" - }, - { - "attributes": - { - "functionSelector": "a4a2c40b", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "fn", - "scope": 23, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "text": "Some comment on fn." - }, - "id": 18, - "name": "StructuredDocumentation", - "src": "162:25:3" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 19, - "name": "ParameterList", - "src": "199:2:3" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 20, - "name": "ParameterList", - "src": "209:0:3" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 21, - "name": "Block", - "src": "209:2:3" - } - ], - "id": 22, - "name": "FunctionDefinition", - "src": "188:23:3" - } - ], - "id": 23, - "name": "ContractDefinition", - "src": "0:213:3" - } - ], - "id": 24, - "name": "SourceUnit", - "src": "0:214:3" -} diff --git a/test/libsolidity/ASTJSON/documentation_parseOnly.json b/test/libsolidity/ASTJSON/documentation_parseOnly.json new file mode 100644 index 000000000000..aaf7c160fa8a --- /dev/null +++ b/test/libsolidity/ASTJSON/documentation_parseOnly.json @@ -0,0 +1,195 @@ +[ +{ + "absolutePath": "a", + "id": 3, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": + { + "id": 1, + "nodeType": "StructuredDocumentation", + "src": "0:27:1", + "text": "This contract is empty" + }, + "id": 2, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "28:13:1" + } + ], + "src": "28:14:1" +}, +{ + "absolutePath": "b", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "documentation": + { + "id": 4, + "nodeType": "StructuredDocumentation", + "src": "0:61:2", + "text": "This contract is empty\nand has a line-breaking comment." + }, + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "62:13:2" + } + ], + "src": "62:14:2" +}, +{ + "absolutePath": "c", + "id": 24, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 23, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 9, + "mutability": "mutable", + "name": "state", + "nodeType": "VariableDeclaration", + "src": "48:17:3", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 8, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "48:4:3", + "typeDescriptions": {} + }, + "visibility": "public" + }, + { + "anonymous": false, + "documentation": + { + "id": 10, + "nodeType": "StructuredDocumentation", + "src": "69:26:3", + "text": "Some comment on Evt." + }, + "id": 12, + "name": "Evt", + "nodeType": "EventDefinition", + "parameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "105:2:3" + }, + "src": "96:12:3" + }, + { + "body": + { + "id": 16, + "nodeType": "Block", + "src": "153:6:3", + "statements": + [ + { + "id": 15, + "nodeType": "PlaceholderStatement", + "src": "155:1:3" + } + ] + }, + "documentation": + { + "id": 13, + "nodeType": "StructuredDocumentation", + "src": "111:26:3", + "text": "Some comment on mod." + }, + "id": 17, + "name": "mod", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 14, + "nodeType": "ParameterList", + "parameters": [], + "src": "150:2:3" + }, + "src": "138:21:3", + "virtual": false, + "visibility": "internal" + }, + { + "body": + { + "id": 21, + "nodeType": "Block", + "src": "209:2:3", + "statements": [] + }, + "documentation": + { + "id": 18, + "nodeType": "StructuredDocumentation", + "src": "162:25:3", + "text": "Some comment on fn." + }, + "id": 22, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "fn", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 19, + "nodeType": "ParameterList", + "parameters": [], + "src": "199:2:3" + }, + "returnParameters": + { + "id": 20, + "nodeType": "ParameterList", + "parameters": [], + "src": "209:0:3" + }, + "src": "188:23:3", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:213:3" + } + ], + "src": "0:214:3" +} +] diff --git a/test/libsolidity/ASTJSON/enum_value_legacy.json b/test/libsolidity/ASTJSON/enum_value_legacy.json deleted file mode 100644 index 9ae5e1dad7e0..000000000000 --- a/test/libsolidity/ASTJSON/enum_value_legacy.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 4 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 4 - ], - "name": "C", - "scope": 5 - }, - "children": - [ - { - "attributes": - { - "canonicalName": "C.E", - "name": "E" - }, - "children": - [ - { - "attributes": - { - "name": "A" - }, - "id": 1, - "name": "EnumValue", - "src": "22:1:1" - }, - { - "attributes": - { - "name": "B" - }, - "id": 2, - "name": "EnumValue", - "src": "25:1:1" - } - ], - "id": 3, - "name": "EnumDefinition", - "src": "13:15:1" - } - ], - "id": 4, - "name": "ContractDefinition", - "src": "0:30:1" - } - ], - "id": 5, - "name": "SourceUnit", - "src": "0:31:1" -} diff --git a/test/libsolidity/ASTJSON/enum_value_parseOnly.json b/test/libsolidity/ASTJSON/enum_value_parseOnly.json new file mode 100644 index 000000000000..261e06e99295 --- /dev/null +++ b/test/libsolidity/ASTJSON/enum_value_parseOnly.json @@ -0,0 +1,43 @@ +{ + "absolutePath": "a", + "id": 5, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 4, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "id": 3, + "members": + [ + { + "id": 1, + "name": "A", + "nodeType": "EnumValue", + "src": "22:1:1" + }, + { + "id": 2, + "name": "B", + "nodeType": "EnumValue", + "src": "25:1:1" + } + ], + "name": "E", + "nodeType": "EnumDefinition", + "src": "13:15:1" + } + ], + "src": "0:30:1" + } + ], + "src": "0:31:1" +} diff --git a/test/libsolidity/ASTJSON/event_definition_legacy.json b/test/libsolidity/ASTJSON/event_definition_legacy.json deleted file mode 100644 index 7adf0d038940..000000000000 --- a/test/libsolidity/ASTJSON/event_definition_legacy.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 3 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 3 - ], - "name": "C", - "scope": 4 - }, - "children": - [ - { - "attributes": - { - "anonymous": false, - "name": "E" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "20:2:1" - } - ], - "id": 2, - "name": "EventDefinition", - "src": "13:10:1" - } - ], - "id": 3, - "name": "ContractDefinition", - "src": "0:25:1" - } - ], - "id": 4, - "name": "SourceUnit", - "src": "0:26:1" -} diff --git a/test/libsolidity/ASTJSON/event_definition_parseOnly.json b/test/libsolidity/ASTJSON/event_definition_parseOnly.json new file mode 100644 index 000000000000..02099388b98d --- /dev/null +++ b/test/libsolidity/ASTJSON/event_definition_parseOnly.json @@ -0,0 +1,36 @@ +{ + "absolutePath": "a", + "id": 4, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 3, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "anonymous": false, + "id": 2, + "name": "E", + "nodeType": "EventDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "20:2:1" + }, + "src": "13:10:1" + } + ], + "src": "0:25:1" + } + ], + "src": "0:26:1" +} diff --git a/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json b/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json deleted file mode 100644 index 8ac17607e6c7..000000000000 --- a/test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 9 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 9 - ], - "name": "C", - "scope": 10 - }, - "children": - [ - { - "attributes": - { - "implemented": true, - "isConstructor": false, - "kind": "receive", - "modifiers": - [ - null - ], - "name": "", - "scope": 9, - "stateMutability": "payable", - "virtual": false, - "visibility": "external" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "22:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "42:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "42:5:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "15:32:1" - }, - { - "attributes": - { - "implemented": true, - "isConstructor": false, - "kind": "fallback", - "modifiers": - [ - null - ], - "name": "", - "scope": 9, - "stateMutability": "payable", - "virtual": false, - "visibility": "external" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 5, - "name": "ParameterList", - "src": "58:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 6, - "name": "ParameterList", - "src": "78:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 7, - "name": "Block", - "src": "78:5:1" - } - ], - "id": 8, - "name": "FunctionDefinition", - "src": "50:33:1" - } - ], - "id": 9, - "name": "ContractDefinition", - "src": "0:85:1" - } - ], - "id": 10, - "name": "SourceUnit", - "src": "0:86:1" -} diff --git a/test/libsolidity/ASTJSON/fallback_and_reveice_ether_parseOnly.json b/test/libsolidity/ASTJSON/fallback_and_reveice_ether_parseOnly.json new file mode 100644 index 000000000000..838760bcd8e0 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_and_reveice_ether_parseOnly.json @@ -0,0 +1,88 @@ +{ + "absolutePath": "a", + "id": 10, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 9, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "42:5:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "receive", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "22:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "15:32:1", + "stateMutability": "payable", + "virtual": false, + "visibility": "external" + }, + { + "body": + { + "id": 7, + "nodeType": "Block", + "src": "78:5:1", + "statements": [] + }, + "id": 8, + "implemented": true, + "kind": "fallback", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 5, + "nodeType": "ParameterList", + "parameters": [], + "src": "58:2:1" + }, + "returnParameters": + { + "id": 6, + "nodeType": "ParameterList", + "parameters": [], + "src": "78:0:1" + }, + "src": "50:33:1", + "stateMutability": "payable", + "virtual": false, + "visibility": "external" + } + ], + "src": "0:85:1" + } + ], + "src": "0:86:1" +} diff --git a/test/libsolidity/ASTJSON/fallback_legacy.json b/test/libsolidity/ASTJSON/fallback_legacy.json deleted file mode 100644 index 1ee8bf6b30ee..000000000000 --- a/test/libsolidity/ASTJSON/fallback_legacy.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 5 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "C", - "scope": 6 - }, - "children": - [ - { - "attributes": - { - "implemented": true, - "isConstructor": false, - "kind": "fallback", - "modifiers": - [ - null - ], - "name": "", - "scope": 5, - "stateMutability": "payable", - "virtual": false, - "visibility": "external" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "43:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "43:5:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "15:33:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:50:1" - } - ], - "id": 6, - "name": "SourceUnit", - "src": "0:51:1" -} diff --git a/test/libsolidity/ASTJSON/fallback_parseOnly.json b/test/libsolidity/ASTJSON/fallback_parseOnly.json new file mode 100644 index 000000000000..9a7a53e14ab8 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_parseOnly.json @@ -0,0 +1,55 @@ +{ + "absolutePath": "a", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "43:5:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "fallback", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "43:0:1" + }, + "src": "15:33:1", + "stateMutability": "payable", + "virtual": false, + "visibility": "external" + } + ], + "src": "0:50:1" + } + ], + "src": "0:51:1" +} diff --git a/test/libsolidity/ASTJSON/fallback_payable_legacy.json b/test/libsolidity/ASTJSON/fallback_payable_legacy.json deleted file mode 100644 index 69e25ab6e18a..000000000000 --- a/test/libsolidity/ASTJSON/fallback_payable_legacy.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 5 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "C", - "scope": 6 - }, - "children": - [ - { - "attributes": - { - "implemented": true, - "isConstructor": false, - "kind": "fallback", - "modifiers": - [ - null - ], - "name": "", - "scope": 5, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "external" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "22:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "34:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "34:2:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "14:22:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:38:1" - } - ], - "id": 6, - "name": "SourceUnit", - "src": "0:39:1" -} diff --git a/test/libsolidity/ASTJSON/fallback_payable_parseOnly.json b/test/libsolidity/ASTJSON/fallback_payable_parseOnly.json new file mode 100644 index 000000000000..17f78c8d617c --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_payable_parseOnly.json @@ -0,0 +1,55 @@ +{ + "absolutePath": "a", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "34:2:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "fallback", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "22:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "34:0:1" + }, + "src": "14:22:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "external" + } + ], + "src": "0:38:1" + } + ], + "src": "0:39:1" +} diff --git a/test/libsolidity/ASTJSON/function_type_legacy.json b/test/libsolidity/ASTJSON/function_type_legacy.json deleted file mode 100644 index 627ce45f0878..000000000000 --- a/test/libsolidity/ASTJSON/function_type_legacy.json +++ /dev/null @@ -1,266 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 17 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 17 - ], - "name": "C", - "scope": 18 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "d6cd4974", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 17, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "x", - "scope": 16, - "stateVariable": false, - "storageLocation": "default", - "type": "function () payable external returns (uint256)", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "stateMutability": "payable", - "type": "function () payable external returns (uint256)", - "visibility": "external" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "32:2:1" - }, - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "", - "scope": 5, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 2, - "name": "ElementaryTypeName", - "src": "61:4:1" - } - ], - "id": 3, - "name": "VariableDeclaration", - "src": "61:4:1" - } - ], - "id": 4, - "name": "ParameterList", - "src": "60:6:1" - } - ], - "id": 5, - "name": "FunctionTypeName", - "src": "24:44:1" - } - ], - "id": 6, - "name": "VariableDeclaration", - "src": "24:44:1" - } - ], - "id": 7, - "name": "ParameterList", - "src": "23:46:1" - }, - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "", - "scope": 16, - "stateVariable": false, - "storageLocation": "default", - "type": "function () view external returns (uint256)", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "stateMutability": "view", - "type": "function () view external returns (uint256)", - "visibility": "external" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 8, - "name": "ParameterList", - "src": "87:2:1" - }, - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "", - "scope": 12, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 9, - "name": "ElementaryTypeName", - "src": "113:4:1" - } - ], - "id": 10, - "name": "VariableDeclaration", - "src": "113:4:1" - } - ], - "id": 11, - "name": "ParameterList", - "src": "112:6:1" - } - ], - "id": 12, - "name": "FunctionTypeName", - "src": "79:40:1" - } - ], - "id": 13, - "name": "VariableDeclaration", - "src": "79:40:1" - } - ], - "id": 14, - "name": "ParameterList", - "src": "78:41:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 15, - "name": "Block", - "src": "120:2:1" - } - ], - "id": 16, - "name": "FunctionDefinition", - "src": "13:109:1" - } - ], - "id": 17, - "name": "ContractDefinition", - "src": "0:124:1" - } - ], - "id": 18, - "name": "SourceUnit", - "src": "0:125:1" -} diff --git a/test/libsolidity/ASTJSON/function_type_parseOnly.json b/test/libsolidity/ASTJSON/function_type_parseOnly.json new file mode 100644 index 000000000000..3575cbee1611 --- /dev/null +++ b/test/libsolidity/ASTJSON/function_type_parseOnly.json @@ -0,0 +1,173 @@ +{ + "absolutePath": "a", + "id": 18, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 17, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 15, + "nodeType": "Block", + "src": "120:2:1", + "statements": [] + }, + "id": 16, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 6, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "24:44:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 5, + "nodeType": "FunctionTypeName", + "parameterTypes": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "32:2:1" + }, + "returnParameterTypes": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 3, + "mutability": "mutable", + "name": "", + "nodeType": "VariableDeclaration", + "src": "61:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 2, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "61:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "60:6:1" + }, + "src": "24:44:1", + "stateMutability": "payable", + "typeDescriptions": {}, + "visibility": "external" + }, + "visibility": "internal" + } + ], + "src": "23:46:1" + }, + "returnParameters": + { + "id": 14, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 13, + "mutability": "mutable", + "name": "", + "nodeType": "VariableDeclaration", + "src": "79:40:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 12, + "nodeType": "FunctionTypeName", + "parameterTypes": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": [], + "src": "87:2:1" + }, + "returnParameterTypes": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 10, + "mutability": "mutable", + "name": "", + "nodeType": "VariableDeclaration", + "src": "113:4:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 9, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "113:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "112:6:1" + }, + "src": "79:40:1", + "stateMutability": "view", + "typeDescriptions": {}, + "visibility": "external" + }, + "visibility": "internal" + } + ], + "src": "78:41:1" + }, + "src": "13:109:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:124:1" + } + ], + "src": "0:125:1" +} diff --git a/test/libsolidity/ASTJSON/global_enum_legacy.json b/test/libsolidity/ASTJSON/global_enum_legacy.json deleted file mode 100644 index f29649018688..000000000000 --- a/test/libsolidity/ASTJSON/global_enum_legacy.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "E": - [ - 2 - ] - } - }, - "children": - [ - { - "attributes": - { - "canonicalName": "E", - "name": "E" - }, - "children": - [ - { - "attributes": - { - "name": "A" - }, - "id": 1, - "name": "EnumValue", - "src": "9:1:1" - } - ], - "id": 2, - "name": "EnumDefinition", - "src": "0:12:1" - } - ], - "id": 3, - "name": "SourceUnit", - "src": "0:13:1" -} diff --git a/test/libsolidity/ASTJSON/global_enum_parseOnly.json b/test/libsolidity/ASTJSON/global_enum_parseOnly.json new file mode 100644 index 000000000000..389b1f4e2c73 --- /dev/null +++ b/test/libsolidity/ASTJSON/global_enum_parseOnly.json @@ -0,0 +1,24 @@ +{ + "absolutePath": "a", + "id": 3, + "nodeType": "SourceUnit", + "nodes": + [ + { + "id": 2, + "members": + [ + { + "id": 1, + "name": "A", + "nodeType": "EnumValue", + "src": "9:1:1" + } + ], + "name": "E", + "nodeType": "EnumDefinition", + "src": "0:12:1" + } + ], + "src": "0:13:1" +} diff --git a/test/libsolidity/ASTJSON/global_struct_legacy.json b/test/libsolidity/ASTJSON/global_struct_legacy.json deleted file mode 100644 index 09f5d2de61c0..000000000000 --- a/test/libsolidity/ASTJSON/global_struct_legacy.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "S": - [ - 3 - ] - } - }, - "children": - [ - { - "attributes": - { - "canonicalName": "S", - "name": "S", - "scope": 4, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "a", - "scope": 3, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "uint256", - "type": "uint256" - }, - "id": 1, - "name": "ElementaryTypeName", - "src": "11:7:1" - } - ], - "id": 2, - "name": "VariableDeclaration", - "src": "11:9:1" - } - ], - "id": 3, - "name": "StructDefinition", - "src": "0:23:1" - } - ], - "id": 4, - "name": "SourceUnit", - "src": "0:24:1" -} diff --git a/test/libsolidity/ASTJSON/global_struct_parseOnly.json b/test/libsolidity/ASTJSON/global_struct_parseOnly.json new file mode 100644 index 000000000000..8cd50853dcd0 --- /dev/null +++ b/test/libsolidity/ASTJSON/global_struct_parseOnly.json @@ -0,0 +1,39 @@ +{ + "absolutePath": "a", + "id": 4, + "nodeType": "SourceUnit", + "nodes": + [ + { + "id": 3, + "members": + [ + { + "constant": false, + "id": 2, + "mutability": "mutable", + "name": "a", + "nodeType": "VariableDeclaration", + "src": "11:9:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 1, + "name": "uint256", + "nodeType": "ElementaryTypeName", + "src": "11:7:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "name": "S", + "nodeType": "StructDefinition", + "src": "0:23:1", + "visibility": "public" + } + ], + "src": "0:24:1" +} diff --git a/test/libsolidity/ASTJSON/inheritance_specifier.json b/test/libsolidity/ASTJSON/inheritance_specifier.json index 40acc5d2a81e..0e756f6d01f4 100644 --- a/test/libsolidity/ASTJSON/inheritance_specifier.json +++ b/test/libsolidity/ASTJSON/inheritance_specifier.json @@ -41,14 +41,9 @@ { "id": 2, "name": "C1", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 1, - "src": "30:2:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_C1_$1", - "typeString": "contract C1" - } + "src": "30:2:1" }, "id": 3, "nodeType": "InheritanceSpecifier", diff --git a/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json b/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json deleted file mode 100644 index 4d6f0ffec3f2..000000000000 --- a/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C1": - [ - 1 - ], - "C2": - [ - 4 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 1 - ], - "name": "C1", - "nodes": - [ - null - ], - "scope": 5 - }, - "id": 1, - "name": "ContractDefinition", - "src": "0:14:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 1 - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 4, - 1 - ], - "name": "C2", - "nodes": - [ - null - ], - "scope": 5 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "C1", - "referencedDeclaration": 1, - "type": "contract C1" - }, - "id": 2, - "name": "UserDefinedTypeName", - "src": "30:2:1" - } - ], - "id": 3, - "name": "InheritanceSpecifier", - "src": "30:2:1" - } - ], - "id": 4, - "name": "ContractDefinition", - "src": "15:20:1" - } - ], - "id": 5, - "name": "SourceUnit", - "src": "0:36:1" -} diff --git a/test/libsolidity/ASTJSON/inheritance_specifier_parseOnly.json b/test/libsolidity/ASTJSON/inheritance_specifier_parseOnly.json new file mode 100644 index 000000000000..7054f573b320 --- /dev/null +++ b/test/libsolidity/ASTJSON/inheritance_specifier_parseOnly.json @@ -0,0 +1,45 @@ +{ + "absolutePath": "a", + "id": 5, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 1, + "name": "C1", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "0:14:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 2, + "name": "C1", + "nodeType": "IdentifierPath", + "src": "30:2:1" + }, + "id": 3, + "nodeType": "InheritanceSpecifier", + "src": "30:2:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 4, + "name": "C2", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "15:20:1" + } + ], + "src": "0:36:1" +} diff --git a/test/libsolidity/ASTJSON/license_legacy.json b/test/libsolidity/ASTJSON/license_legacy.json deleted file mode 100644 index 03e6b60560a3..000000000000 --- a/test/libsolidity/ASTJSON/license_legacy.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 1 - ] - }, - "license": "GPL-3.0" - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 1 - ], - "name": "C", - "nodes": - [ - null - ], - "scope": 2 - }, - "id": 1, - "name": "ContractDefinition", - "src": "36:13:1" - } - ], - "id": 2, - "name": "SourceUnit", - "src": "36:14:1" -} diff --git a/test/libsolidity/ASTJSON/license_parseOnly.json b/test/libsolidity/ASTJSON/license_parseOnly.json new file mode 100644 index 000000000000..a38f05a376ea --- /dev/null +++ b/test/libsolidity/ASTJSON/license_parseOnly.json @@ -0,0 +1,21 @@ +{ + "absolutePath": "a", + "id": 2, + "license": "GPL-3.0", + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 1, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "36:13:1" + } + ], + "src": "36:14:1" +} diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json deleted file mode 100644 index 46e5b0417add..000000000000 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json +++ /dev/null @@ -1,203 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "c": - [ - 11 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 11 - ], - "name": "c", - "scope": 12 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 11, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "33:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 4 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "a", - "scope": 9, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 3, - "name": "ElementaryTypeName", - "src": "35:4:1" - } - ], - "id": 4, - "name": "VariableDeclaration", - "src": "35:6:1" - }, - { - "attributes": - { - "commonType": - { - "typeIdentifier": "t_rational_5_by_1", - "typeString": "int_const 5" - }, - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "operator": "+", - "type": "int_const 5" - }, - "children": - [ - { - "attributes": - { - "hexvalue": "32", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 2", - "value": "2" - }, - "id": 5, - "name": "Literal", - "src": "44:1:1" - }, - { - "attributes": - { - "hexvalue": "33", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 3", - "value": "3" - }, - "id": 6, - "name": "Literal", - "src": "48:1:1" - } - ], - "id": 7, - "name": "BinaryOperation", - "src": "44:5:1" - } - ], - "id": 8, - "name": "VariableDeclarationStatement", - "src": "35:14:1" - } - ], - "id": 9, - "name": "Block", - "src": "33:19:1" - } - ], - "id": 10, - "name": "FunctionDefinition", - "src": "13:39:1" - } - ], - "id": 11, - "name": "ContractDefinition", - "src": "0:54:1" - } - ], - "id": 12, - "name": "SourceUnit", - "src": "0:55:1" -} diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation_parseOnly.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation_parseOnly.json new file mode 100644 index 000000000000..51ebaf12db6f --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation_parseOnly.json @@ -0,0 +1,118 @@ +{ + "absolutePath": "a", + "id": 12, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 11, + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "33:19:1", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "a", + "nodeType": "VariableDeclaration", + "src": "35:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "35:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 8, + "initialValue": + { + "commonType": {}, + "id": 7, + "leftExpression": + { + "hexValue": "32", + "id": 5, + "kind": "number", + "nodeType": "Literal", + "src": "44:1:1", + "typeDescriptions": {}, + "value": "2" + }, + "nodeType": "BinaryOperation", + "operator": "+", + "rightExpression": + { + "hexValue": "33", + "id": 6, + "kind": "number", + "nodeType": "Literal", + "src": "48:1:1", + "typeDescriptions": {}, + "value": "3" + }, + "src": "44:5:1", + "typeDescriptions": {} + }, + "nodeType": "VariableDeclarationStatement", + "src": "35:14:1" + } + ] + }, + "id": 10, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "src": "13:39:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:54:1" + } + ], + "src": "0:55:1" +} diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json deleted file mode 100644 index ced331a6dda5..000000000000 --- a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "c": - [ - 15 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 15 - ], - "name": "c", - "scope": 16 - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "a", - "scope": 15, - "stateVariable": true, - "storageLocation": "default", - "type": "uint256[]", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "uint256[]" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 1, - "name": "ElementaryTypeName", - "src": "13:4:1" - } - ], - "id": 2, - "name": "ArrayTypeName", - "src": "13:6:1" - } - ], - "id": 3, - "name": "VariableDeclaration", - "src": "13:8:1" - }, - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 15, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 4, - "name": "ParameterList", - "src": "33:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 5, - "name": "ParameterList", - "src": "43:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 10 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "b", - "scope": 13, - "stateVariable": false, - "storageLocation": "storage", - "type": "uint256[]", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "uint256[]" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 8, - "name": "ElementaryTypeName", - "src": "45:4:1" - } - ], - "id": 9, - "name": "ArrayTypeName", - "src": "45:6:1" - } - ], - "id": 10, - "name": "VariableDeclaration", - "src": "45:16:1" - }, - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 3, - "type": "uint256[] storage ref", - "value": "a" - }, - "id": 11, - "name": "Identifier", - "src": "64:1:1" - } - ], - "id": 12, - "name": "VariableDeclarationStatement", - "src": "45:20:1" - } - ], - "id": 13, - "name": "Block", - "src": "43:25:1" - } - ], - "id": 14, - "name": "FunctionDefinition", - "src": "23:45:1" - } - ], - "id": 15, - "name": "ContractDefinition", - "src": "0:70:1" - } - ], - "id": 16, - "name": "SourceUnit", - "src": "0:71:1" -} diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier_parseOnly.json b/test/libsolidity/ASTJSON/long_type_name_identifier_parseOnly.json new file mode 100644 index 000000000000..bc94cc152eea --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_identifier_parseOnly.json @@ -0,0 +1,132 @@ +{ + "absolutePath": "a", + "id": 16, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 15, + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 3, + "mutability": "mutable", + "name": "a", + "nodeType": "VariableDeclaration", + "src": "13:8:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "baseType": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "13:4:1", + "typeDescriptions": {} + }, + "id": 2, + "nodeType": "ArrayTypeName", + "src": "13:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + }, + { + "body": + { + "id": 13, + "nodeType": "Block", + "src": "43:25:1", + "statements": + [ + { + "assignments": + [ + 10 + ], + "declarations": + [ + { + "constant": false, + "id": 10, + "mutability": "mutable", + "name": "b", + "nodeType": "VariableDeclaration", + "src": "45:16:1", + "stateVariable": false, + "storageLocation": "storage", + "typeDescriptions": {}, + "typeName": + { + "baseType": + { + "id": 8, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "45:4:1", + "typeDescriptions": {} + }, + "id": 9, + "nodeType": "ArrayTypeName", + "src": "45:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 12, + "initialValue": + { + "id": 11, + "name": "a", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "64:1:1", + "typeDescriptions": {} + }, + "nodeType": "VariableDeclarationStatement", + "src": "45:20:1" + } + ] + }, + "id": 14, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 4, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:2:1" + }, + "returnParameters": + { + "id": 5, + "nodeType": "ParameterList", + "parameters": [], + "src": "43:0:1" + }, + "src": "23:45:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:70:1" + } + ], + "src": "0:71:1" +} diff --git a/test/libsolidity/ASTJSON/mappings.json b/test/libsolidity/ASTJSON/mappings.json index b1f150f10334..ce72bbb111fc 100644 --- a/test/libsolidity/ASTJSON/mappings.json +++ b/test/libsolidity/ASTJSON/mappings.json @@ -4,10 +4,10 @@ { "C": [ - 17 + 19 ] }, - "id": 18, + "id": 20, "nodeType": "SourceUnit", "nodes": [ @@ -17,10 +17,10 @@ "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, - "id": 17, + "id": 19, "linearizedBaseContracts": [ - 17 + 19 ], "name": "C", "nodeType": "ContractDefinition", @@ -56,32 +56,39 @@ }, { "constant": false, - "id": 8, + "id": 9, "mutability": "mutable", "name": "a", "nodeType": "VariableDeclaration", - "scope": 17, + "scope": 19, "src": "40:20:1", "stateVariable": true, "storageLocation": "default", "typeDescriptions": { - "typeIdentifier": "t_mapping$_t_contract$_C_$17_$_t_bool_$", + "typeIdentifier": "t_mapping$_t_contract$_C_$19_$_t_bool_$", "typeString": "mapping(contract C => bool)" }, "typeName": { - "id": 7, + "id": 8, "keyType": { - "id": 5, - "name": "C", + "id": 6, "nodeType": "UserDefinedTypeName", - "referencedDeclaration": 17, + "pathNode": + { + "id": 5, + "name": "C", + "nodeType": "IdentifierPath", + "referencedDeclaration": 19, + "src": "48:1:1" + }, + "referencedDeclaration": 19, "src": "48:1:1", "typeDescriptions": { - "typeIdentifier": "t_contract$_C_$17", + "typeIdentifier": "t_contract$_C_$19", "typeString": "contract C" } }, @@ -89,12 +96,12 @@ "src": "40:18:1", "typeDescriptions": { - "typeIdentifier": "t_mapping$_t_contract$_C_$17_$_t_bool_$", + "typeIdentifier": "t_mapping$_t_contract$_C_$19_$_t_bool_$", "typeString": "mapping(contract C => bool)" }, "valueType": { - "id": 6, + "id": 7, "name": "bool", "nodeType": "ElementaryTypeName", "src": "53:4:1", @@ -109,11 +116,11 @@ }, { "constant": false, - "id": 12, + "id": 13, "mutability": "mutable", "name": "b", "nodeType": "VariableDeclaration", - "scope": 17, + "scope": 19, "src": "66:26:1", "stateVariable": true, "storageLocation": "default", @@ -124,10 +131,10 @@ }, "typeName": { - "id": 11, + "id": 12, "keyType": { - "id": 9, + "id": 10, "name": "address", "nodeType": "ElementaryTypeName", "src": "74:7:1", @@ -146,7 +153,7 @@ }, "valueType": { - "id": 10, + "id": 11, "name": "bool", "nodeType": "ElementaryTypeName", "src": "85:4:1", @@ -161,11 +168,11 @@ }, { "constant": false, - "id": 16, + "id": 18, "mutability": "mutable", "name": "c", "nodeType": "VariableDeclaration", - "scope": 17, + "scope": 19, "src": "98:20:1", "stateVariable": true, "storageLocation": "default", @@ -176,12 +183,19 @@ }, "typeName": { - "id": 15, + "id": 17, "keyType": { - "id": 13, - "name": "E", + "id": 15, "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 14, + "name": "E", + "nodeType": "IdentifierPath", + "referencedDeclaration": 4, + "src": "106:1:1" + }, "referencedDeclaration": 4, "src": "106:1:1", "typeDescriptions": @@ -199,7 +213,7 @@ }, "valueType": { - "id": 14, + "id": 16, "name": "bool", "nodeType": "ElementaryTypeName", "src": "111:4:1", @@ -213,7 +227,7 @@ "visibility": "internal" } ], - "scope": 18, + "scope": 20, "src": "0:121:1" } ], diff --git a/test/libsolidity/ASTJSON/mappings_legacy.json b/test/libsolidity/ASTJSON/mappings_legacy.json deleted file mode 100644 index a44eea0ebd45..000000000000 --- a/test/libsolidity/ASTJSON/mappings_legacy.json +++ /dev/null @@ -1,242 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 17 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 17 - ], - "name": "C", - "scope": 18 - }, - "children": - [ - { - "attributes": - { - "canonicalName": "C.E", - "name": "E" - }, - "children": - [ - { - "attributes": - { - "name": "A" - }, - "id": 1, - "name": "EnumValue", - "src": "26:1:1" - }, - { - "attributes": - { - "name": "B" - }, - "id": 2, - "name": "EnumValue", - "src": "29:1:1" - }, - { - "attributes": - { - "name": "C" - }, - "id": 3, - "name": "EnumValue", - "src": "32:1:1" - } - ], - "id": 4, - "name": "EnumDefinition", - "src": "17:18:1" - }, - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "a", - "scope": 17, - "stateVariable": true, - "storageLocation": "default", - "type": "mapping(contract C => bool)", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "mapping(contract C => bool)" - }, - "children": - [ - { - "attributes": - { - "name": "C", - "referencedDeclaration": 17, - "type": "contract C" - }, - "id": 5, - "name": "UserDefinedTypeName", - "src": "48:1:1" - }, - { - "attributes": - { - "name": "bool", - "type": "bool" - }, - "id": 6, - "name": "ElementaryTypeName", - "src": "53:4:1" - } - ], - "id": 7, - "name": "Mapping", - "src": "40:18:1" - } - ], - "id": 8, - "name": "VariableDeclaration", - "src": "40:20:1" - }, - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "b", - "scope": 17, - "stateVariable": true, - "storageLocation": "default", - "type": "mapping(address => bool)", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "mapping(address => bool)" - }, - "children": - [ - { - "attributes": - { - "name": "address", - "type": "address" - }, - "id": 9, - "name": "ElementaryTypeName", - "src": "74:7:1" - }, - { - "attributes": - { - "name": "bool", - "type": "bool" - }, - "id": 10, - "name": "ElementaryTypeName", - "src": "85:4:1" - } - ], - "id": 11, - "name": "Mapping", - "src": "66:24:1" - } - ], - "id": 12, - "name": "VariableDeclaration", - "src": "66:26:1" - }, - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "c", - "scope": 17, - "stateVariable": true, - "storageLocation": "default", - "type": "mapping(enum C.E => bool)", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "mapping(enum C.E => bool)" - }, - "children": - [ - { - "attributes": - { - "name": "E", - "referencedDeclaration": 4, - "type": "enum C.E" - }, - "id": 13, - "name": "UserDefinedTypeName", - "src": "106:1:1" - }, - { - "attributes": - { - "name": "bool", - "type": "bool" - }, - "id": 14, - "name": "ElementaryTypeName", - "src": "111:4:1" - } - ], - "id": 15, - "name": "Mapping", - "src": "98:18:1" - } - ], - "id": 16, - "name": "VariableDeclaration", - "src": "98:20:1" - } - ], - "id": 17, - "name": "ContractDefinition", - "src": "0:121:1" - } - ], - "id": 18, - "name": "SourceUnit", - "src": "0:122:1" -} diff --git a/test/libsolidity/ASTJSON/mappings_parseOnly.json b/test/libsolidity/ASTJSON/mappings_parseOnly.json new file mode 100644 index 000000000000..7c78ad264b2f --- /dev/null +++ b/test/libsolidity/ASTJSON/mappings_parseOnly.json @@ -0,0 +1,166 @@ +{ + "absolutePath": "a", + "id": 20, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 19, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "id": 4, + "members": + [ + { + "id": 1, + "name": "A", + "nodeType": "EnumValue", + "src": "26:1:1" + }, + { + "id": 2, + "name": "B", + "nodeType": "EnumValue", + "src": "29:1:1" + }, + { + "id": 3, + "name": "C", + "nodeType": "EnumValue", + "src": "32:1:1" + } + ], + "name": "E", + "nodeType": "EnumDefinition", + "src": "17:18:1" + }, + { + "constant": false, + "id": 9, + "mutability": "mutable", + "name": "a", + "nodeType": "VariableDeclaration", + "src": "40:20:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 8, + "keyType": + { + "id": 6, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 5, + "name": "C", + "nodeType": "IdentifierPath", + "src": "48:1:1" + }, + "src": "48:1:1", + "typeDescriptions": {} + }, + "nodeType": "Mapping", + "src": "40:18:1", + "typeDescriptions": {}, + "valueType": + { + "id": 7, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "53:4:1", + "typeDescriptions": {} + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 13, + "mutability": "mutable", + "name": "b", + "nodeType": "VariableDeclaration", + "src": "66:26:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 12, + "keyType": + { + "id": 10, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "74:7:1", + "typeDescriptions": {} + }, + "nodeType": "Mapping", + "src": "66:24:1", + "typeDescriptions": {}, + "valueType": + { + "id": 11, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "85:4:1", + "typeDescriptions": {} + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 18, + "mutability": "mutable", + "name": "c", + "nodeType": "VariableDeclaration", + "src": "98:20:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 17, + "keyType": + { + "id": 15, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 14, + "name": "E", + "nodeType": "IdentifierPath", + "src": "106:1:1" + }, + "src": "106:1:1", + "typeDescriptions": {} + }, + "nodeType": "Mapping", + "src": "98:18:1", + "typeDescriptions": {}, + "valueType": + { + "id": 16, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "111:4:1", + "typeDescriptions": {} + } + }, + "visibility": "internal" + } + ], + "src": "0:121:1" + } + ], + "src": "0:122:1" +} diff --git a/test/libsolidity/ASTJSON/modifier_definition.json b/test/libsolidity/ASTJSON/modifier_definition.json index 198afe45415d..89fdab879318 100644 --- a/test/libsolidity/ASTJSON/modifier_definition.json +++ b/test/libsolidity/ASTJSON/modifier_definition.json @@ -126,15 +126,9 @@ { "id": 8, "name": "M", - "nodeType": "Identifier", - "overloadedDeclarations": [], + "nodeType": "IdentifierPath", "referencedDeclaration": 6, - "src": "52:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_modifier$_t_uint256_$", - "typeString": "modifier (uint256)" - } + "src": "52:1:1" }, "nodeType": "ModifierInvocation", "src": "52:4:1" diff --git a/test/libsolidity/ASTJSON/modifier_definition_legacy.json b/test/libsolidity/ASTJSON/modifier_definition_legacy.json deleted file mode 100644 index cc3089395ef0..000000000000 --- a/test/libsolidity/ASTJSON/modifier_definition_legacy.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 14 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 14 - ], - "name": "C", - "scope": 15 - }, - "children": - [ - { - "attributes": - { - "name": "M", - "virtual": false, - "visibility": "internal" - }, - "children": - [ - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "i", - "scope": 6, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 1, - "name": "ElementaryTypeName", - "src": "24:4:1" - } - ], - "id": 2, - "name": "VariableDeclaration", - "src": "24:6:1" - } - ], - "id": 3, - "name": "ParameterList", - "src": "23:8:1" - }, - { - "children": - [ - { - "id": 4, - "name": "PlaceholderStatement", - "src": "34:1:1" - } - ], - "id": 5, - "name": "Block", - "src": "32:6:1" - } - ], - "id": 6, - "name": "ModifierDefinition", - "src": "13:25:1" - }, - { - "attributes": - { - "functionSelector": "28811f59", - "implemented": true, - "isConstructor": false, - "kind": "function", - "name": "F", - "scope": 14, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 7, - "name": "ParameterList", - "src": "49:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 11, - "name": "ParameterList", - "src": "64:0:1" - }, - { - "children": - [ - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 6, - "type": "modifier (uint256)", - "value": "M" - }, - "id": 8, - "name": "Identifier", - "src": "52:1:1" - }, - { - "attributes": - { - "hexvalue": "31", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 1", - "value": "1" - }, - "id": 9, - "name": "Literal", - "src": "54:1:1" - } - ], - "id": 10, - "name": "ModifierInvocation", - "src": "52:4:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 12, - "name": "Block", - "src": "64:2:1" - } - ], - "id": 13, - "name": "FunctionDefinition", - "src": "39:27:1" - } - ], - "id": 14, - "name": "ContractDefinition", - "src": "0:68:1" - } - ], - "id": 15, - "name": "SourceUnit", - "src": "0:69:1" -} diff --git a/test/libsolidity/ASTJSON/modifier_definition_parseOnly.json b/test/libsolidity/ASTJSON/modifier_definition_parseOnly.json new file mode 100644 index 000000000000..33611e6ee812 --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_definition_parseOnly.json @@ -0,0 +1,132 @@ +{ + "absolutePath": "a", + "id": 15, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 14, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 5, + "nodeType": "Block", + "src": "32:6:1", + "statements": + [ + { + "id": 4, + "nodeType": "PlaceholderStatement", + "src": "34:1:1" + } + ] + }, + "id": 6, + "name": "M", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 2, + "mutability": "mutable", + "name": "i", + "nodeType": "VariableDeclaration", + "src": "24:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "24:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "23:8:1" + }, + "src": "13:25:1", + "virtual": false, + "visibility": "internal" + }, + { + "body": + { + "id": 12, + "nodeType": "Block", + "src": "64:2:1", + "statements": [] + }, + "id": 13, + "implemented": true, + "kind": "function", + "modifiers": + [ + { + "arguments": + [ + { + "hexValue": "31", + "id": 9, + "kind": "number", + "nodeType": "Literal", + "src": "54:1:1", + "typeDescriptions": {}, + "value": "1" + } + ], + "id": 10, + "modifierName": + { + "id": 8, + "name": "M", + "nodeType": "IdentifierPath", + "src": "52:1:1" + }, + "nodeType": "ModifierInvocation", + "src": "52:4:1" + } + ], + "name": "F", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "49:2:1" + }, + "returnParameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "64:0:1" + }, + "src": "39:27:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:68:1" + } + ], + "src": "0:69:1" +} diff --git a/test/libsolidity/ASTJSON/modifier_invocation.json b/test/libsolidity/ASTJSON/modifier_invocation.json index 198afe45415d..89fdab879318 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation.json +++ b/test/libsolidity/ASTJSON/modifier_invocation.json @@ -126,15 +126,9 @@ { "id": 8, "name": "M", - "nodeType": "Identifier", - "overloadedDeclarations": [], + "nodeType": "IdentifierPath", "referencedDeclaration": 6, - "src": "52:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_modifier$_t_uint256_$", - "typeString": "modifier (uint256)" - } + "src": "52:1:1" }, "nodeType": "ModifierInvocation", "src": "52:4:1" diff --git a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json deleted file mode 100644 index cc3089395ef0..000000000000 --- a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 14 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 14 - ], - "name": "C", - "scope": 15 - }, - "children": - [ - { - "attributes": - { - "name": "M", - "virtual": false, - "visibility": "internal" - }, - "children": - [ - { - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "i", - "scope": 6, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 1, - "name": "ElementaryTypeName", - "src": "24:4:1" - } - ], - "id": 2, - "name": "VariableDeclaration", - "src": "24:6:1" - } - ], - "id": 3, - "name": "ParameterList", - "src": "23:8:1" - }, - { - "children": - [ - { - "id": 4, - "name": "PlaceholderStatement", - "src": "34:1:1" - } - ], - "id": 5, - "name": "Block", - "src": "32:6:1" - } - ], - "id": 6, - "name": "ModifierDefinition", - "src": "13:25:1" - }, - { - "attributes": - { - "functionSelector": "28811f59", - "implemented": true, - "isConstructor": false, - "kind": "function", - "name": "F", - "scope": 14, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 7, - "name": "ParameterList", - "src": "49:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 11, - "name": "ParameterList", - "src": "64:0:1" - }, - { - "children": - [ - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 6, - "type": "modifier (uint256)", - "value": "M" - }, - "id": 8, - "name": "Identifier", - "src": "52:1:1" - }, - { - "attributes": - { - "hexvalue": "31", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 1", - "value": "1" - }, - "id": 9, - "name": "Literal", - "src": "54:1:1" - } - ], - "id": 10, - "name": "ModifierInvocation", - "src": "52:4:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 12, - "name": "Block", - "src": "64:2:1" - } - ], - "id": 13, - "name": "FunctionDefinition", - "src": "39:27:1" - } - ], - "id": 14, - "name": "ContractDefinition", - "src": "0:68:1" - } - ], - "id": 15, - "name": "SourceUnit", - "src": "0:69:1" -} diff --git a/test/libsolidity/ASTJSON/modifier_invocation_parseOnly.json b/test/libsolidity/ASTJSON/modifier_invocation_parseOnly.json new file mode 100644 index 000000000000..33611e6ee812 --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_invocation_parseOnly.json @@ -0,0 +1,132 @@ +{ + "absolutePath": "a", + "id": 15, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 14, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 5, + "nodeType": "Block", + "src": "32:6:1", + "statements": + [ + { + "id": 4, + "nodeType": "PlaceholderStatement", + "src": "34:1:1" + } + ] + }, + "id": 6, + "name": "M", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 3, + "nodeType": "ParameterList", + "parameters": + [ + { + "constant": false, + "id": 2, + "mutability": "mutable", + "name": "i", + "nodeType": "VariableDeclaration", + "src": "24:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "24:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "src": "23:8:1" + }, + "src": "13:25:1", + "virtual": false, + "visibility": "internal" + }, + { + "body": + { + "id": 12, + "nodeType": "Block", + "src": "64:2:1", + "statements": [] + }, + "id": 13, + "implemented": true, + "kind": "function", + "modifiers": + [ + { + "arguments": + [ + { + "hexValue": "31", + "id": 9, + "kind": "number", + "nodeType": "Literal", + "src": "54:1:1", + "typeDescriptions": {}, + "value": "1" + } + ], + "id": 10, + "modifierName": + { + "id": 8, + "name": "M", + "nodeType": "IdentifierPath", + "src": "52:1:1" + }, + "nodeType": "ModifierInvocation", + "src": "52:4:1" + } + ], + "name": "F", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "49:2:1" + }, + "returnParameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "64:0:1" + }, + "src": "39:27:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:68:1" + } + ], + "src": "0:69:1" +} diff --git a/test/libsolidity/ASTJSON/mutability_legacy.json b/test/libsolidity/ASTJSON/mutability_legacy.json deleted file mode 100644 index f4eb7e0c5dcd..000000000000 --- a/test/libsolidity/ASTJSON/mutability_legacy.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 10 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 10 - ], - "name": "C", - "scope": 11 - }, - "children": - [ - { - "attributes": - { - "constant": false, - "functionSelector": "0dbe671f", - "mutability": "immutable", - "name": "a", - "scope": 10, - "stateVariable": true, - "storageLocation": "default", - "type": "uint256", - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 1, - "name": "ElementaryTypeName", - "src": "17:4:1" - }, - { - "attributes": - { - "hexvalue": "34", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 4", - "value": "4" - }, - "id": 2, - "name": "Literal", - "src": "43:1:1" - } - ], - "id": 3, - "name": "VariableDeclaration", - "src": "17:27:1" - }, - { - "attributes": - { - "constant": true, - "functionSelector": "4df7e3d0", - "mutability": "constant", - "name": "b", - "scope": 10, - "stateVariable": true, - "storageLocation": "default", - "type": "uint256", - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 4, - "name": "ElementaryTypeName", - "src": "50:4:1" - }, - { - "attributes": - { - "hexvalue": "32", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 2", - "value": "2" - }, - "id": 5, - "name": "Literal", - "src": "75:1:1" - } - ], - "id": 6, - "name": "VariableDeclaration", - "src": "50:26:1" - }, - { - "attributes": - { - "constant": false, - "functionSelector": "c3da42b8", - "mutability": "mutable", - "name": "c", - "scope": 10, - "stateVariable": true, - "storageLocation": "default", - "type": "uint256", - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 7, - "name": "ElementaryTypeName", - "src": "82:4:1" - }, - { - "attributes": - { - "hexvalue": "33", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 3", - "value": "3" - }, - "id": 8, - "name": "Literal", - "src": "98:1:1" - } - ], - "id": 9, - "name": "VariableDeclaration", - "src": "82:17:1" - } - ], - "id": 10, - "name": "ContractDefinition", - "src": "0:102:1" - } - ], - "id": 11, - "name": "SourceUnit", - "src": "0:103:1" -} diff --git a/test/libsolidity/ASTJSON/mutability_parseOnly.json b/test/libsolidity/ASTJSON/mutability_parseOnly.json new file mode 100644 index 000000000000..5f9d49c22273 --- /dev/null +++ b/test/libsolidity/ASTJSON/mutability_parseOnly.json @@ -0,0 +1,112 @@ +{ + "absolutePath": "a", + "id": 11, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 10, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 3, + "mutability": "immutable", + "name": "a", + "nodeType": "VariableDeclaration", + "src": "17:27:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 1, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "17:4:1", + "typeDescriptions": {} + }, + "value": + { + "hexValue": "34", + "id": 2, + "kind": "number", + "nodeType": "Literal", + "src": "43:1:1", + "typeDescriptions": {}, + "value": "4" + }, + "visibility": "public" + }, + { + "constant": true, + "id": 6, + "mutability": "constant", + "name": "b", + "nodeType": "VariableDeclaration", + "src": "50:26:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 4, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "50:4:1", + "typeDescriptions": {} + }, + "value": + { + "hexValue": "32", + "id": 5, + "kind": "number", + "nodeType": "Literal", + "src": "75:1:1", + "typeDescriptions": {}, + "value": "2" + }, + "visibility": "public" + }, + { + "constant": false, + "id": 9, + "mutability": "mutable", + "name": "c", + "nodeType": "VariableDeclaration", + "src": "82:17:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 7, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "82:4:1", + "typeDescriptions": {} + }, + "value": + { + "hexValue": "33", + "id": 8, + "kind": "number", + "nodeType": "Literal", + "src": "98:1:1", + "typeDescriptions": {}, + "value": "3" + }, + "visibility": "public" + } + ], + "src": "0:102:1" + } + ], + "src": "0:103:1" +} diff --git a/test/libsolidity/ASTJSON/non_utf8.json b/test/libsolidity/ASTJSON/non_utf8.json index 98db4273fdd5..1a1ea5b96bc6 100644 --- a/test/libsolidity/ASTJSON/non_utf8.json +++ b/test/libsolidity/ASTJSON/non_utf8.json @@ -86,7 +86,7 @@ "typeDescriptions": { "typeIdentifier": "t_stringliteral_8b1a944cf13a9a1c08facb2c9e98623ef3254d2ddb48113885c3e8e97fec8db9", - "typeString": "literal_string (contains invalid UTF-8 sequence at position 0)" + "typeString": "literal_string hex\"ff\"" } }, "nodeType": "VariableDeclarationStatement", diff --git a/test/libsolidity/ASTJSON/non_utf8_legacy.json b/test/libsolidity/ASTJSON/non_utf8_legacy.json deleted file mode 100644 index d9430c82d30f..000000000000 --- a/test/libsolidity/ASTJSON/non_utf8_legacy.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 9 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 9 - ], - "name": "C", - "scope": 10 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 9, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "33:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 4 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "x", - "scope": 7, - "stateVariable": false, - "storageLocation": "memory", - "type": "string", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "string", - "type": "string" - }, - "id": 3, - "name": "ElementaryTypeName", - "src": "35:6:1" - } - ], - "id": 4, - "name": "VariableDeclaration", - "src": "35:15:1" - }, - { - "attributes": - { - "hexvalue": "ff", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "hexString", - "type": "literal_string (contains invalid UTF-8 sequence at position 0)" - }, - "id": 5, - "name": "Literal", - "src": "53:7:1" - } - ], - "id": 6, - "name": "VariableDeclarationStatement", - "src": "35:25:1" - } - ], - "id": 7, - "name": "Block", - "src": "33:30:1" - } - ], - "id": 8, - "name": "FunctionDefinition", - "src": "13:50:1" - } - ], - "id": 9, - "name": "ContractDefinition", - "src": "0:65:1" - } - ], - "id": 10, - "name": "SourceUnit", - "src": "0:66:1" -} diff --git a/test/libsolidity/ASTJSON/non_utf8_parseOnly.json b/test/libsolidity/ASTJSON/non_utf8_parseOnly.json new file mode 100644 index 000000000000..a8ddabfbdcfe --- /dev/null +++ b/test/libsolidity/ASTJSON/non_utf8_parseOnly.json @@ -0,0 +1,98 @@ +{ + "absolutePath": "a", + "id": 10, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 9, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 7, + "nodeType": "Block", + "src": "33:30:1", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "35:15:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": {}, + "typeName": + { + "id": 3, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "35:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 6, + "initialValue": + { + "hexValue": "ff", + "id": 5, + "kind": "hexString", + "nodeType": "Literal", + "src": "53:7:1", + "typeDescriptions": {} + }, + "nodeType": "VariableDeclarationStatement", + "src": "35:25:1" + } + ] + }, + "id": 8, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "src": "13:50:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:65:1" + } + ], + "src": "0:66:1" +} diff --git a/test/libsolidity/ASTJSON/not_existing_import.json b/test/libsolidity/ASTJSON/not_existing_import.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test/libsolidity/ASTJSON/not_existing_import.sol b/test/libsolidity/ASTJSON/not_existing_import.sol new file mode 100644 index 000000000000..6353c7527bde --- /dev/null +++ b/test/libsolidity/ASTJSON/not_existing_import.sol @@ -0,0 +1,8 @@ +import "notexisting.sol" as NotExisting; +contract C is NotExisting.X +{ + NotExisting.SomeStruct public myStruct; + constructor() {} +} + +// ---- diff --git a/test/libsolidity/ASTJSON/not_existing_import_parseOnly.json b/test/libsolidity/ASTJSON/not_existing_import_parseOnly.json new file mode 100644 index 000000000000..4a854afaa761 --- /dev/null +++ b/test/libsolidity/ASTJSON/not_existing_import_parseOnly.json @@ -0,0 +1,102 @@ +{ + "absolutePath": "a", + "id": 12, + "nodeType": "SourceUnit", + "nodes": + [ + { + "file": "notexisting.sol", + "id": 1, + "nodeType": "ImportDirective", + "src": "0:40:1", + "symbolAliases": [], + "unitAlias": "NotExisting" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 2, + "name": "NotExisting.X", + "nodeType": "IdentifierPath", + "src": "55:13:1" + }, + "id": 3, + "nodeType": "InheritanceSpecifier", + "src": "55:13:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 11, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "constant": false, + "id": 6, + "mutability": "mutable", + "name": "myStruct", + "nodeType": "VariableDeclaration", + "src": "72:38:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 5, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 4, + "name": "NotExisting.SomeStruct", + "nodeType": "IdentifierPath", + "src": "72:22:1" + }, + "src": "72:22:1", + "typeDescriptions": {} + }, + "visibility": "public" + }, + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "127:2:1", + "statements": [] + }, + "id": 10, + "implemented": true, + "kind": "constructor", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "124:2:1" + }, + "returnParameters": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": [], + "src": "127:0:1" + }, + "src": "113:16:1", + "stateMutability": "nonpayable", + "virtual": false + } + ], + "src": "41:90:1" + } + ], + "src": "0:132:1" +} diff --git a/test/libsolidity/ASTJSON/override.json b/test/libsolidity/ASTJSON/override.json index 49f85144e8fa..a7412cb5aa04 100644 --- a/test/libsolidity/ASTJSON/override.json +++ b/test/libsolidity/ASTJSON/override.json @@ -82,14 +82,9 @@ { "id": 6, "name": "A", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 5, - "src": "55:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_A_$5", - "typeString": "contract A" - } + "src": "55:1:1" }, "id": 7, "nodeType": "InheritanceSpecifier", @@ -199,14 +194,9 @@ { "id": 17, "name": "B", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 16, - "src": "134:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_B_$16", - "typeString": "contract B" - } + "src": "134:1:1" }, "id": 18, "nodeType": "InheritanceSpecifier", @@ -305,26 +295,16 @@ { "id": 25, "name": "A", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 5, - "src": "206:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_A_$5", - "typeString": "contract A" - } + "src": "206:1:1" }, { "id": 26, "name": "B", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 16, - "src": "209:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_B_$16", - "typeString": "contract B" - } + "src": "209:1:1" } ], "src": "197:14:1" diff --git a/test/libsolidity/ASTJSON/override_legacy.json b/test/libsolidity/ASTJSON/override_legacy.json deleted file mode 100644 index 5cdc2f3649ff..000000000000 --- a/test/libsolidity/ASTJSON/override_legacy.json +++ /dev/null @@ -1,515 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "A": - [ - 5 - ], - "B": - [ - 16 - ], - "C": - [ - 31 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "A", - "scope": 32 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "a399b6a2", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "faa", - "scope": 5, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "26:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "36:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "36:2:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "14:24:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:40:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 5 - ], - "contractKind": "contract", - "fullyImplemented": false, - "linearizedBaseContracts": - [ - 16, - 5 - ], - "name": "B", - "scope": 32 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "A", - "referencedDeclaration": 5, - "type": "contract A" - }, - "id": 6, - "name": "UserDefinedTypeName", - "src": "55:1:1" - } - ], - "id": 7, - "name": "InheritanceSpecifier", - "src": "55:1:1" - }, - { - "attributes": - { - "functionSelector": "c2985578", - "implemented": false, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "foo", - "scope": 16, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 8, - "name": "ParameterList", - "src": "72:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 9, - "name": "ParameterList", - "src": "81:0:1" - } - ], - "id": 10, - "name": "FunctionDefinition", - "src": "60:22:1" - }, - { - "attributes": - { - "baseFunctions": - [ - 4 - ], - "functionSelector": "a399b6a2", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "faa", - "scope": 16, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "overrides": - [ - null - ] - }, - "id": 12, - "name": "OverrideSpecifier", - "src": "106:8:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 11, - "name": "ParameterList", - "src": "96:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 13, - "name": "ParameterList", - "src": "115:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 14, - "name": "Block", - "src": "115:2:1" - } - ], - "id": 15, - "name": "FunctionDefinition", - "src": "84:33:1" - } - ], - "id": 16, - "name": "ContractDefinition", - "src": "41:78:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 5, - 16 - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 31, - 16, - 5 - ], - "name": "C", - "scope": 32 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "B", - "referencedDeclaration": 16, - "type": "contract B" - }, - "id": 17, - "name": "UserDefinedTypeName", - "src": "134:1:1" - } - ], - "id": 18, - "name": "InheritanceSpecifier", - "src": "134:1:1" - }, - { - "attributes": - { - "baseFunctions": - [ - 10 - ], - "functionSelector": "c2985578", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "foo", - "scope": 31, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "overrides": - [ - null - ] - }, - "id": 20, - "name": "OverrideSpecifier", - "src": "161:8:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 19, - "name": "ParameterList", - "src": "151:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 21, - "name": "ParameterList", - "src": "170:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 22, - "name": "Block", - "src": "170:3:1" - } - ], - "id": 23, - "name": "FunctionDefinition", - "src": "139:34:1" - }, - { - "attributes": - { - "baseFunctions": - [ - 15 - ], - "functionSelector": "a399b6a2", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "faa", - "scope": 31, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "children": - [ - { - "attributes": - { - "name": "A", - "referencedDeclaration": 5, - "type": "contract A" - }, - "id": 25, - "name": "UserDefinedTypeName", - "src": "206:1:1" - }, - { - "attributes": - { - "name": "B", - "referencedDeclaration": 16, - "type": "contract B" - }, - "id": 26, - "name": "UserDefinedTypeName", - "src": "209:1:1" - } - ], - "id": 27, - "name": "OverrideSpecifier", - "src": "197:14:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 24, - "name": "ParameterList", - "src": "187:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 28, - "name": "ParameterList", - "src": "212:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 29, - "name": "Block", - "src": "212:2:1" - } - ], - "id": 30, - "name": "FunctionDefinition", - "src": "175:39:1" - } - ], - "id": 31, - "name": "ContractDefinition", - "src": "120:96:1" - } - ], - "id": 32, - "name": "SourceUnit", - "src": "0:217:1" -} diff --git a/test/libsolidity/ASTJSON/override_parseOnly.json b/test/libsolidity/ASTJSON/override_parseOnly.json new file mode 100644 index 000000000000..60c70f9f7254 --- /dev/null +++ b/test/libsolidity/ASTJSON/override_parseOnly.json @@ -0,0 +1,269 @@ +{ + "absolutePath": "a", + "id": 32, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "A", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "36:2:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "faa", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "26:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "36:0:1" + }, + "src": "14:24:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:40:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 6, + "name": "A", + "nodeType": "IdentifierPath", + "src": "55:1:1" + }, + "id": 7, + "nodeType": "InheritanceSpecifier", + "src": "55:1:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 16, + "name": "B", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "id": 10, + "implemented": false, + "kind": "function", + "modifiers": [], + "name": "foo", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 8, + "nodeType": "ParameterList", + "parameters": [], + "src": "72:2:1" + }, + "returnParameters": + { + "id": 9, + "nodeType": "ParameterList", + "parameters": [], + "src": "81:0:1" + }, + "src": "60:22:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": + { + "id": 14, + "nodeType": "Block", + "src": "115:2:1", + "statements": [] + }, + "id": 15, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "faa", + "nodeType": "FunctionDefinition", + "overrides": + { + "id": 12, + "nodeType": "OverrideSpecifier", + "overrides": [], + "src": "106:8:1" + }, + "parameters": + { + "id": 11, + "nodeType": "ParameterList", + "parameters": [], + "src": "96:2:1" + }, + "returnParameters": + { + "id": 13, + "nodeType": "ParameterList", + "parameters": [], + "src": "115:0:1" + }, + "src": "84:33:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "41:78:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 17, + "name": "B", + "nodeType": "IdentifierPath", + "src": "134:1:1" + }, + "id": 18, + "nodeType": "InheritanceSpecifier", + "src": "134:1:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 31, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 22, + "nodeType": "Block", + "src": "170:3:1", + "statements": [] + }, + "id": 23, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "foo", + "nodeType": "FunctionDefinition", + "overrides": + { + "id": 20, + "nodeType": "OverrideSpecifier", + "overrides": [], + "src": "161:8:1" + }, + "parameters": + { + "id": 19, + "nodeType": "ParameterList", + "parameters": [], + "src": "151:2:1" + }, + "returnParameters": + { + "id": 21, + "nodeType": "ParameterList", + "parameters": [], + "src": "170:0:1" + }, + "src": "139:34:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": + { + "id": 29, + "nodeType": "Block", + "src": "212:2:1", + "statements": [] + }, + "id": 30, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "faa", + "nodeType": "FunctionDefinition", + "overrides": + { + "id": 27, + "nodeType": "OverrideSpecifier", + "overrides": + [ + { + "id": 25, + "name": "A", + "nodeType": "IdentifierPath", + "src": "206:1:1" + }, + { + "id": 26, + "name": "B", + "nodeType": "IdentifierPath", + "src": "209:1:1" + } + ], + "src": "197:14:1" + }, + "parameters": + { + "id": 24, + "nodeType": "ParameterList", + "parameters": [], + "src": "187:2:1" + }, + "returnParameters": + { + "id": 28, + "nodeType": "ParameterList", + "parameters": [], + "src": "212:0:1" + }, + "src": "175:39:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "120:96:1" + } + ], + "src": "0:217:1" +} diff --git a/test/libsolidity/ASTJSON/placeholder_statement_legacy.json b/test/libsolidity/ASTJSON/placeholder_statement_legacy.json deleted file mode 100644 index 661feb13e26b..000000000000 --- a/test/libsolidity/ASTJSON/placeholder_statement_legacy.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 5 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "C", - "scope": 6 - }, - "children": - [ - { - "attributes": - { - "name": "M", - "virtual": false, - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "24:0:1" - }, - { - "children": - [ - { - "id": 2, - "name": "PlaceholderStatement", - "src": "26:1:1" - } - ], - "id": 3, - "name": "Block", - "src": "24:6:1" - } - ], - "id": 4, - "name": "ModifierDefinition", - "src": "13:17:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:32:1" - } - ], - "id": 6, - "name": "SourceUnit", - "src": "0:33:1" -} diff --git a/test/libsolidity/ASTJSON/placeholder_statement_parseOnly.json b/test/libsolidity/ASTJSON/placeholder_statement_parseOnly.json new file mode 100644 index 000000000000..aac418e75bb2 --- /dev/null +++ b/test/libsolidity/ASTJSON/placeholder_statement_parseOnly.json @@ -0,0 +1,51 @@ +{ + "absolutePath": "a", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "24:6:1", + "statements": + [ + { + "id": 2, + "nodeType": "PlaceholderStatement", + "src": "26:1:1" + } + ] + }, + "id": 4, + "name": "M", + "nodeType": "ModifierDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "24:0:1" + }, + "src": "13:17:1", + "virtual": false, + "visibility": "internal" + } + ], + "src": "0:32:1" + } + ], + "src": "0:33:1" +} diff --git a/test/libsolidity/ASTJSON/receive_ether_legacy.json b/test/libsolidity/ASTJSON/receive_ether_legacy.json deleted file mode 100644 index d9544f138a48..000000000000 --- a/test/libsolidity/ASTJSON/receive_ether_legacy.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 5 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "C", - "scope": 6 - }, - "children": - [ - { - "attributes": - { - "implemented": true, - "isConstructor": false, - "kind": "receive", - "modifiers": - [ - null - ], - "name": "", - "scope": 5, - "stateMutability": "payable", - "virtual": false, - "visibility": "external" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "22:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "42:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "42:5:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "15:32:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:49:1" - } - ], - "id": 6, - "name": "SourceUnit", - "src": "0:50:1" -} diff --git a/test/libsolidity/ASTJSON/receive_ether_parseOnly.json b/test/libsolidity/ASTJSON/receive_ether_parseOnly.json new file mode 100644 index 000000000000..476dd6d689c6 --- /dev/null +++ b/test/libsolidity/ASTJSON/receive_ether_parseOnly.json @@ -0,0 +1,55 @@ +{ + "absolutePath": "a", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "42:5:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "receive", + "modifiers": [], + "name": "", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "22:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "42:0:1" + }, + "src": "15:32:1", + "stateMutability": "payable", + "virtual": false, + "visibility": "external" + } + ], + "src": "0:49:1" + } + ], + "src": "0:50:1" +} diff --git a/test/libsolidity/ASTJSON/short_type_name_legacy.json b/test/libsolidity/ASTJSON/short_type_name_legacy.json deleted file mode 100644 index 8e255954d9a9..000000000000 --- a/test/libsolidity/ASTJSON/short_type_name_legacy.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "c": - [ - 11 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 11 - ], - "name": "c", - "scope": 12 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 11, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "33:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 7 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "x", - "scope": 9, - "stateVariable": false, - "storageLocation": "memory", - "type": "uint256[]", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "uint256[]" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 5, - "name": "ElementaryTypeName", - "src": "35:4:1" - } - ], - "id": 6, - "name": "ArrayTypeName", - "src": "35:6:1" - } - ], - "id": 7, - "name": "VariableDeclaration", - "src": "35:15:1" - } - ], - "id": 8, - "name": "VariableDeclarationStatement", - "src": "35:15:1" - } - ], - "id": 9, - "name": "Block", - "src": "33:20:1" - } - ], - "id": 10, - "name": "FunctionDefinition", - "src": "13:40:1" - } - ], - "id": 11, - "name": "ContractDefinition", - "src": "0:55:1" - } - ], - "id": 12, - "name": "SourceUnit", - "src": "0:56:1" -} diff --git a/test/libsolidity/ASTJSON/short_type_name_parseOnly.json b/test/libsolidity/ASTJSON/short_type_name_parseOnly.json new file mode 100644 index 000000000000..aa10984570c8 --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name_parseOnly.json @@ -0,0 +1,96 @@ +{ + "absolutePath": "a", + "id": 12, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 11, + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 9, + "nodeType": "Block", + "src": "33:20:1", + "statements": + [ + { + "assignments": + [ + 7 + ], + "declarations": + [ + { + "constant": false, + "id": 7, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "35:15:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": {}, + "typeName": + { + "baseType": + { + "id": 5, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "35:4:1", + "typeDescriptions": {} + }, + "id": 6, + "nodeType": "ArrayTypeName", + "src": "35:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 8, + "nodeType": "VariableDeclarationStatement", + "src": "35:15:1" + } + ] + }, + "id": 10, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "src": "13:40:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:55:1" + } + ], + "src": "0:56:1" +} diff --git a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json deleted file mode 100644 index 5316c56ac572..000000000000 --- a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "c": - [ - 12 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 12 - ], - "name": "c", - "scope": 13 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 12, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "33:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 8 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "rows", - "scope": 10, - "stateVariable": false, - "storageLocation": "memory", - "type": "uint256[][]", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "type": "uint256[][]" - }, - "children": - [ - { - "attributes": - { - "type": "uint256[]" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 5, - "name": "ElementaryTypeName", - "src": "35:4:1" - } - ], - "id": 6, - "name": "ArrayTypeName", - "src": "35:6:1" - } - ], - "id": 7, - "name": "ArrayTypeName", - "src": "35:8:1" - } - ], - "id": 8, - "name": "VariableDeclaration", - "src": "35:20:1" - } - ], - "id": 9, - "name": "VariableDeclarationStatement", - "src": "35:20:1" - } - ], - "id": 10, - "name": "Block", - "src": "33:25:1" - } - ], - "id": 11, - "name": "FunctionDefinition", - "src": "13:45:1" - } - ], - "id": 12, - "name": "ContractDefinition", - "src": "0:60:1" - } - ], - "id": 13, - "name": "SourceUnit", - "src": "0:61:1" -} diff --git a/test/libsolidity/ASTJSON/short_type_name_ref_parseOnly.json b/test/libsolidity/ASTJSON/short_type_name_ref_parseOnly.json new file mode 100644 index 000000000000..df5e7491873c --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name_ref_parseOnly.json @@ -0,0 +1,103 @@ +{ + "absolutePath": "a", + "id": 13, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 12, + "name": "c", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 10, + "nodeType": "Block", + "src": "33:25:1", + "statements": + [ + { + "assignments": + [ + 8 + ], + "declarations": + [ + { + "constant": false, + "id": 8, + "mutability": "mutable", + "name": "rows", + "nodeType": "VariableDeclaration", + "src": "35:20:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": {}, + "typeName": + { + "baseType": + { + "baseType": + { + "id": 5, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "35:4:1", + "typeDescriptions": {} + }, + "id": 6, + "nodeType": "ArrayTypeName", + "src": "35:6:1", + "typeDescriptions": {} + }, + "id": 7, + "nodeType": "ArrayTypeName", + "src": "35:8:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 9, + "nodeType": "VariableDeclarationStatement", + "src": "35:20:1" + } + ] + }, + "id": 11, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "src": "13:45:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:60:1" + } + ], + "src": "0:61:1" +} diff --git a/test/libsolidity/ASTJSON/smoke_legacy.json b/test/libsolidity/ASTJSON/smoke_legacy.json deleted file mode 100644 index 6097976e922c..000000000000 --- a/test/libsolidity/ASTJSON/smoke_legacy.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 1 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 1 - ], - "name": "C", - "nodes": - [ - null - ], - "scope": 2 - }, - "id": 1, - "name": "ContractDefinition", - "src": "0:13:1" - } - ], - "id": 2, - "name": "SourceUnit", - "src": "0:14:1" -} diff --git a/test/libsolidity/ASTJSON/smoke_parseOnly.json b/test/libsolidity/ASTJSON/smoke_parseOnly.json new file mode 100644 index 000000000000..2c3936629aae --- /dev/null +++ b/test/libsolidity/ASTJSON/smoke_parseOnly.json @@ -0,0 +1,20 @@ +{ + "absolutePath": "a", + "id": 2, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 1, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "0:13:1" + } + ], + "src": "0:14:1" +} diff --git a/test/libsolidity/ASTJSON/source_location_legacy.json b/test/libsolidity/ASTJSON/source_location_legacy.json deleted file mode 100644 index bf3dfaedd468..000000000000 --- a/test/libsolidity/ASTJSON/source_location_legacy.json +++ /dev/null @@ -1,206 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 12 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 12 - ], - "name": "C", - "scope": 13 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 12, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "26:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 4 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "x", - "scope": 10, - "stateVariable": false, - "storageLocation": "default", - "type": "uint256", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 3, - "name": "ElementaryTypeName", - "src": "28:4:1" - } - ], - "id": 4, - "name": "VariableDeclaration", - "src": "28:6:1" - }, - { - "attributes": - { - "hexvalue": "32", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "number", - "type": "int_const 2", - "value": "2" - }, - "id": 5, - "name": "Literal", - "src": "37:1:1" - } - ], - "id": 6, - "name": "VariableDeclarationStatement", - "src": "28:10:1" - }, - { - "children": - [ - { - "attributes": - { - "isConstant": false, - "isLValue": false, - "isPure": false, - "lValueRequested": false, - "operator": "++", - "prefix": false, - "type": "uint256" - }, - "children": - [ - { - "attributes": - { - "overloadedDeclarations": - [ - null - ], - "referencedDeclaration": 4, - "type": "uint256", - "value": "x" - }, - "id": 7, - "name": "Identifier", - "src": "40:1:1" - } - ], - "id": 8, - "name": "UnaryOperation", - "src": "40:3:1" - } - ], - "id": 9, - "name": "ExpressionStatement", - "src": "40:3:1" - } - ], - "id": 10, - "name": "Block", - "src": "26:20:1" - } - ], - "id": 11, - "name": "FunctionDefinition", - "src": "13:33:1" - } - ], - "id": 12, - "name": "ContractDefinition", - "src": "0:48:1" - } - ], - "id": 13, - "name": "SourceUnit", - "src": "0:49:1" -} diff --git a/test/libsolidity/ASTJSON/source_location_parseOnly.json b/test/libsolidity/ASTJSON/source_location_parseOnly.json new file mode 100644 index 000000000000..2bd8e80d8c80 --- /dev/null +++ b/test/libsolidity/ASTJSON/source_location_parseOnly.json @@ -0,0 +1,122 @@ +{ + "absolutePath": "a", + "id": 13, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 12, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 10, + "nodeType": "Block", + "src": "26:20:1", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "28:6:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "28:4:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 6, + "initialValue": + { + "hexValue": "32", + "id": 5, + "kind": "number", + "nodeType": "Literal", + "src": "37:1:1", + "typeDescriptions": {}, + "value": "2" + }, + "nodeType": "VariableDeclarationStatement", + "src": "28:10:1" + }, + { + "expression": + { + "id": 8, + "nodeType": "UnaryOperation", + "operator": "++", + "prefix": false, + "src": "40:3:1", + "subExpression": + { + "id": 7, + "name": "x", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "src": "40:1:1", + "typeDescriptions": {} + }, + "typeDescriptions": {} + }, + "id": 9, + "nodeType": "ExpressionStatement", + "src": "40:3:1" + } + ] + }, + "id": 11, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "26:0:1" + }, + "src": "13:33:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:48:1" + } + ], + "src": "0:49:1" +} diff --git a/test/libsolidity/ASTJSON/string_legacy.json b/test/libsolidity/ASTJSON/string_legacy.json deleted file mode 100644 index 96c984209237..000000000000 --- a/test/libsolidity/ASTJSON/string_legacy.json +++ /dev/null @@ -1,165 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 9 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 9 - ], - "name": "C", - "scope": 10 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 9, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "33:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 4 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "x", - "scope": 7, - "stateVariable": false, - "storageLocation": "memory", - "type": "string", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "string", - "type": "string" - }, - "id": 3, - "name": "ElementaryTypeName", - "src": "35:6:1" - } - ], - "id": 4, - "name": "VariableDeclaration", - "src": "35:15:1" - }, - { - "attributes": - { - "hexvalue": "48656c6c6f20576f726c64", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "string", - "type": "literal_string \"Hello World\"", - "value": "Hello World" - }, - "id": 5, - "name": "Literal", - "src": "53:13:1" - } - ], - "id": 6, - "name": "VariableDeclarationStatement", - "src": "35:31:1" - } - ], - "id": 7, - "name": "Block", - "src": "33:36:1" - } - ], - "id": 8, - "name": "FunctionDefinition", - "src": "13:56:1" - } - ], - "id": 9, - "name": "ContractDefinition", - "src": "0:71:1" - } - ], - "id": 10, - "name": "SourceUnit", - "src": "0:72:1" -} diff --git a/test/libsolidity/ASTJSON/string_parseOnly.json b/test/libsolidity/ASTJSON/string_parseOnly.json new file mode 100644 index 000000000000..06ba82aaa6b3 --- /dev/null +++ b/test/libsolidity/ASTJSON/string_parseOnly.json @@ -0,0 +1,99 @@ +{ + "absolutePath": "a", + "id": 10, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 9, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 7, + "nodeType": "Block", + "src": "33:36:1", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "35:15:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": {}, + "typeName": + { + "id": 3, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "35:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 6, + "initialValue": + { + "hexValue": "48656c6c6f20576f726c64", + "id": 5, + "kind": "string", + "nodeType": "Literal", + "src": "53:13:1", + "typeDescriptions": {}, + "value": "Hello World" + }, + "nodeType": "VariableDeclarationStatement", + "src": "35:31:1" + } + ] + }, + "id": 8, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "src": "13:56:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:71:1" + } + ], + "src": "0:72:1" +} diff --git a/test/libsolidity/ASTJSON/two_base_functions.json b/test/libsolidity/ASTJSON/two_base_functions.json index 769e5c3f88d1..1b3eb13fd97b 100644 --- a/test/libsolidity/ASTJSON/two_base_functions.json +++ b/test/libsolidity/ASTJSON/two_base_functions.json @@ -136,14 +136,9 @@ { "id": 11, "name": "A", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 5, - "src": "114:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_A_$5", - "typeString": "contract A" - } + "src": "114:1:1" }, "id": 12, "nodeType": "InheritanceSpecifier", @@ -154,14 +149,9 @@ { "id": 13, "name": "B", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 10, - "src": "117:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_B_$10", - "typeString": "contract B" - } + "src": "117:1:1" }, "id": 14, "nodeType": "InheritanceSpecifier", @@ -215,26 +205,16 @@ { "id": 16, "name": "A", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 5, - "src": "154:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_A_$5", - "typeString": "contract A" - } + "src": "154:1:1" }, { "id": 17, "name": "B", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 10, - "src": "157:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_B_$10", - "typeString": "contract B" - } + "src": "157:1:1" } ], "src": "145:14:1" diff --git a/test/libsolidity/ASTJSON/two_base_functions_legacy.json b/test/libsolidity/ASTJSON/two_base_functions_legacy.json deleted file mode 100644 index a5bd2d725d80..000000000000 --- a/test/libsolidity/ASTJSON/two_base_functions_legacy.json +++ /dev/null @@ -1,374 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "A": - [ - 5 - ], - "B": - [ - 10 - ], - "C": - [ - 22 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "A", - "scope": 23 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 5, - "stateMutability": "nonpayable", - "virtual": true, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "27:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "45:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 3, - "name": "Block", - "src": "45:2:1" - } - ], - "id": 4, - "name": "FunctionDefinition", - "src": "17:30:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "0:49:1" - }, - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 10 - ], - "name": "B", - "scope": 23 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 10, - "stateMutability": "nonpayable", - "virtual": true, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 6, - "name": "ParameterList", - "src": "77:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 7, - "name": "ParameterList", - "src": "95:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 8, - "name": "Block", - "src": "95:2:1" - } - ], - "id": 9, - "name": "FunctionDefinition", - "src": "67:30:1" - } - ], - "id": 10, - "name": "ContractDefinition", - "src": "50:49:1" - }, - { - "attributes": - { - "abstract": false, - "contractDependencies": - [ - 5, - 10 - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 22, - 10, - 5 - ], - "name": "C", - "scope": 23 - }, - "children": - [ - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "A", - "referencedDeclaration": 5, - "type": "contract A" - }, - "id": 11, - "name": "UserDefinedTypeName", - "src": "114:1:1" - } - ], - "id": 12, - "name": "InheritanceSpecifier", - "src": "114:1:1" - }, - { - "attributes": {}, - "children": - [ - { - "attributes": - { - "name": "B", - "referencedDeclaration": 10, - "type": "contract B" - }, - "id": 13, - "name": "UserDefinedTypeName", - "src": "117:1:1" - } - ], - "id": 14, - "name": "InheritanceSpecifier", - "src": "117:1:1" - }, - { - "attributes": - { - "baseFunctions": - [ - 4, - 9 - ], - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 22, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "children": - [ - { - "attributes": - { - "name": "A", - "referencedDeclaration": 5, - "type": "contract A" - }, - "id": 16, - "name": "UserDefinedTypeName", - "src": "154:1:1" - }, - { - "attributes": - { - "name": "B", - "referencedDeclaration": 10, - "type": "contract B" - }, - "id": 17, - "name": "UserDefinedTypeName", - "src": "157:1:1" - } - ], - "id": 18, - "name": "OverrideSpecifier", - "src": "145:14:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 15, - "name": "ParameterList", - "src": "135:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 19, - "name": "ParameterList", - "src": "160:0:1" - }, - { - "attributes": - { - "statements": - [ - null - ] - }, - "children": [], - "id": 20, - "name": "Block", - "src": "160:2:1" - } - ], - "id": 21, - "name": "FunctionDefinition", - "src": "125:37:1" - } - ], - "id": 22, - "name": "ContractDefinition", - "src": "100:64:1" - } - ], - "id": 23, - "name": "SourceUnit", - "src": "0:165:1" -} diff --git a/test/libsolidity/ASTJSON/two_base_functions_parseOnly.json b/test/libsolidity/ASTJSON/two_base_functions_parseOnly.json new file mode 100644 index 000000000000..a807cabbd1d4 --- /dev/null +++ b/test/libsolidity/ASTJSON/two_base_functions_parseOnly.json @@ -0,0 +1,194 @@ +{ + "absolutePath": "a", + "id": 23, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "A", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 3, + "nodeType": "Block", + "src": "45:2:1", + "statements": [] + }, + "id": 4, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "27:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "45:0:1" + }, + "src": "17:30:1", + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "public" + } + ], + "src": "0:49:1" + }, + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 10, + "name": "B", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 8, + "nodeType": "Block", + "src": "95:2:1", + "statements": [] + }, + "id": 9, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 6, + "nodeType": "ParameterList", + "parameters": [], + "src": "77:2:1" + }, + "returnParameters": + { + "id": 7, + "nodeType": "ParameterList", + "parameters": [], + "src": "95:0:1" + }, + "src": "67:30:1", + "stateMutability": "nonpayable", + "virtual": true, + "visibility": "public" + } + ], + "src": "50:49:1" + }, + { + "abstract": false, + "baseContracts": + [ + { + "baseName": + { + "id": 11, + "name": "A", + "nodeType": "IdentifierPath", + "src": "114:1:1" + }, + "id": 12, + "nodeType": "InheritanceSpecifier", + "src": "114:1:1" + }, + { + "baseName": + { + "id": 13, + "name": "B", + "nodeType": "IdentifierPath", + "src": "117:1:1" + }, + "id": 14, + "nodeType": "InheritanceSpecifier", + "src": "117:1:1" + } + ], + "contractDependencies": [], + "contractKind": "contract", + "id": 22, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 20, + "nodeType": "Block", + "src": "160:2:1", + "statements": [] + }, + "id": 21, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "overrides": + { + "id": 18, + "nodeType": "OverrideSpecifier", + "overrides": + [ + { + "id": 16, + "name": "A", + "nodeType": "IdentifierPath", + "src": "154:1:1" + }, + { + "id": 17, + "name": "B", + "nodeType": "IdentifierPath", + "src": "157:1:1" + } + ], + "src": "145:14:1" + }, + "parameters": + { + "id": 15, + "nodeType": "ParameterList", + "parameters": [], + "src": "135:2:1" + }, + "returnParameters": + { + "id": 19, + "nodeType": "ParameterList", + "parameters": [], + "src": "160:0:1" + }, + "src": "125:37:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "100:64:1" + } + ], + "src": "0:165:1" +} diff --git a/test/libsolidity/ASTJSON/unicode.json b/test/libsolidity/ASTJSON/unicode.json index 81c8ad0da52c..219d89500f2d 100644 --- a/test/libsolidity/ASTJSON/unicode.json +++ b/test/libsolidity/ASTJSON/unicode.json @@ -86,7 +86,7 @@ "typeDescriptions": { "typeIdentifier": "t_stringliteral_cd7a99177cebb3d14b8cc54e313dbf76867c71cd6fbb9a33ce3870dc80e9992b", - "typeString": "literal_string \"Hello \ud83d\ude03\"" + "typeString": "literal_string hex\"48656c6c6f20f09f9883\"" }, "value": "Hello \ud83d\ude03" }, diff --git a/test/libsolidity/ASTJSON/unicode_legacy.json b/test/libsolidity/ASTJSON/unicode_legacy.json deleted file mode 100644 index 22976f6f9775..000000000000 --- a/test/libsolidity/ASTJSON/unicode_legacy.json +++ /dev/null @@ -1,165 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 9 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 9 - ], - "name": "C", - "scope": 10 - }, - "children": - [ - { - "attributes": - { - "functionSelector": "26121ff0", - "implemented": true, - "isConstructor": false, - "kind": "function", - "modifiers": - [ - null - ], - "name": "f", - "scope": 9, - "stateMutability": "nonpayable", - "virtual": false, - "visibility": "public" - }, - "children": - [ - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 1, - "name": "ParameterList", - "src": "23:2:1" - }, - { - "attributes": - { - "parameters": - [ - null - ] - }, - "children": [], - "id": 2, - "name": "ParameterList", - "src": "33:0:1" - }, - { - "children": - [ - { - "attributes": - { - "assignments": - [ - 4 - ] - }, - "children": - [ - { - "attributes": - { - "constant": false, - "mutability": "mutable", - "name": "x", - "scope": 7, - "stateVariable": false, - "storageLocation": "memory", - "type": "string", - "visibility": "internal" - }, - "children": - [ - { - "attributes": - { - "name": "string", - "type": "string" - }, - "id": 3, - "name": "ElementaryTypeName", - "src": "35:6:1" - } - ], - "id": 4, - "name": "VariableDeclaration", - "src": "35:15:1" - }, - { - "attributes": - { - "hexvalue": "48656c6c6f20f09f9883", - "isConstant": false, - "isLValue": false, - "isPure": true, - "lValueRequested": false, - "token": "unicodeString", - "type": "literal_string \"Hello \ud83d\ude03\"", - "value": "Hello \ud83d\ude03" - }, - "id": 5, - "name": "Literal", - "src": "53:19:1" - } - ], - "id": 6, - "name": "VariableDeclarationStatement", - "src": "35:37:1" - } - ], - "id": 7, - "name": "Block", - "src": "33:42:1" - } - ], - "id": 8, - "name": "FunctionDefinition", - "src": "13:62:1" - } - ], - "id": 9, - "name": "ContractDefinition", - "src": "0:77:1" - } - ], - "id": 10, - "name": "SourceUnit", - "src": "0:78:1" -} diff --git a/test/libsolidity/ASTJSON/unicode_parseOnly.json b/test/libsolidity/ASTJSON/unicode_parseOnly.json new file mode 100644 index 000000000000..c48013a2e6ad --- /dev/null +++ b/test/libsolidity/ASTJSON/unicode_parseOnly.json @@ -0,0 +1,99 @@ +{ + "absolutePath": "a", + "id": 10, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 9, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 7, + "nodeType": "Block", + "src": "33:42:1", + "statements": + [ + { + "assignments": + [ + 4 + ], + "declarations": + [ + { + "constant": false, + "id": 4, + "mutability": "mutable", + "name": "x", + "nodeType": "VariableDeclaration", + "src": "35:15:1", + "stateVariable": false, + "storageLocation": "memory", + "typeDescriptions": {}, + "typeName": + { + "id": 3, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "35:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 6, + "initialValue": + { + "hexValue": "48656c6c6f20f09f9883", + "id": 5, + "kind": "unicodeString", + "nodeType": "Literal", + "src": "53:19:1", + "typeDescriptions": {}, + "value": "Hello \ud83d\ude03" + }, + "nodeType": "VariableDeclarationStatement", + "src": "35:37:1" + } + ] + }, + "id": 8, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "23:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "33:0:1" + }, + "src": "13:62:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:77:1" + } + ], + "src": "0:78:1" +} diff --git a/test/libsolidity/ASTJSON/using_for_directive.json b/test/libsolidity/ASTJSON/using_for_directive.json index d26922262241..b6ef6a072dd4 100644 --- a/test/libsolidity/ASTJSON/using_for_directive.json +++ b/test/libsolidity/ASTJSON/using_for_directive.json @@ -53,14 +53,9 @@ { "id": 2, "name": "L", - "nodeType": "UserDefinedTypeName", + "nodeType": "IdentifierPath", "referencedDeclaration": 1, - "src": "32:1:1", - "typeDescriptions": - { - "typeIdentifier": "t_contract$_L_$1", - "typeString": "library L" - } + "src": "32:1:1" }, "nodeType": "UsingForDirective", "src": "26:17:1", diff --git a/test/libsolidity/ASTJSON/using_for_directive_legacy.json b/test/libsolidity/ASTJSON/using_for_directive_legacy.json deleted file mode 100644 index bdd92fcbaa31..000000000000 --- a/test/libsolidity/ASTJSON/using_for_directive_legacy.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "attributes": - { - "absolutePath": "a", - "exportedSymbols": - { - "C": - [ - 5 - ], - "L": - [ - 1 - ] - } - }, - "children": - [ - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "library", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 1 - ], - "name": "L", - "nodes": - [ - null - ], - "scope": 6 - }, - "id": 1, - "name": "ContractDefinition", - "src": "0:12:1" - }, - { - "attributes": - { - "abstract": false, - "baseContracts": - [ - null - ], - "contractDependencies": - [ - null - ], - "contractKind": "contract", - "fullyImplemented": true, - "linearizedBaseContracts": - [ - 5 - ], - "name": "C", - "scope": 6 - }, - "children": - [ - { - "children": - [ - { - "attributes": - { - "name": "L", - "referencedDeclaration": 1, - "type": "library L" - }, - "id": 2, - "name": "UserDefinedTypeName", - "src": "32:1:1" - }, - { - "attributes": - { - "name": "uint", - "type": "uint256" - }, - "id": 3, - "name": "ElementaryTypeName", - "src": "38:4:1" - } - ], - "id": 4, - "name": "UsingForDirective", - "src": "26:17:1" - } - ], - "id": 5, - "name": "ContractDefinition", - "src": "13:32:1" - } - ], - "id": 6, - "name": "SourceUnit", - "src": "0:46:1" -} diff --git a/test/libsolidity/ASTJSON/using_for_directive_parseOnly.json b/test/libsolidity/ASTJSON/using_for_directive_parseOnly.json new file mode 100644 index 000000000000..36baafcadfe8 --- /dev/null +++ b/test/libsolidity/ASTJSON/using_for_directive_parseOnly.json @@ -0,0 +1,53 @@ +{ + "absolutePath": "a", + "id": 6, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "library", + "id": 1, + "name": "L", + "nodeType": "ContractDefinition", + "nodes": [], + "src": "0:12:1" + }, + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 5, + "name": "C", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "id": 4, + "libraryName": + { + "id": 2, + "name": "L", + "nodeType": "IdentifierPath", + "src": "32:1:1" + }, + "nodeType": "UsingForDirective", + "src": "26:17:1", + "typeName": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "38:4:1", + "typeDescriptions": {} + } + } + ], + "src": "13:32:1" + } + ], + "src": "0:46:1" +} diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp index 979c79aa5c60..a571d282b9ed 100644 --- a/test/libsolidity/ASTJSONTest.cpp +++ b/test/libsolidity/ASTJSONTest.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,6 +44,8 @@ using namespace boost::unit_test; namespace { +string const sourceDelimiter("==== Source: "); + void replaceVersionWithTag(string& _input) { boost::algorithm::replace_all( @@ -71,7 +73,7 @@ ASTJSONTest::ASTJSONTest(string const& _filename) BOOST_THROW_EXCEPTION(runtime_error("Invalid test contract file name: \"" + _filename + "\".")); m_astFilename = _filename.substr(0, _filename.size() - 4) + ".json"; - m_legacyAstFilename = _filename.substr(0, _filename.size() - 4) + "_legacy.json"; + m_astParseOnlyFilename = _filename.substr(0, _filename.size() - 4) + "_parseOnly.json"; ifstream file(_filename); if (!file) @@ -81,7 +83,6 @@ ASTJSONTest::ASTJSONTest(string const& _filename) string sourceName; string source; string line; - string const sourceDelimiter("// ---- SOURCE: "); string const delimiter("// ----"); while (getline(file, line)) { @@ -90,7 +91,10 @@ ASTJSONTest::ASTJSONTest(string const& _filename) if (!sourceName.empty()) m_sources.emplace_back(sourceName, source); - sourceName = line.substr(sourceDelimiter.size(), string::npos); + sourceName = line.substr( + sourceDelimiter.size(), + line.size() - " ===="s.size() - sourceDelimiter.size() + ); source = string(); } else if (!line.empty() && !boost::algorithm::starts_with(line, delimiter)) @@ -109,13 +113,15 @@ ASTJSONTest::ASTJSONTest(string const& _filename) } file.close(); - file.open(m_legacyAstFilename); + file.open(m_astParseOnlyFilename); if (file) { string line; while (getline(file, line)) - m_expectationLegacy += line + "\n"; + m_expectationParseOnly += line + "\n"; } + + file.close(); } TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) @@ -127,97 +133,123 @@ TestCase::TestResult ASTJSONTest::run(ostream& _stream, string const& _linePrefi for (size_t i = 0; i < m_sources.size(); i++) { sources[m_sources[i].first] = m_sources[i].second; - sourceIndices[m_sources[i].first] = i + 1; + sourceIndices[m_sources[i].first] = static_cast(i + 1); } c.setSources(sources); c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); - if (c.parse()) - c.analyze(); - else + + + if (!c.compile(CompilerStack::State::Parsed)) { - SourceReferenceFormatterHuman formatter(_stream, _formatted, false); + SourceReferenceFormatter formatter(_stream, _formatted, false); for (auto const& error: c.errors()) formatter.printErrorInformation(*error); return TestResult::FatalError; } - if (m_sources.size() > 1) - m_result += "[\n"; + bool resultsMatch = runTest( + m_expectationParseOnly, + m_resultParseOnly, + sourceIndices, + c, + "parseOnly", + _stream, + _linePrefix, + _formatted + ); - for (size_t i = 0; i < m_sources.size(); i++) + c.reset(); + c.setSources(sources); + c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + if (!c.parse()) { - ostringstream result; - ASTJsonConverter(false, sourceIndices).print(result, c.ast(m_sources[i].first)); - m_result += result.str(); - if (i != m_sources.size() - 1) - m_result += ","; - m_result += "\n"; + // Empty Expectations means we expect failure + if (m_expectation.empty()) + return resultsMatch ? TestResult::Success : TestResult::Failure; + + SourceReferenceFormatter formatter(_stream, _formatted, false); + for (auto const& error: c.errors()) + formatter.printErrorInformation(*error); + return TestResult::FatalError; } - if (m_sources.size() > 1) - m_result += "]\n"; + c.analyze(); - bool resultsMatch = true; + resultsMatch = runTest( + m_expectation, + m_result, + sourceIndices, + c, + "", + _stream, + _linePrefix, + _formatted + ) && resultsMatch; - replaceTagWithVersion(m_expectation); + return resultsMatch ? TestResult::Success : TestResult::Failure; +} - if (m_expectation != m_result) - { - string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - { - istringstream stream(m_expectation); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - { - istringstream stream(m_result); - string line; - while (getline(stream, line)) - _stream << nextIndentLevel << line << endl; - } - _stream << endl; - resultsMatch = false; - } +bool ASTJSONTest::runTest( + string& _expectation, + string& _result, + map const& _sourceIndices, + CompilerStack& _compiler, + string const& _variation, + ostream& _stream, + string const& _linePrefix, + bool const _formatted +) +{ + if (m_sources.size() > 1) + _result += "[\n"; for (size_t i = 0; i < m_sources.size(); i++) { ostringstream result; - ASTJsonConverter(true, sourceIndices).print(result, c.ast(m_sources[i].first)); - m_resultLegacy = result.str(); + ASTJsonConverter(_compiler.state(), _sourceIndices).print(result, _compiler.ast(m_sources[i].first)); + _result += result.str(); if (i != m_sources.size() - 1) - m_resultLegacy += ","; - m_resultLegacy += "\n"; + _result += ","; + _result += "\n"; } - replaceTagWithVersion(m_expectationLegacy); + if (m_sources.size() > 1) + _result += "]\n"; + + replaceTagWithVersion(_expectation); - if (m_expectationLegacy != m_resultLegacy) + if (_expectation != _result) { string nextIndentLevel = _linePrefix + " "; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result (legacy):" << endl; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << + _linePrefix << + "Expected result" << + (!_variation.empty() ? " (" + _variation + "):" : ":") << + endl; { - istringstream stream(m_expectationLegacy); + istringstream stream(_expectation); string line; while (getline(stream, line)) _stream << nextIndentLevel << line << endl; } _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result (legacy):" << endl; + + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << + _linePrefix << + "Obtained result" << + (!_variation.empty() ? " (" + _variation + "):" : ":") << + endl; { - istringstream stream(m_resultLegacy); + istringstream stream(_result); string line; while (getline(stream, line)) _stream << nextIndentLevel << line << endl; } _stream << endl; - resultsMatch = false; + return false; } - return resultsMatch ? TestResult::Success : TestResult::Failure; + return true; } void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const @@ -225,7 +257,7 @@ void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool for (auto const& source: m_sources) { if (m_sources.size() > 1 || source.first != "a") - _stream << _linePrefix << "// ---- SOURCE: " << source.first << endl << endl; + _stream << _linePrefix << sourceDelimiter << source.first << " ====" << endl << endl; stringstream stream(source.second); string line; while (getline(stream, line)) @@ -236,24 +268,20 @@ void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) const { - ofstream file(m_astFilename.c_str()); - if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write AST expectation to \"" + m_astFilename + "\".")); + updateExpectation(m_astFilename, m_result, ""); + updateExpectation(m_astParseOnlyFilename, m_resultParseOnly, "parseOnly "); +} + +void ASTJSONTest::updateExpectation(string const& _filename, string const& _expectation, string const& _variation) const +{ + ofstream file(_filename.c_str()); + if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write " + _variation + "AST expectation to \"" + _filename + "\".")); file.exceptions(ios::badbit); - string replacedResult = m_result; + string replacedResult = _expectation; replaceVersionWithTag(replacedResult); file << replacedResult; file.flush(); file.close(); - - file.open(m_legacyAstFilename.c_str()); - if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write legacy AST expectation to \"" + m_legacyAstFilename + "\".")); - - string replacedResultLegacy = m_resultLegacy; - replaceVersionWithTag(replacedResultLegacy); - - file << replacedResultLegacy; - file.flush(); - file.close(); } diff --git a/test/libsolidity/ASTJSONTest.h b/test/libsolidity/ASTJSONTest.h index 4e74d79f376f..ac57277ac3e5 100644 --- a/test/libsolidity/ASTJSONTest.h +++ b/test/libsolidity/ASTJSONTest.h @@ -26,6 +26,11 @@ #include #include +namespace solidity::frontend +{ +class CompilerStack; +} + namespace solidity::frontend::test { @@ -33,7 +38,9 @@ class ASTJSONTest: public TestCase { public: static std::unique_ptr create(Config const& _config) - { return std::make_unique(_config.filename); } + { + return std::make_unique(_config.filename); + } ASTJSONTest(std::string const& _filename); TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; @@ -41,12 +48,28 @@ class ASTJSONTest: public TestCase void printSource(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) const override; void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; private: + bool runTest( + std::string& _expectation, + std::string& _result, + std::map const& _sourceIndices, + CompilerStack& _compiler, + std::string const& _variation, + std::ostream& _stream, + std::string const& _linePrefix = "", + bool const _formatted = false + ); + void updateExpectation( + std::string const& _filename, + std::string const& _expectation, + std::string const& _variation + ) const; + std::vector> m_sources; - std::string m_expectationLegacy; + std::string m_expectationParseOnly; std::string m_astFilename; - std::string m_legacyAstFilename; + std::string m_astParseOnlyFilename; std::string m_result; - std::string m_resultLegacy; + std::string m_resultParseOnly; }; } diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index bd28de2401cd..a88c14581424 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -51,9 +51,11 @@ AnalysisFramework::parseAnalyseAndReturnError( ) { compiler().reset(); + // Do not insert license if it is already present. + bool insertLicense = _insertLicenseAndVersionPragma && _source.find("// SPDX-License-Identifier:") == string::npos; compiler().setSources({{"", - _insertLicenseAndVersionPragma ? - "pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n" + _source : + string{_insertLicenseAndVersionPragma ? "pragma solidity >=0.0;\n" : ""} + + string{insertLicense ? "// SPDX-License-Identifier: GPL-3.0\n" : ""} + _source }}); compiler().setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); @@ -94,7 +96,22 @@ ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _inc continue; } - errors.emplace_back(currentError); + std::shared_ptr newError = currentError; + for (auto const& messagePrefix: m_messagesToCut) + if (currentError->comment()->find(messagePrefix) == 0) + { + SourceLocation const* location = boost::get_error_info(*currentError); + // sufficient for now, but in future we might clone the error completely, including the secondary location + newError = make_shared( + currentError->errorId(), + currentError->type(), + location ? *location : SourceLocation(), + messagePrefix + " ...." + ); + break; + } + + errors.emplace_back(newError); } return errors; diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index 293e0e17c562..f6b9d92da2a2 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -71,6 +71,7 @@ class AnalysisFramework langutil::ErrorList filterErrors(langutil::ErrorList const& _errorList, bool _includeWarnings) const; std::vector m_warningsToFilter = {"This is a pre-release compiler version"}; + std::vector m_messagesToCut = {"Source file requires different compiler version (current compiler is"}; /// @returns reference to lazy-instanciated CompilerStack. solidity::frontend::CompilerStack& compiler() diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 12e088fac1d5..5aae847aaa9a 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -30,9 +30,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -59,6 +61,8 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared(_sourceCode))); BOOST_CHECK(!!sourceUnit); + Scoper::assignScopes(*sourceUnit); + BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); GlobalContext globalContext; NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); @@ -87,7 +91,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) ); compiler.compileContract(*contract, map>{}, bytes()); - return compiler.runtimeAssemblyItems(); + return compiler.runtimeAssembly().items(); } BOOST_FAIL("No contract found in source."); return AssemblyItems(); @@ -154,6 +158,7 @@ BOOST_AUTO_TEST_SUITE(Assembly) BOOST_AUTO_TEST_CASE(location_test) { auto sourceCode = make_shared(R"( + pragma abicoder v1; contract test { function f() public returns (uint256 a) { return 16; @@ -168,18 +173,18 @@ BOOST_AUTO_TEST_CASE(location_test) vector locations; if (solidity::test::CommonOptions::get().optimize) locations = - vector(31, SourceLocation{2, 82, sourceCode}) + - vector(21, SourceLocation{20, 79, sourceCode}) + - vector(1, SourceLocation{72, 74, sourceCode}) + - vector(2, SourceLocation{20, 79, sourceCode}); + vector(31, SourceLocation{23, 103, sourceCode}) + + vector(21, SourceLocation{41, 100, sourceCode}) + + vector(1, SourceLocation{93, 95, sourceCode}) + + vector(2, SourceLocation{41, 100, sourceCode}); else locations = - vector(hasShifts ? 31 : 32, SourceLocation{2, 82, sourceCode}) + - vector(24, SourceLocation{20, 79, sourceCode}) + - vector(1, SourceLocation{49, 58, sourceCode}) + - vector(1, SourceLocation{72, 74, sourceCode}) + - vector(2, SourceLocation{65, 74, sourceCode}) + - vector(2, SourceLocation{20, 79, sourceCode}); + vector(hasShifts ? 31 : 32, SourceLocation{23, 103, sourceCode}) + + vector(24, SourceLocation{41, 100, sourceCode}) + + vector(1, SourceLocation{70, 79, sourceCode}) + + vector(1, SourceLocation{93, 95, sourceCode}) + + vector(2, SourceLocation{86, 95, sourceCode}) + + vector(2, SourceLocation{41, 100, sourceCode}); checkAssemblyLocations(items, locations); } @@ -187,6 +192,7 @@ BOOST_AUTO_TEST_CASE(location_test) BOOST_AUTO_TEST_CASE(jump_type) { auto sourceCode = make_shared(R"( + pragma abicoder v1; contract C { function f(uint a) public pure returns (uint t) { assembly { diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index bb86aaa835cf..e23de065ab40 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -39,18 +39,18 @@ namespace solidity::frontend::test #define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt, _evmVersion) \ do \ { \ - u256 ipfsCost = GasMeter::dataGas(util::ipfsHash(m_compiler.metadata(m_compiler.lastContractName())), true, _evmVersion); \ + u256 metaCost = GasMeter::dataGas(m_compiler.cborMetadata(m_compiler.lastContractName()), true, _evmVersion); \ u256 gasOpt{_gasOpt}; \ u256 gasNoOpt{_gasNoOpt}; \ u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \ BOOST_CHECK_MESSAGE( \ - m_gasUsed >= ipfsCost, \ + m_gasUsed >= metaCost, \ "Gas used: " + \ m_gasUsed.str() + \ - " is less than the data cost for the IPFS hash: " + \ - u256(ipfsCost).str() \ + " is less than the data cost for the cbor metadata: " + \ + u256(metaCost).str() \ ); \ - u256 gasUsed = m_gasUsed - ipfsCost; \ + u256 gasUsed = m_gasUsed - metaCost; \ BOOST_CHECK_MESSAGE( \ gas == gasUsed, \ "Gas used: " + \ @@ -97,38 +97,43 @@ BOOST_AUTO_TEST_CASE(string_storage) auto evmVersion = solidity::test::CommonOptions::get().evmVersion(); if (evmVersion <= EVMVersion::byzantium()) - CHECK_DEPLOY_GAS(134145, 130831, evmVersion); + { + if (CommonOptions::get().useABIEncoderV1) + CHECK_DEPLOY_GAS(133045, 129731, evmVersion); + else + CHECK_DEPLOY_GAS(152657, 135201, evmVersion); + } // This is only correct on >=Constantinople. - else if (CommonOptions::get().useABIEncoderV2) + else if (!CommonOptions::get().useABIEncoderV1) { if (CommonOptions::get().optimize) { // Costs with 0 are cases which cannot be triggered in tests. if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(0, 123969, evmVersion); + CHECK_DEPLOY_GAS(0, 122869, evmVersion); else - CHECK_DEPLOY_GAS(0, 110969, evmVersion); + CHECK_DEPLOY_GAS(0, 110701, evmVersion); } else { if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(147835, 123969, evmVersion); + CHECK_DEPLOY_GAS(146671, 123969, evmVersion); else - CHECK_DEPLOY_GAS(131871, 110969, evmVersion); + CHECK_DEPLOY_GAS(131591, 110969, evmVersion); } } else if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(126929, 119659, evmVersion); + CHECK_DEPLOY_GAS(125829, 118559, evmVersion); else - CHECK_DEPLOY_GAS(114345, 107335, evmVersion); + CHECK_DEPLOY_GAS(114077, 107067, evmVersion); if (evmVersion >= EVMVersion::byzantium()) { callContractFunction("f()"); if (evmVersion == EVMVersion::byzantium()) - CHECK_GAS(21545, 21526, 20); + CHECK_GAS(21712, 21555, 20); // This is only correct on >=Constantinople. - else if (CommonOptions::get().useABIEncoderV2) + else if (!CommonOptions::get().useABIEncoderV1) { if (CommonOptions::get().optimize) { @@ -162,10 +167,10 @@ BOOST_AUTO_TEST_CASE(single_callvaluecheck) a = b; } function f1(address b) public pure returns (uint c) { - return uint(b) + 2; + return uint160(b) + 2; } function f2(address b) public pure returns (uint) { - return uint(b) + 8; + return uint160(b) + 8; } function f3(address, uint c) pure public returns (uint) { return c - 5; @@ -178,10 +183,10 @@ BOOST_AUTO_TEST_CASE(single_callvaluecheck) a = b; } function f1(address b) public pure returns (uint c) { - return uint(b) + 2; + return uint160(b) + 2; } function f2(address b) public pure returns (uint) { - return uint(b) + 8; + return uint160(b) + 8; } function f3(address, uint c) payable public returns (uint) { return c - 5; diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 412dfb9d7fc4..3e545ac63cf5 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -27,7 +27,6 @@ #include #include #include -#include using namespace std; using namespace solidity::langutil; @@ -49,14 +48,6 @@ class GasMeterTestFramework: public SolidityExecutionFramework m_compiler.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); m_compiler.setEVMVersion(m_evmVersion); BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - - AssemblyItems const* items = m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()); - ASTNode const& sourceUnit = m_compiler.ast(""); - BOOST_REQUIRE(items != nullptr); - m_gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator(solidity::test::CommonOptions::get().evmVersion()).structuralEstimation(*items, vector({&sourceUnit})), - {&sourceUnit} - ); } void testCreationTimeGas(string const& _sourceCode, u256 const& _tolerance = u256(0)) @@ -71,9 +62,9 @@ class GasMeterTestFramework: public SolidityExecutionFramework // costs for transaction gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true); - // Skip the tests when we force ABIEncoderV2. + // Skip the tests when we use ABIEncoderV2. // TODO: We should enable this again once the yul optimizer is activated. - if (!solidity::test::CommonOptions::get().useABIEncoderV2) + if (solidity::test::CommonOptions::get().useABIEncoderV1) { BOOST_REQUIRE(!gas.isInfinite); BOOST_CHECK_LE(m_gasUsed, gas.value); @@ -100,9 +91,9 @@ class GasMeterTestFramework: public SolidityExecutionFramework *m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()), _sig ); - // Skip the tests when we force ABIEncoderV2. + // Skip the tests when we use ABIEncoderV2. // TODO: We should enable this again once the yul optimizer is activated. - if (!solidity::test::CommonOptions::get().useABIEncoderV2) + if (solidity::test::CommonOptions::get().useABIEncoderV1) { BOOST_REQUIRE(!gas.isInfinite); BOOST_CHECK_LE(m_gasUsed, gas.value); @@ -118,43 +109,10 @@ class GasMeterTestFramework: public SolidityExecutionFramework gas += i != 0 ? GasCosts::txDataNonZeroGas(evmVersion) : GasCosts::txDataZeroGas; return gas; } - -protected: - map m_gasCosts; }; BOOST_FIXTURE_TEST_SUITE(GasMeterTests, GasMeterTestFramework) -BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) -{ - char const* sourceCode = R"( - contract test { - bytes x; - function f(uint a) public returns (uint b) { - for (; a < 200; ++a) { - x.push(0x09); - b = a * a; - } - return f(a - 1); - } - } - )"; - compile(sourceCode); - for (auto first = m_gasCosts.cbegin(); first != m_gasCosts.cend(); ++first) - { - auto second = first; - for (++second; second != m_gasCosts.cend(); ++second) - if (first->first->location().intersects(second->first->location())) - { - BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!"); - langutil::SourceReferenceFormatter formatter(cout); - - formatter.printSourceLocation(&first->first->location()); - formatter.printSourceLocation(&second->first->location()); - } - } -} - BOOST_AUTO_TEST_CASE(simple_contract) { // Tests a simple "deploy contract" code without constructor. The actual contract is not relevant. @@ -224,7 +182,7 @@ BOOST_AUTO_TEST_CASE(function_calls) uint data2; function f(uint x) public { if (x > 7) - data2 = g(x**8) + 1; + { unchecked { data2 = g(x**8) + 1; } } else data = 1; } @@ -245,7 +203,7 @@ BOOST_AUTO_TEST_CASE(multiple_external_functions) uint data2; function f(uint x) public { if (x > 7) - data2 = g(x**8) + 1; + { unchecked { data2 = g(x**8) + 1; } } else data = 1; } @@ -264,13 +222,13 @@ BOOST_AUTO_TEST_CASE(exponent_size) char const* sourceCode = R"( contract A { function f(uint x) public returns (uint) { - return x ** 0; + unchecked { return x ** 0; } } function g(uint x) public returns (uint) { - return x ** 0x100; + unchecked { return x ** 0x100; } } function h(uint x) public returns (uint) { - return x ** 0x10000; + unchecked { return x ** 0x10000; } } } )"; @@ -330,29 +288,31 @@ BOOST_AUTO_TEST_CASE(complex_control_flow) char const* sourceCode = R"( contract log { function ln(int128 x) public pure returns (int128 result) { - int128 t = x / 256; - int128 y = 5545177; - x = t; - t = x * 16; if (t <= 1000000) { x = t; y = y - 2772588; } - t = x * 4; if (t <= 1000000) { x = t; y = y - 1386294; } - t = x * 2; if (t <= 1000000) { x = t; y = y - 693147; } - t = x + x / 2; if (t <= 1000000) { x = t; y = y - 405465; } - t = x + x / 4; if (t <= 1000000) { x = t; y = y - 223144; } - t = x + x / 8; if (t <= 1000000) { x = t; y = y - 117783; } - t = x + x / 16; if (t <= 1000000) { x = t; y = y - 60624; } - t = x + x / 32; if (t <= 1000000) { x = t; y = y - 30771; } - t = x + x / 64; if (t <= 1000000) { x = t; y = y - 15504; } - t = x + x / 128; if (t <= 1000000) { x = t; y = y - 7782; } - t = x + x / 256; if (t <= 1000000) { x = t; y = y - 3898; } - t = x + x / 512; if (t <= 1000000) { x = t; y = y - 1951; } - t = x + x / 1024; if (t <= 1000000) { x = t; y = y - 976; } - t = x + x / 2048; if (t <= 1000000) { x = t; y = y - 488; } - t = x + x / 4096; if (t <= 1000000) { x = t; y = y - 244; } - t = x + x / 8192; if (t <= 1000000) { x = t; y = y - 122; } - t = x + x / 16384; if (t <= 1000000) { x = t; y = y - 61; } - t = x + x / 32768; if (t <= 1000000) { x = t; y = y - 31; } - t = x + x / 65536; if (t <= 1000000) { y = y - 15; } - return y; + unchecked { + int128 t = x / 256; + int128 y = 5545177; + x = t; + t = x * 16; if (t <= 1000000) { x = t; y = y - 2772588; } + t = x * 4; if (t <= 1000000) { x = t; y = y - 1386294; } + t = x * 2; if (t <= 1000000) { x = t; y = y - 693147; } + t = x + x / 2; if (t <= 1000000) { x = t; y = y - 405465; } + t = x + x / 4; if (t <= 1000000) { x = t; y = y - 223144; } + t = x + x / 8; if (t <= 1000000) { x = t; y = y - 117783; } + t = x + x / 16; if (t <= 1000000) { x = t; y = y - 60624; } + t = x + x / 32; if (t <= 1000000) { x = t; y = y - 30771; } + t = x + x / 64; if (t <= 1000000) { x = t; y = y - 15504; } + t = x + x / 128; if (t <= 1000000) { x = t; y = y - 7782; } + t = x + x / 256; if (t <= 1000000) { x = t; y = y - 3898; } + t = x + x / 512; if (t <= 1000000) { x = t; y = y - 1951; } + t = x + x / 1024; if (t <= 1000000) { x = t; y = y - 976; } + t = x + x / 2048; if (t <= 1000000) { x = t; y = y - 488; } + t = x + x / 4096; if (t <= 1000000) { x = t; y = y - 244; } + t = x + x / 8192; if (t <= 1000000) { x = t; y = y - 122; } + t = x + x / 16384; if (t <= 1000000) { x = t; y = y - 61; } + t = x + x / 32768; if (t <= 1000000) { x = t; y = y - 31; } + t = x + x / 65536; if (t <= 1000000) { y = y - 15; } + return y; + } } } )"; diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index f996f7352283..4427c7dd61cd 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -119,7 +119,7 @@ TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, b if (!compiler().parseAndAnalyze() || !compiler().compile()) { - SourceReferenceFormatterHuman formatter(_stream, _formatted, false); + SourceReferenceFormatter formatter(_stream, _formatted, false); for (auto const& error: compiler().errors()) formatter.printErrorInformation(*error); return TestResult::FatalError; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 1d61484a9c48..af7bbd831e14 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -170,343 +170,7 @@ do { successParse((text), false, false, AssemblyStack::Language::StrictAssembly) BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly) - -BOOST_AUTO_TEST_SUITE(Parsing) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - BOOST_CHECK(successParse("{ }")); -} - -BOOST_AUTO_TEST_CASE(surplus_input) -{ - CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected end of source but got '{'"); -} - -BOOST_AUTO_TEST_CASE(simple_instructions) -{ - BOOST_CHECK(successParse("{ let y := mul(0x10, mul(0x20, mload(0x40)))}")); -} - -BOOST_AUTO_TEST_CASE(selfdestruct) -{ - BOOST_CHECK(successParse("{ selfdestruct(0x02) }")); -} - -BOOST_AUTO_TEST_CASE(keywords) -{ - BOOST_CHECK(successParse("{ return (byte(1, 2), 2) pop(address()) }")); -} - -BOOST_AUTO_TEST_CASE(constants) -{ - BOOST_CHECK(successParse("{ pop(mul(7, 8)) }")); -} - -BOOST_AUTO_TEST_CASE(vardecl) -{ - BOOST_CHECK(successParse("{ let x := 7 }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_name_clashes) -{ - CHECK_PARSE_ERROR("{ let x := 1 let x := 2 }", DeclarationError, "Variable name x already taken in this scope."); -} - -BOOST_AUTO_TEST_CASE(vardecl_multi) -{ - BOOST_CHECK(successParse("{ function f() -> x, y {} let x, y := f() }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_multi_conflict) -{ - CHECK_PARSE_ERROR("{ function f() -> x, y {} let x, x := f() }", DeclarationError, "Variable name x already taken in this scope."); -} - -BOOST_AUTO_TEST_CASE(vardecl_bool) -{ - successParse("{ let x := true }"); - successParse("{ let x := false }"); -} - -BOOST_AUTO_TEST_CASE(vardecl_empty) -{ - BOOST_CHECK(successParse("{ let x }")); -} - -BOOST_AUTO_TEST_CASE(functional) -{ - BOOST_CHECK(successParse("{ let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) }")); -} - -BOOST_AUTO_TEST_CASE(functional_partial) -{ - CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected '(' but got '}'"); -} - -BOOST_AUTO_TEST_CASE(functional_partial_success) -{ - BOOST_CHECK(successParse("{ let x := byte(1, 2) }")); -} - -BOOST_AUTO_TEST_CASE(functional_assignment) -{ - BOOST_CHECK(successParse("{ let x := 2 x := 7 }")); -} - -BOOST_AUTO_TEST_CASE(functional_assignment_complex) -{ - BOOST_CHECK(successParse("{ let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_complex) -{ - BOOST_CHECK(successParse("{ let y := 2 let x := add(add(7, mul(6, y)), mul(7, 8)) }")); -} - -BOOST_AUTO_TEST_CASE(variable_use_before_decl) -{ - CHECK_PARSE_ERROR("{ x := 2 let x := 3 }", DeclarationError, "Variable x used before it was declared."); - CHECK_PARSE_ERROR("{ let x := mul(2, x) }", DeclarationError, "Variable x used before it was declared."); -} - -BOOST_AUTO_TEST_CASE(if_statement) -{ - BOOST_CHECK(successParse("{ if 42 {} }")); - BOOST_CHECK(successParse("{ if 42 { let x := 3 } }")); - BOOST_CHECK(successParse("{ function f() -> x {} if f() { pop(f()) } }")); -} - -BOOST_AUTO_TEST_CASE(if_statement_scope) -{ - BOOST_CHECK(successParse("{ let x := 2 if 42 { x := 3 } }")); - CHECK_PARSE_ERROR("{ if 32 { let x := 3 } x := 2 }", DeclarationError, "Variable not found or variable not lvalue."); -} - -BOOST_AUTO_TEST_CASE(if_statement_invalid) -{ - CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' but got '{'"); - BOOST_CHECK("{ if calldatasize() {}"); - CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead."); - CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected '{' but got reserved keyword 'let'"); -} - -BOOST_AUTO_TEST_CASE(switch_statement) -{ - BOOST_CHECK(successParse("{ switch 42 default {} }")); - BOOST_CHECK(successParse("{ switch 42 case 1 {} }")); - BOOST_CHECK(successParse("{ switch 42 case 1 {} case 2 {} }")); - BOOST_CHECK(successParse("{ switch 42 case 1 {} default {} }")); - BOOST_CHECK(successParse("{ switch 42 case 1 {} case 2 {} default {} }")); - BOOST_CHECK(successParse("{ switch mul(1, 2) case 1 {} case 2 {} default {} }")); - BOOST_CHECK(successParse("{ function f() -> x {} switch f() case 1 {} case 2 {} default {} }")); -} - -BOOST_AUTO_TEST_CASE(switch_no_cases) -{ - CHECK_PARSE_ERROR("{ switch 42 }", ParserError, "Switch statement without any cases."); -} - -BOOST_AUTO_TEST_CASE(switch_duplicate_case) -{ - CHECK_PARSE_ERROR("{ switch 42 case 1 {} case 1 {} default {} }", DeclarationError, "Duplicate case defined."); -} - -BOOST_AUTO_TEST_CASE(switch_invalid_expression) -{ - CHECK_PARSE_ERROR("{ switch {} case 1 {} default {} }", ParserError, "Literal or identifier expected."); - CHECK_PARSE_ERROR( - "{ switch mload case 1 {} default {} }", - ParserError, - "Expected '(' but got reserved keyword 'case'" - ); - CHECK_PARSE_ERROR( - "{ switch mstore(1, 1) case 1 {} default {} }", - TypeError, - "Expected expression to evaluate to one value, but got 0 values instead." - ); -} - -BOOST_AUTO_TEST_CASE(switch_default_before_case) -{ - CHECK_PARSE_ERROR("{ switch 42 default {} case 1 {} }", ParserError, "Case not allowed after default case."); -} - -BOOST_AUTO_TEST_CASE(switch_duplicate_default_case) -{ - CHECK_PARSE_ERROR("{ switch 42 default {} default {} }", ParserError, "Only one default case allowed."); -} - -BOOST_AUTO_TEST_CASE(switch_invalid_case) -{ - CHECK_PARSE_ERROR("{ switch 42 case mul(1, 2) {} case 2 {} default {} }", ParserError, "Literal expected."); -} - -BOOST_AUTO_TEST_CASE(switch_invalid_body) -{ - CHECK_PARSE_ERROR("{ switch 42 case 1 mul case 2 {} default {} }", ParserError, "Expected '{' but got identifier"); -} - -BOOST_AUTO_TEST_CASE(for_statement) -{ - BOOST_CHECK(successParse("{ for {} 1 {} {} }")); - BOOST_CHECK(successParse("{ for { let i := 1 } lt(i, 5) { i := add(i, 1) } {} }")); -} - -BOOST_AUTO_TEST_CASE(for_invalid_expression) -{ - CHECK_PARSE_ERROR("{ for {} {} {} {} }", ParserError, "Literal or identifier expected."); - CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected '{' but got 'Number'"); - CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'"); - CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'"); - CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' but got '{'"); - CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", TypeError, "Expected expression to evaluate to one value, but got 0 values instead."); -} - -BOOST_AUTO_TEST_CASE(for_visibility) -{ - BOOST_CHECK(successParse("{ for { let i := 1 } i { pop(i) } { pop(i) } }")); - CHECK_PARSE_ERROR("{ for {} i { let i := 1 } {} }", DeclarationError, "Identifier not found"); - CHECK_PARSE_ERROR("{ for {} 1 { let i := 1 } { pop(i) } }", DeclarationError, "Identifier not found"); - CHECK_PARSE_ERROR("{ for {} 1 { pop(i) } { let i := 1 } }", DeclarationError, "Identifier not found"); - CHECK_PARSE_ERROR("{ for { pop(i) } 1 { let i := 1 } {} }", DeclarationError, "Identifier not found"); - CHECK_PARSE_ERROR("{ for { pop(i) } 1 { } { let i := 1 } }", DeclarationError, "Identifier not found"); - CHECK_PARSE_ERROR("{ for {} i {} { let i := 1 } }", DeclarationError, "Identifier not found"); - CHECK_PARSE_ERROR("{ for {} 1 { pop(i) } { let i := 1 } }", DeclarationError, "Identifier not found"); - CHECK_PARSE_ERROR("{ for { let x := 1 } 1 { let x := 1 } {} }", DeclarationError, "Variable name x already taken in this scope"); - CHECK_PARSE_ERROR("{ for { let x := 1 } 1 {} { let x := 1 } }", DeclarationError, "Variable name x already taken in this scope"); - CHECK_PARSE_ERROR("{ let x := 1 for { let x := 1 } 1 {} {} }", DeclarationError, "Variable name x already taken in this scope"); - CHECK_PARSE_ERROR("{ let x := 1 for {} 1 { let x := 1 } {} }", DeclarationError, "Variable name x already taken in this scope"); - CHECK_PARSE_ERROR("{ let x := 1 for {} 1 {} { let x := 1 } }", DeclarationError, "Variable name x already taken in this scope"); - // Check that body and post are not sub-scopes of each other. - BOOST_CHECK(successParse("{ for {} 1 { let x := 1 } { let x := 1 } }")); -} - -BOOST_AUTO_TEST_CASE(blocks) -{ - BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); -} - -BOOST_AUTO_TEST_CASE(number_literals) -{ - BOOST_CHECK(successParse("{ let x := 1 }")); - CHECK_PARSE_ERROR("{ let x := .1 }", ParserError, "Invalid number literal."); - CHECK_PARSE_ERROR("{ let x := 1e5 }", ParserError, "Invalid number literal."); - CHECK_PARSE_ERROR("{ let x := 67.235 }", ParserError, "Invalid number literal."); - CHECK_STRICT_ERROR("{ let x := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", TypeError, "Number literal too large (> 256 bits)"); -} - -BOOST_AUTO_TEST_CASE(function_definitions) -{ - BOOST_CHECK(successParse("{ function f() { } function g(a) -> x { } }")); -} - -BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) -{ - BOOST_CHECK(successParse("{ function f(a, d) { } function g(a, d) -> x, y { } }")); -} - -BOOST_AUTO_TEST_CASE(function_calls) -{ - BOOST_CHECK(successParse("{ function f(a) -> b {} function g(a, b, c) {} function x() { g(1, 2, f(mul(2, 3))) x() } }")); -} - -BOOST_AUTO_TEST_CASE(opcode_for_functions) -{ - CHECK_PARSE_ERROR("{ function gas() { } }", ParserError, "Cannot use builtin"); -} - -BOOST_AUTO_TEST_CASE(opcode_for_function_args) -{ - CHECK_PARSE_ERROR("{ function f(gas) { } }", ParserError, "Cannot use builtin"); - CHECK_PARSE_ERROR("{ function f() -> gas { } }", ParserError, "Cannot use builtin"); -} - -BOOST_AUTO_TEST_CASE(name_clashes) -{ - CHECK_PARSE_ERROR("{ let g := 2 function g() { } }", DeclarationError, "Variable name g already taken in this scope"); -} - -BOOST_AUTO_TEST_CASE(name_clashes_function_subscope) -{ - CHECK_PARSE_ERROR("{ function g() { function g() {} } }", DeclarationError, "Function name g already taken in this scope"); -} - -BOOST_AUTO_TEST_CASE(name_clashes_function_subscope_reverse) -{ - CHECK_PARSE_ERROR("{ { function g() {} } function g() { } }", DeclarationError, "Function name g already taken in this scope"); -} - -BOOST_AUTO_TEST_CASE(name_clashes_function_variable_subscope) -{ - CHECK_PARSE_ERROR("{ function g() { let g := 0 } }", DeclarationError, "Variable name g already taken in this scope"); -} - -BOOST_AUTO_TEST_CASE(name_clashes_function_variable_subscope_reverse) -{ - CHECK_PARSE_ERROR("{ { let g := 0 } function g() { } }", DeclarationError, "Variable name g already taken in this scope"); -} -BOOST_AUTO_TEST_CASE(functions_in_parallel_scopes) -{ - BOOST_CHECK(successParse("{ { function g() {} } { function g() {} } }")); -} - -BOOST_AUTO_TEST_CASE(variable_access_cross_functions) -{ - CHECK_PARSE_ERROR("{ let x := 2 function g() { pop(x) } }", DeclarationError, "Identifier not found."); -} - -BOOST_AUTO_TEST_CASE(invalid_tuple_assignment) -{ - CHECK_PARSE_ERROR("{ let x, y := 1 }", DeclarationError, "Variable count mismatch: 2 variables and 1 values"); -} - -BOOST_AUTO_TEST_CASE(instruction_too_few_arguments) -{ - CHECK_PARSE_ERROR("{ pop(mul()) }", TypeError, "Function expects 2 arguments but got 0."); - CHECK_PARSE_ERROR("{ pop(mul(1)) }", TypeError, "Function expects 2 arguments but got 1."); -} - -BOOST_AUTO_TEST_CASE(instruction_too_many_arguments) -{ - CHECK_PARSE_ERROR("{ pop(mul(1, 2, 3)) }", TypeError, "Function expects 2 arguments but got 3"); -} - -BOOST_AUTO_TEST_CASE(recursion_depth) -{ - string input; - for (size_t i = 0; i < 20000; i++) - input += "{"; - input += "let x := 0"; - for (size_t i = 0; i < 20000; i++) - input += "}"; - - CHECK_PARSE_ERROR(input, ParserError, "recursion"); -} - -BOOST_AUTO_TEST_CASE(multiple_assignment) -{ - CHECK_PARSE_ERROR("{ let x function f() -> a, b {} 123, x := f() }", ParserError, "Variable name must precede \",\" in multiple assignment."); - CHECK_PARSE_ERROR("{ let x function f() -> a, b {} x, 123 := f() }", ParserError, "Variable name must precede \":=\" in assignment."); - - /// NOTE: Travis hiccups if not having a variable - char const* text = R"( - { - function f(a) -> r1, r2 { - r1 := a - r2 := 7 - } - let x := 9 - let y := 2 - x, y := f(x) - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(Printing) +BOOST_AUTO_TEST_SUITE(Printing) // {{{ BOOST_AUTO_TEST_CASE(print_smoke) { @@ -593,8 +257,9 @@ BOOST_AUTO_TEST_CASE(function_calls) } BOOST_AUTO_TEST_SUITE_END() +// }}} -BOOST_AUTO_TEST_SUITE(Analysis) +BOOST_AUTO_TEST_SUITE(Analysis) // {{{ BOOST_AUTO_TEST_CASE(string_literals) { @@ -633,50 +298,6 @@ BOOST_AUTO_TEST_CASE(revert) BOOST_CHECK(successAssemble("{ revert(0, 0) }")); } -BOOST_AUTO_TEST_CASE(function_calls) -{ - BOOST_CHECK(successAssemble("{ function f() {} }")); - BOOST_CHECK(successAssemble("{ function f() { let y := 2 } }")); - BOOST_CHECK(successAssemble("{ function f() -> z { let y := 2 } }")); - BOOST_CHECK(successAssemble("{ function f(a) { let y := 2 } }")); - BOOST_CHECK(successAssemble("{ function f(a) { let y := a } }")); - BOOST_CHECK(successAssemble("{ function f() -> x, y, z {} }")); - BOOST_CHECK(successAssemble("{ function f(x, y, z) {} }")); - BOOST_CHECK(successAssemble("{ function f(a, b) -> x, y, z { y := a } }")); - BOOST_CHECK(successAssemble("{ function f() {} f() }")); - BOOST_CHECK(successAssemble("{ function f() -> x, y { x := 1 y := 2} let a, b := f() }")); - BOOST_CHECK(successAssemble("{ function f(a, b) -> x, y { x := b y := a } let a, b := f(2, 3) }")); - BOOST_CHECK(successAssemble("{ function rec(a) { rec(sub(a, 1)) } rec(2) }")); - BOOST_CHECK(successAssemble("{ let r := 2 function f() -> x, y { x := 1 y := 2} let a, b := f() b := r }")); - BOOST_CHECK(successAssemble("{ function f() { g() } function g() { f() } }")); -} - -BOOST_AUTO_TEST_CASE(embedded_functions) -{ - BOOST_CHECK(successAssemble("{ function f(r, s) -> x { function g(a) -> b { } x := g(2) } let x := f(2, 3) }")); -} - -BOOST_AUTO_TEST_CASE(switch_statement) -{ - BOOST_CHECK(successAssemble("{ switch 1 default {} }")); - BOOST_CHECK(successAssemble("{ switch 1 case 1 {} default {} }")); - BOOST_CHECK(successAssemble("{ switch 1 case 1 {} }")); - BOOST_CHECK(successAssemble("{ let a := 3 switch a case 1 { a := 1 } case 2 { a := 5 } a := 9}")); - BOOST_CHECK(successAssemble("{ let a := 2 switch calldataload(0) case 1 { a := 1 } case 2 { a := 5 } }")); -} - -BOOST_AUTO_TEST_CASE(for_statement) -{ - BOOST_CHECK(successAssemble("{ for {} 1 {} {} }")); - BOOST_CHECK(successAssemble("{ let x := calldatasize() for { let i := 0} lt(i, x) { i := add(i, 1) } { mstore(i, 2) } }")); -} - -BOOST_AUTO_TEST_CASE(if_statement) -{ - BOOST_CHECK(successAssemble("{ if 1 {} }")); - BOOST_CHECK(successAssemble("{ let x := 0 if eq(calldatasize(), 0) { x := 1 } mstore(0, x) }")); -} - BOOST_AUTO_TEST_CASE(large_constant) { auto source = R"({ @@ -706,13 +327,6 @@ BOOST_AUTO_TEST_CASE(returndatacopy) BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }")); } -BOOST_AUTO_TEST_CASE(returndatacopy_functional) -{ - if (!solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - return; - BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }")); -} - BOOST_AUTO_TEST_CASE(staticcall) { if (!solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) @@ -745,13 +359,7 @@ BOOST_AUTO_TEST_CASE(shift_constantinople_warning) CHECK_PARSE_WARNING("{ sar(10, 32) }", TypeError, "The \"sar\" instruction is only available for Constantinople-compatible VMs"); } -BOOST_AUTO_TEST_CASE(jump_error) -{ - CHECK_PARSE_WARNING("{ jump(44) }", DeclarationError, "Function not found."); - CHECK_PARSE_WARNING("{ jumpi(44, 2) }", DeclarationError, "Function not found."); -} - -BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() // }}} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 665a175908b9..b90d11d09f55 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -282,6 +282,38 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent) check(sourceCode, false); } +BOOST_AUTO_TEST_CASE(metadata_viair) +{ + char const* sourceCode = R"( + pragma solidity >=0.0; + contract test { + } + )"; + + auto check = [](char const* _src, bool _viair) + { + CompilerStack compilerStack; + compilerStack.setSources({{"", std::string(_src)}}); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); + compilerStack.setViaIR(_viair); + BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); + string metadata_str = compilerStack.metadata("test"); + Json::Value metadata; + util::jsonParseStrict(metadata_str, metadata); + BOOST_CHECK(solidity::test::isValidMetadata(metadata_str)); + BOOST_CHECK(metadata.isMember("settings")); + if (_viair) + { + BOOST_CHECK(metadata["settings"].isMember("viaIR")); + BOOST_CHECK(metadata["settings"]["viaIR"].asBool()); + } + }; + + check(sourceCode, true); + check(sourceCode, false); +} + BOOST_AUTO_TEST_CASE(metadata_revert_strings) { CompilerStack compilerStack; diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp deleted file mode 100644 index a7347a21a628..000000000000 --- a/test/libsolidity/SMTChecker.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -/** - * Unit tests for the SMT checker. - */ - -#include -#include - -#include - -#include - -using namespace std; -using namespace solidity::langutil; - -namespace solidity::frontend::test -{ - -class SMTCheckerFramework: public AnalysisFramework -{ -protected: - std::pair - parseAnalyseAndReturnError( - std::string const& _source, - bool _reportWarnings = false, - bool _insertLicenseAndVersionPragma = true, - bool _allowMultipleErrors = false, - bool _allowRecoveryErrors = false - ) override - { - return AnalysisFramework::parseAnalyseAndReturnError( - "pragma experimental SMTChecker;\n" + _source, - _reportWarnings, - _insertLicenseAndVersionPragma, - _allowMultipleErrors, - _allowRecoveryErrors - ); - } -}; - -BOOST_FIXTURE_TEST_SUITE(SMTChecker, SMTCheckerFramework) - -BOOST_AUTO_TEST_CASE(import_base, *boost::unit_test::label("no_options")) -{ - CompilerStack c; - c.setSources({ - {"base", R"( - pragma solidity >=0.0; - contract Base { - uint x; - address a; - function f() internal returns (uint) { - a = address(this); - ++x; - return 2; - } - } - )"}, - {"der", R"( - pragma solidity >=0.0; - pragma experimental SMTChecker; - import "base"; - contract Der is Base { - function g(uint y) public { - x += f(); - assert(y > x); - } - } - )"} - }); - c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); - BOOST_CHECK(c.compile()); - - unsigned asserts = 0; - for (auto const& e: c.errors()) - { - string const* msg = e->comment(); - BOOST_REQUIRE(msg); - if (msg->find("Assertion violation") != string::npos) - ++asserts; - } - BOOST_CHECK_EQUAL(asserts, 1); -} - -BOOST_AUTO_TEST_CASE(import_library, *boost::unit_test::label("no_options")) -{ - CompilerStack c; - c.setSources({ - {"lib", R"( - pragma solidity >=0.0; - library L { - uint constant one = 1; - function f() internal pure returns (uint) { - return one; - } - } - )"}, - {"c", R"( - pragma solidity >=0.0; - pragma experimental SMTChecker; - import "lib"; - contract C { - function g(uint x) public pure { - uint y = L.f(); - assert(x > y); - } - } - )"} - }); - c.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); - BOOST_CHECK(c.compile()); - - unsigned asserts = 0; - for (auto const& e: c.errors()) - { - string const* msg = e->comment(); - BOOST_REQUIRE(msg); - if (msg->find("Assertion violation") != string::npos) - ++asserts; - } - BOOST_CHECK_EQUAL(asserts, 1); - -} - - -BOOST_AUTO_TEST_SUITE_END() - -} diff --git a/test/libsolidity/SMTCheckerJSONTest.cpp b/test/libsolidity/SMTCheckerJSONTest.cpp deleted file mode 100644 index ca6608905d12..000000000000 --- a/test/libsolidity/SMTCheckerJSONTest.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see . -*/ -// SPDX-License-Identifier: GPL-3.0 - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -using namespace std; -using namespace solidity; -using namespace solidity::frontend; -using namespace solidity::frontend::test; -using namespace solidity::util; -using namespace solidity::util::formatting; -using namespace boost::unit_test; - -SMTCheckerJSONTest::SMTCheckerJSONTest(string const& _filename, langutil::EVMVersion _evmVersion) -: SyntaxTest(_filename, _evmVersion) -{ - if (!boost::algorithm::ends_with(_filename, ".sol")) - BOOST_THROW_EXCEPTION(runtime_error("Invalid test contract file name: \"" + _filename + "\".")); - - string jsonFilename = _filename.substr(0, _filename.size() - 4) + ".json"; - if ( - !jsonParseStrict(readFileAsString(jsonFilename), m_smtResponses) || - !m_smtResponses.isObject() - ) - BOOST_THROW_EXCEPTION(runtime_error("Invalid JSON file.")); - - if (ModelChecker::availableSolvers().none()) - m_shouldRun = false; -} - -TestCase::TestResult SMTCheckerJSONTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) -{ - StandardCompiler compiler; - - // Run the compiler and retrieve the smtlib2queries (1st run) - string preamble = "pragma solidity >=0.0;\n// SPDX-License-Identifier: GPL-3.0\n"; - Json::Value input = buildJson(preamble); - Json::Value result = compiler.compile(input); - - // This is the list of query hashes requested by the 1st run - vector outHashes = hashesFromJson(result, "auxiliaryInputRequested", "smtlib2queries"); - - // This is the list of responses provided in the test - string auxInput("auxiliaryInput"); - if (!m_smtResponses.isMember(auxInput)) - BOOST_THROW_EXCEPTION(runtime_error("JSON file does not contain field \"auxiliaryInput\".")); - - vector inHashes = hashesFromJson(m_smtResponses, auxInput, "smtlib2responses"); - - // Ensure that the provided list matches the requested one - if (outHashes != inHashes) - BOOST_THROW_EXCEPTION(runtime_error( - "SMT query hashes differ: " + - boost::algorithm::join(outHashes, ", ") + - " x " + - boost::algorithm::join(inHashes, ", ") - )); - - // Rerun the compiler with the provided hashed (2nd run) - input[auxInput] = m_smtResponses[auxInput]; - Json::Value endResult = compiler.compile(input); - - if (endResult.isMember("errors") && endResult["errors"].isArray()) - { - Json::Value const& errors = endResult["errors"]; - for (auto const& error: errors) - { - if ( - !error.isMember("type") || - !error["type"].isString() - ) - BOOST_THROW_EXCEPTION(runtime_error("Error must have a type.")); - if ( - !error.isMember("message") || - !error["message"].isString() - ) - BOOST_THROW_EXCEPTION(runtime_error("Error must have a message.")); - if (!error.isMember("sourceLocation")) - continue; - Json::Value const& location = error["sourceLocation"]; - if ( - !location.isMember("start") || - !location["start"].isInt() || - !location.isMember("end") || - !location["end"].isInt() - ) - BOOST_THROW_EXCEPTION(runtime_error("Error must have a SourceLocation with start and end.")); - size_t start = location["start"].asUInt(); - size_t end = location["end"].asUInt(); - std::string sourceName; - if (location.isMember("source") && location["source"].isString()) - sourceName = location["source"].asString(); - if (start >= preamble.size()) - start -= preamble.size(); - if (end >= preamble.size()) - end -= preamble.size(); - m_errorList.emplace_back(SyntaxTestError{ - error["type"].asString(), - error["errorId"].isNull() ? nullopt : optional(langutil::ErrorId{error["errorId"].asUInt()}), - error["message"].asString(), - sourceName, - static_cast(start), - static_cast(end) - }); - } - } - - return conclude(_stream, _linePrefix, _formatted); -} - -vector SMTCheckerJSONTest::hashesFromJson(Json::Value const& _jsonObj, string const& _auxInput, string const& _smtlib) -{ - vector hashes; - Json::Value const& auxInputs = _jsonObj[_auxInput]; - if (!!auxInputs) - { - Json::Value const& smtlib = auxInputs[_smtlib]; - if (!!smtlib) - for (auto const& hashString: smtlib.getMemberNames()) - hashes.push_back(hashString); - } - return hashes; -} - -Json::Value SMTCheckerJSONTest::buildJson(string const& _extra) -{ - string language = "\"language\": \"Solidity\""; - string sources = " \"sources\": { "; - bool first = true; - for (auto [sourceName, sourceContent]: m_sources) - { - string sourceObj = "{ \"content\": \"" + _extra + sourceContent + "\"}"; - if (!first) - sources += ", "; - sources += "\"" + sourceName + "\": " + sourceObj; - first = false; - } - sources += "}"; - string input = "{" + language + ", " + sources + "}"; - Json::Value source; - if (!jsonParseStrict(input, source)) - BOOST_THROW_EXCEPTION(runtime_error("Could not build JSON from string: " + input)); - return source; -} diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index 51d1e7b364a4..bdc408b9f60e 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -19,8 +19,6 @@ #include #include -#include - using namespace std; using namespace solidity; using namespace solidity::langutil; @@ -47,16 +45,48 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E if (!available.cvc4) m_enabledSolvers.cvc4 = false; - if (m_enabledSolvers.none()) + auto engine = ModelCheckerEngine::fromString(m_reader.stringSetting("SMTEngine", "all")); + if (engine) + m_modelCheckerSettings.engine = *engine; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT engine choice.")); + + if (m_enabledSolvers.none() || m_modelCheckerSettings.engine.none()) m_shouldRun = false; + + auto const& ignoreCex = m_reader.stringSetting("SMTIgnoreCex", "no"); + if (ignoreCex == "no") + m_ignoreCex = false; + else if (ignoreCex == "yes") + m_ignoreCex = true; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT counterexample choice.")); } TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { setupCompiler(); compiler().setSMTSolverChoice(m_enabledSolvers); + compiler().setModelCheckerSettings(m_modelCheckerSettings); parseAndAnalyze(); filterObtainedErrors(); return conclude(_stream, _linePrefix, _formatted); } + +void SMTCheckerTest::filterObtainedErrors() +{ + SyntaxTest::filterObtainedErrors(); + + static auto removeCex = [](vector& errors) { + for (auto& e: errors) + if ( + auto cexPos = e.message.find("\\nCounterexample"); + cexPos != string::npos + ) + e.message = e.message.substr(0, cexPos); + }; + + if (m_ignoreCex) + removeCex(m_errorList); +} diff --git a/test/libsolidity/SMTCheckerTest.h b/test/libsolidity/SMTCheckerTest.h index 522abb677106..b65005e986eb 100644 --- a/test/libsolidity/SMTCheckerTest.h +++ b/test/libsolidity/SMTCheckerTest.h @@ -22,6 +22,8 @@ #include +#include + #include namespace solidity::frontend::test @@ -38,11 +40,21 @@ class SMTCheckerTest: public SyntaxTest TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; + void filterObtainedErrors() override; + protected: + /// This contains engine and timeout. + /// The engine can be set via option SMTEngine in the test. + /// The possible options are `all`, `chc`, `bmc`, `none`, + /// where the default is `all`. + ModelCheckerSettings m_modelCheckerSettings; + /// This is set via option SMTSolvers in the test. /// The possible options are `all`, `z3`, `cvc4`, `none`, /// where if none is given the default used option is `all`. smtutil::SMTSolverChoice m_enabledSolvers; + + bool m_ignoreCex = false; }; } diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index a3e1661ef8df..5337c844a05e 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -38,6 +38,9 @@ namespace solidity::frontend::test BOOST_AUTO_TEST_SUITE(SemVerMatcher) +namespace +{ + SemVerMatchExpression parseExpression(string const& _input) { Scanner scanner{CharStream(_input, "")}; @@ -62,6 +65,8 @@ SemVerMatchExpression parseExpression(string const& _input) return expression; } +} + BOOST_AUTO_TEST_CASE(positive_range) { // Positive range tests diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 180e49bd7ceb..f532a73345b9 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -39,8 +39,8 @@ using namespace boost::unit_test; namespace fs = boost::filesystem; -SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, bool enforceViaYul): - SolidityExecutionFramework(_evmVersion), +SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, vector const& _vmPaths, bool enforceViaYul): + SolidityExecutionFramework(_evmVersion, _vmPaths), EVMVersionRestrictedTestCase(_filename), m_sources(m_reader.sources()), m_lineOffset(m_reader.lineNumber()), @@ -72,8 +72,23 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer else BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + ".")); + string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false"); + if (compileToEwasm == "also") + m_runWithEwasm = true; + else if (compileToEwasm == "false") + m_runWithEwasm = false; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ".")); + + if (m_runWithEwasm && !m_runWithYul) + BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ", compileViaYul need to be enabled.")); + + // run ewasm tests only, if an ewasm evmc vm was defined + if (m_runWithEwasm && !m_supportsEwasm) + m_runWithEwasm = false; + m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false); - if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) + if (m_runWithABIEncoderV1Only && !solidity::test::CommonOptions::get().useABIEncoderV1) m_shouldRun = false; auto revertStrings = revertStringsFromString(m_reader.stringSetting("revertStrings", "default")); @@ -88,156 +103,196 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { + TestResult result = TestResult::Success; + bool compileViaYul = m_runWithYul || m_enforceViaYul; + + if (m_runWithoutYul) + result = runTest(_stream, _linePrefix, _formatted, false, false); - for (bool compileViaYul: set{!m_runWithoutYul, m_runWithYul || m_enforceViaYul}) + if (compileViaYul && result == TestResult::Success) + result = runTest(_stream, _linePrefix, _formatted, true, false); + + if (m_runWithEwasm && result == TestResult::Success) + result = runTest(_stream, _linePrefix, _formatted, true, true); + + return result; +} + +TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm) +{ + try { - try - { - reset(); - bool success = true; + bool success = true; - m_compileViaYul = compileViaYul; - m_compileViaYulCanBeSet = false; + if (_compileViaYul && _compileToEwasm) + selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM); + else + selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1); - if (compileViaYul) - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; + reset(); - for (auto& test: m_tests) - test.reset(); + m_compileViaYul = _compileViaYul; + if (_compileToEwasm) + { + soltestAssert(m_compileViaYul, ""); + m_compileToEwasm = _compileToEwasm; + } - map libraries; + m_compileViaYulCanBeSet = false; - bool constructed = false; + if (_compileViaYul) + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; - for (auto& test: m_tests) - { - if (constructed) - { - soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); - soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); - } - else if (test.call().isLibrary) - { - soltestAssert( - deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, - "Failed to deploy library " + test.call().signature - ); - libraries[test.call().signature] = m_contractAddress; - continue; - } - else - { - if (test.call().isConstructor) - deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries); - else - soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); - constructed = true; - } + for (auto& test: m_tests) + test.reset(); - if (test.call().isConstructor) - { - if (m_transactionSuccessful == test.call().expectations.failure) - success = false; + map libraries; - test.setFailure(!m_transactionSuccessful); - test.setRawBytes(bytes()); - } + bool constructed = false; + + for (auto& test: m_tests) + { + if (constructed) + { + soltestAssert(test.call().kind != FunctionCall::Kind::Library, "Libraries have to be deployed before any other call."); + soltestAssert( + test.call().kind != FunctionCall::Kind::Constructor, + "Constructor has to be the first function call expect for library deployments."); + } + else if (test.call().kind == FunctionCall::Kind::Library) + { + soltestAssert( + deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, + "Failed to deploy library " + test.call().signature); + libraries[test.call().signature] = m_contractAddress; + continue; + } + else + { + if (test.call().kind == FunctionCall::Kind::Constructor) + deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries); else - { - bytes output; - if (test.call().useCallWithoutSignature) - output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); - else - { - soltestAssert( - m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), - "The function " + test.call().signature + " is not known to the compiler" - ); - - output = callContractFunctionWithValueNoEncoding( - test.call().signature, - test.call().value.value, - test.call().arguments.rawBytes() - ); - } - - if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) - success = false; - - test.setFailure(!m_transactionSuccessful); - test.setRawBytes(std::move(output)); - test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName())); - } + soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract."); + constructed = true; } - if (success && !m_runWithYul && compileViaYul) + if (test.call().kind == FunctionCall::Kind::Storage) { - m_compileViaYulCanBeSet = true; - AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix - << "Test can pass via Yul and marked with compileViaYul: false." << endl; - return TestResult::Failure; + test.setFailure(false); + bytes result(1, !storageEmpty(m_contractAddress)); + test.setRawBytes(result); + soltestAssert(test.call().expectations.rawBytes().size() == 1, ""); + if (test.call().expectations.rawBytes() != result) + success = false; } + else if (test.call().kind == FunctionCall::Kind::Constructor) + { + if (m_transactionSuccessful == test.call().expectations.failure) + success = false; - if (!success && (m_runWithYul || !compileViaYul)) + test.setFailure(!m_transactionSuccessful); + test.setRawBytes(bytes()); + } + else { - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - for (auto const& test: m_tests) - { - ErrorReporter errorReporter; - _stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl; - _stream << errorReporter.format(_linePrefix, _formatted); - } - _stream << endl; - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; - for (auto const& test: m_tests) - { - ErrorReporter errorReporter; - _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; - _stream << errorReporter.format(_linePrefix, _formatted); - } - AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix - << "Attention: Updates on the test will apply the detected format displayed." << endl; - if (compileViaYul && m_runWithoutYul) + bytes output; + if (test.call().kind == FunctionCall::Kind::LowLevel) + output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); + else { - _stream << _linePrefix << endl << _linePrefix; - AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) - << "Note that the test passed without Yul."; - _stream << endl; + soltestAssert( + m_allowNonExistingFunctions || + m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), + "The function " + test.call().signature + " is not known to the compiler" + ); + + output = callContractFunctionWithValueNoEncoding( + test.call().signature, + test.call().value.value, + test.call().arguments.rawBytes() + ); } - else if (!compileViaYul && m_runWithYul) - AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix - << "Note that the test also has to pass via Yul." << endl; - return TestResult::Failure; + + bool outputMismatch = (output != test.call().expectations.rawBytes()); + // Pre byzantium, it was not possible to return failure data, so we disregard + // output mismatch for those EVM versions. + if (test.call().expectations.failure && !m_transactionSuccessful && !m_evmVersion.supportsReturndata()) + outputMismatch = false; + if (m_transactionSuccessful != !test.call().expectations.failure || outputMismatch) + success = false; + + test.setFailure(!m_transactionSuccessful); + test.setRawBytes(std::move(output)); + test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName())); } } - catch (WhiskersError const&) + + if (success && !m_runWithYul && _compileViaYul) { - // this is an error in Whiskers template, so should be thrown anyway - throw; + m_compileViaYulCanBeSet = true; + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << + _linePrefix << endl << + _linePrefix << "Test can pass via Yul and marked with compileViaYul: false." << endl; + return TestResult::Failure; } - catch (YulException const&) + + if (!success && (m_runWithYul || !_compileViaYul)) { - // this should be an error in yul compilation or translation - throw; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + for (auto const& test: m_tests) + { + ErrorReporter errorReporter; + _stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl; + _stream << errorReporter.format(_linePrefix, _formatted); + } + _stream << endl; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + for (auto const& test: m_tests) + { + ErrorReporter errorReporter; + _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; + _stream << errorReporter.format(_linePrefix, _formatted); + } + AnsiColorized(_stream, _formatted, {BOLD, RED}) + << _linePrefix << endl + << _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl; + if (_compileViaYul && m_runWithoutYul) + { + _stream << _linePrefix << endl << _linePrefix; + AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul."; + _stream << endl; + } + else if (!_compileViaYul && m_runWithYul) + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) + << _linePrefix << endl + << _linePrefix << "Note that the test also has to pass via Yul." << endl; + return TestResult::Failure; } - catch (boost::exception const&) - { - if (compileViaYul && !m_runWithYul) - continue; + } + catch (WhiskersError const&) + { + // this is an error in Whiskers template, so should be thrown anyway + throw; + } + catch (YulException const&) + { + // this should be an error in yul compilation or translation + throw; + } + catch (boost::exception const&) + { + if (!_compileViaYul || m_runWithYul) throw; - } - catch (std::exception const&) - { - if (compileViaYul && !m_runWithYul) - continue; + } + catch (std::exception const&) + { + if (!_compileViaYul || m_runWithYul) throw; - } - catch (...) - { - if (compileViaYul && !m_runWithYul) - continue; + } + catch (...) + { + if (!_compileViaYul || m_runWithYul) throw; - } } return TestResult::Success; diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 646e7e00ba32..acb656763aea 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -40,9 +40,9 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict { public: static std::unique_ptr create(Config const& _options) - { return std::make_unique(_options.filename, _options.evmVersion, _options.enforceCompileViaYul); } + { return std::make_unique(_options.filename, _options.evmVersion, _options.vmPaths, _options.enforceCompileViaYul); } - explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, bool _enforceViaYul = false); + explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, std::vector const& _vmPaths, bool _enforceViaYul = false); TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override; @@ -59,10 +59,12 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict /// Returns true if deployment was successful, false otherwise. bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map const& _libraries = {}); private: + TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm); SourceMap m_sources; std::size_t m_lineOffset; std::vector m_tests; bool m_runWithYul = false; + bool m_runWithEwasm = false; bool m_runWithoutYul = true; bool m_enforceViaYul = false; bool m_runWithABIEncoderV1Only = false; diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index 13f714e5e90a..045ac167a905 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions) contract C { uint x; constructor() { f(); } - function f() internal { for (uint i = 0; i < 10; ++i) x += 3 + i; } + function f() internal { unchecked { for (uint i = 0; i < 10; ++i) x += 3 + i; } } } )"; compiler().setOptimiserSettings(solidity::test::CommonOptions::get().optimize); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d3398f510a56..b5001a26aaac 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -33,11 +33,13 @@ #include #include +#include #include #include #include +#include #include #include @@ -48,20 +50,40 @@ using namespace solidity::util; using namespace solidity::test; using namespace solidity::langutil; -#define ALSO_VIA_YUL(CODE) \ -{ \ - { CODE } \ - reset(); \ - m_compileViaYul = true; \ - { CODE } \ -} +#define ALSO_VIA_YUL(CODE) \ +{ \ + m_doEwasmTestrun = true; \ + \ + m_compileViaYul = false; \ + m_compileToEwasm = false; \ + { CODE } \ + \ + m_compileViaYul = true; \ + reset(); \ + { CODE } \ + \ + if (m_doEwasmTestrun) \ + { \ + m_compileToEwasm = true; \ + reset(); \ + { CODE } \ + } \ +} + +#define DISABLE_EWASM_TESTRUN() \ + { m_doEwasmTestrun = false; } namespace solidity::frontend::test { -BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework) +struct SolidityEndToEndTestExecutionFramework: public SolidityExecutionFramework +{ + bool m_doEwasmTestrun = false; +}; + +BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityEndToEndTestExecutionFramework) -int constexpr roundTo32(int _num) +unsigned constexpr roundTo32(unsigned _num) { return (_num + 31) / 32 * 32; } @@ -115,6 +137,8 @@ BOOST_AUTO_TEST_CASE(recursive_calls) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); function recursive_calls_cpp = [&recursive_calls_cpp](u256 const& n) -> u256 { @@ -140,6 +164,8 @@ BOOST_AUTO_TEST_CASE(while_loop) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto while_loop_cpp = [](u256 const& n) -> u256 @@ -168,6 +194,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto do_while_loop_cpp = [](u256 const& n) -> u256 @@ -213,6 +241,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto do_while = [](u256 n) -> u256 @@ -263,6 +293,8 @@ BOOST_AUTO_TEST_CASE(nested_loops) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto nested_loops_cpp = [](u256 n) -> u256 @@ -329,6 +361,8 @@ BOOST_AUTO_TEST_CASE(nested_loops_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto nested_loops_cpp = [](u256 n) -> u256 @@ -383,6 +417,8 @@ BOOST_AUTO_TEST_CASE(for_loop_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop = [](u256 n) -> u256 @@ -444,6 +480,8 @@ BOOST_AUTO_TEST_CASE(nested_for_loop_multiple_local_vars) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop = [](u256 n) -> u256 @@ -484,6 +522,8 @@ BOOST_AUTO_TEST_CASE(for_loop) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop_cpp = [](u256 const& n) -> u256 @@ -498,37 +538,6 @@ BOOST_AUTO_TEST_CASE(for_loop) ) } -BOOST_AUTO_TEST_CASE(for_loop_empty) -{ - char const* sourceCode = R"( - contract test { - function f() public returns(uint ret) { - ret = 1; - for (;;) { - ret += 1; - if (ret >= 10) break; - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - - auto for_loop_empty_cpp = []() -> u256 - { - u256 ret = 1; - for (;;) - { - ret += 1; - if (ret >= 10) break; - } - return ret; - }; - - testContractAgainstCpp("f()", for_loop_empty_cpp); - ) -} - BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) { char const* sourceCode = R"( @@ -542,6 +551,8 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256 @@ -580,102 +591,30 @@ BOOST_AUTO_TEST_CASE(for_loop_break_continue) } } )"; - compileAndRun(sourceCode); - - auto breakContinue = [](u256 const& n) -> u256 - { - u256 i = 1; - u256 k = 0; - for (i *= 5; k < n; i *= 7) - { - k++; - i += 4; - if (n % 3 == 0) - break; - i += 9; - if (n % 2 == 0) - continue; - i += 19; - } - return i; - }; - - testContractAgainstCppOnRange("f(uint256)", breakContinue, 0, 10); -} - -BOOST_AUTO_TEST_CASE(calling_other_functions) -{ - char const* sourceCode = R"( - contract collatz { - function run(uint x) public returns(uint y) { - while ((y = x) > 1) { - if (x % 2 == 0) x = evenStep(x); - else x = oddStep(x); - } - } - function evenStep(uint x) public returns(uint y) { - return x / 2; - } - function oddStep(uint x) public returns(uint y) { - return 3 * x + 1; - } - } - )"; - compileAndRun(sourceCode); - - auto evenStep_cpp = [](u256 const& n) -> u256 - { - return n / 2; - }; - - auto oddStep_cpp = [](u256 const& n) -> u256 - { - return 3 * n + 1; - }; - - auto collatz_cpp = [&evenStep_cpp, &oddStep_cpp](u256 n) -> u256 - { - u256 y; - while ((y = n) > 1) - { - if (n % 2 == 0) - n = evenStep_cpp(n); - else - n = oddStep_cpp(n); - } - return y; - }; - - testContractAgainstCpp("run(uint256)", collatz_cpp, u256(0)); - testContractAgainstCpp("run(uint256)", collatz_cpp, u256(1)); - testContractAgainstCpp("run(uint256)", collatz_cpp, u256(2)); - testContractAgainstCpp("run(uint256)", collatz_cpp, u256(8)); - testContractAgainstCpp("run(uint256)", collatz_cpp, u256(127)); -} - -BOOST_AUTO_TEST_CASE(many_local_variables) -{ - char const* sourceCode = R"( - contract test { - function run(uint x1, uint x2, uint x3) public returns(uint y) { - uint8 a = 0x1; uint8 b = 0x10; uint16 c = 0x100; - y = a + b + c + x1 + x2 + x3; - y += b + x2; - } - } - )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() compileAndRun(sourceCode); - auto f = [](u256 const& x1, u256 const& x2, u256 const& x3) -> u256 + + auto breakContinue = [](u256 const& n) -> u256 { - u256 a = 0x1; - u256 b = 0x10; - u256 c = 0x100; - u256 y = a + b + c + x1 + x2 + x3; - return y + b + x2; + u256 i = 1; + u256 k = 0; + for (i *= 5; k < n; i *= 7) + { + k++; + i += 4; + if (n % 3 == 0) + break; + i += 9; + if (n % 2 == 0) + continue; + i += 19; + } + return i; }; - testContractAgainstCpp("run(uint256,uint256,uint256)", f, u256(0x1000), u256(0x10000), u256(0x100000)); - ) + + testContractAgainstCppOnRange("f(uint256)", breakContinue, 0, 10); + ); } BOOST_AUTO_TEST_CASE(short_circuiting) @@ -689,6 +628,8 @@ BOOST_AUTO_TEST_CASE(short_circuiting) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); auto short_circuiting_cpp = [](u256 n) -> u256 @@ -706,10 +647,12 @@ BOOST_AUTO_TEST_CASE(high_bits_cleaning) char const* sourceCode = R"( contract test { function run() public returns(uint256 y) { - uint32 t = uint32(0xffffffff); - uint32 x = t + 10; - if (x >= 0xffffffff) return 0; - return x; + unchecked { + uint32 t = uint32(0xffffffff); + uint32 x = t + 10; + if (x >= 0xffffffff) return 0; + return x; + } } } )"; @@ -730,9 +673,11 @@ BOOST_AUTO_TEST_CASE(sign_extension) char const* sourceCode = R"( contract test { function run() public returns(uint256 y) { - int64 x = -int32(0xff); - if (x >= 0xff) return 0; - return -uint256(x); + unchecked { + int64 x = -int32(int64(0xff)); + if (x >= 0xff) return 0; + return 0 - uint256(int256(x)); + } } } )"; @@ -752,9 +697,11 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types) char const* sourceCode = R"( contract test { function run() public returns(uint256 y) { - uint32 t = uint32(0xffffff); - uint32 x = t * 0xffffff; - return x / 0x100; + unchecked { + uint32 t = uint32(0xffffff); + uint32 x = t * 0xffffff; + return x / 0x100; + } } } )"; @@ -768,142 +715,6 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types) testContractAgainstCpp("run()", small_unsigned_types_cpp); } -BOOST_AUTO_TEST_CASE(small_signed_types) -{ - char const* sourceCode = R"( - contract test { - function run() public returns(int256 y) { - return -int32(10) * -int64(20); - } - } - )"; - compileAndRun(sourceCode); - auto small_signed_types_cpp = []() -> u256 - { - return -int32_t(10) * -int64_t(20); - }; - testContractAgainstCpp("run()", small_signed_types_cpp); -} - -BOOST_AUTO_TEST_CASE(compound_assign) -{ - char const* sourceCode = R"( - contract test { - uint value1; - uint value2; - function f(uint x, uint y) public returns (uint w) { - uint value3 = y; - value1 += x; - value3 *= x; - value2 *= value3 + value1; - return value2 += 7; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - - u256 value1; - u256 value2; - auto f = [&](u256 const& _x, u256 const& _y) -> u256 - { - u256 value3 = _y; - value1 += _x; - value3 *= _x; - value2 *= value3 + value1; - return value2 += 7; - }; - testContractAgainstCpp("f(uint256,uint256)", f, u256(0), u256(6)); - testContractAgainstCpp("f(uint256,uint256)", f, u256(1), u256(3)); - testContractAgainstCpp("f(uint256,uint256)", f, u256(2), u256(25)); - testContractAgainstCpp("f(uint256,uint256)", f, u256(3), u256(69)); - testContractAgainstCpp("f(uint256,uint256)", f, u256(4), u256(84)); - testContractAgainstCpp("f(uint256,uint256)", f, u256(5), u256(2)); - testContractAgainstCpp("f(uint256,uint256)", f, u256(6), u256(51)); - testContractAgainstCpp("f(uint256,uint256)", f, u256(7), u256(48)); - ) -} - -BOOST_AUTO_TEST_CASE(mapping_state) -{ - char const* sourceCode = R"( - contract Ballot { - mapping(address => bool) canVote; - mapping(address => uint) voteCount; - mapping(address => bool) voted; - function getVoteCount(address addr) public returns (uint retVoteCount) { - return voteCount[addr]; - } - function grantVoteRight(address addr) public { - canVote[addr] = true; - } - function vote(address voter, address vote) public returns (bool success) { - if (!canVote[voter] || voted[voter]) return false; - voted[voter] = true; - voteCount[vote] = voteCount[vote] + 1; - return true; - } - } - )"; - class Ballot - { - public: - u256 getVoteCount(u160 _address) { return m_voteCount[_address]; } - void grantVoteRight(u160 _address) { m_canVote[_address] = true; } - bool vote(u160 _voter, u160 _vote) - { - if (!m_canVote[_voter] || m_voted[_voter]) return false; - m_voted[_voter] = true; - m_voteCount[_vote]++; - return true; - } - private: - map m_canVote; - map m_voteCount; - map m_voted; - }; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - Ballot ballot; - - auto getVoteCount = bind(&Ballot::getVoteCount, &ballot, _1); - auto grantVoteRight = bind(&Ballot::grantVoteRight, &ballot, _1); - auto vote = bind(&Ballot::vote, &ballot, _1, _2); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); - // voting without vote right should be rejected - testContractAgainstCpp("vote(address,address)", vote, u160(0), u160(2)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); - // grant vote rights - testContractAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(0)); - testContractAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(1)); - // vote, should increase 2's vote count - testContractAgainstCpp("vote(address,address)", vote, u160(0), u160(2)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); - // vote again, should be rejected - testContractAgainstCpp("vote(address,address)", vote, u160(0), u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); - // vote without right to vote - testContractAgainstCpp("vote(address,address)", vote, u160(2), u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); - // grant vote right and now vote again - testContractAgainstCpp("grantVoteRight(address)", grantVoteRight, u160(2)); - testContractAgainstCpp("vote(address,address)", vote, u160(2), u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(0)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(1)); - testContractAgainstCpp("getVoteCount(address)", getVoteCount, u160(2)); - ) -} - BOOST_AUTO_TEST_CASE(mapping_state_inc_dec) { char const* sourceCode = R"( @@ -936,6 +747,8 @@ BOOST_AUTO_TEST_CASE(mapping_state_inc_dec) return --table[value++]; }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); value = 0; table.clear(); @@ -962,6 +775,8 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping) else return table[_x][_y] = _z; }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); table.clear(); @@ -998,34 +813,14 @@ BOOST_AUTO_TEST_CASE(constructor) }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); testContractAgainstCpp("get(uint256)", get, u256(6)); testContractAgainstCpp("get(uint256)", get, u256(7)); ) } -BOOST_AUTO_TEST_CASE(blockchain) -{ - char const* sourceCode = R"( - contract test { - constructor() payable {} - function someInfo() public payable returns (uint256 value, address coinbase, uint256 blockNumber) { - value = msg.value; - coinbase = block.coinbase; - blockNumber = block.number; - } - } - )"; - m_evmHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212")); - m_evmHost->newBlock(); - m_evmHost->newBlock(); - m_evmHost->newBlock(); - m_evmHost->newBlock(); - m_evmHost->newBlock(); - compileAndRun(sourceCode, 27); - ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7)); -} - BOOST_AUTO_TEST_CASE(send_ether) { char const* sourceCode = R"( @@ -1038,9 +833,11 @@ BOOST_AUTO_TEST_CASE(send_ether) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + u256 amount(250); compileAndRun(sourceCode, amount + 1); - u160 address(23); + h160 address(23); ABI_CHECK(callContractFunction("a(address,uint256)", address, amount), encodeArgs(1)); BOOST_CHECK_EQUAL(balanceAt(address), amount); ) @@ -1070,12 +867,14 @@ BOOST_AUTO_TEST_CASE(transfer_ether) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, 0, "B"); - u160 const nonPayableRecipient = m_contractAddress; + h160 const nonPayableRecipient = m_contractAddress; compileAndRun(sourceCode, 0, "C"); - u160 const oogRecipient = m_contractAddress; + h160 const oogRecipient = m_contractAddress; compileAndRun(sourceCode, 20, "A"); - u160 payableRecipient(23); + h160 payableRecipient(23); ABI_CHECK(callContractFunction("a(address,uint256)", payableRecipient, 10), encodeArgs(10)); BOOST_CHECK_EQUAL(balanceAt(payableRecipient), 10); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); @@ -1084,143 +883,6 @@ BOOST_AUTO_TEST_CASE(transfer_ether) ) } -BOOST_AUTO_TEST_CASE(uncalled_blockhash) -{ - char const* code = R"( - contract C { - function f() public view returns (bytes32) - { - return (blockhash)(block.number - 1); - } - } - )"; - compileAndRun(code, 0, "C"); - bytes result = callContractFunction("f()"); - BOOST_REQUIRE_EQUAL(result.size(), 32); - BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); -} - -BOOST_AUTO_TEST_CASE(log0) -{ - char const* sourceCode = R"( - contract test { - function a() public { - log0(bytes32(uint256(1))); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - callContractFunction("a()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1))); - BOOST_CHECK_EQUAL(numLogTopics(0), 0); - ) -} - -BOOST_AUTO_TEST_CASE(log1) -{ - char const* sourceCode = R"( - contract test { - function a() public { - log1(bytes32(uint256(1)), bytes32(uint256(2))); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - callContractFunction("a()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), h256(u256(2))); - ) -} - -BOOST_AUTO_TEST_CASE(log2) -{ - char const* sourceCode = R"( - contract test { - function a() public { - log2(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3))); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - callContractFunction("a()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); - for (unsigned i = 0; i < 2; ++i) - BOOST_CHECK_EQUAL(logTopic(0, i), h256(u256(i + 2))); - ) -} - -BOOST_AUTO_TEST_CASE(log3) -{ - char const* sourceCode = R"( - contract test { - function a() public { - log3(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)), bytes32(uint256(4))); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - callContractFunction("a()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); - for (unsigned i = 0; i < 3; ++i) - BOOST_CHECK_EQUAL(logTopic(0, i), h256(u256(i + 2))); - ) -} - -BOOST_AUTO_TEST_CASE(log4) -{ - char const* sourceCode = R"( - contract test { - function a() public { - log4(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)), bytes32(uint256(4)), bytes32(uint256(5))); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - callContractFunction("a()"); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 4); - for (unsigned i = 0; i < 4; ++i) - BOOST_CHECK_EQUAL(logTopic(0, i), h256(u256(i + 2))); - ) -} - -BOOST_AUTO_TEST_CASE(log_in_constructor) -{ - char const* sourceCode = R"( - contract test { - constructor() { - log1(bytes32(uint256(1)), bytes32(uint256(2))); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_REQUIRE_EQUAL(numLogs(), 1); - BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(1))); - BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); - BOOST_CHECK_EQUAL(logTopic(0, 0), h256(u256(2))); - ) -} - BOOST_AUTO_TEST_CASE(selfdestruct) { char const* sourceCode = R"( @@ -1233,8 +895,10 @@ BOOST_AUTO_TEST_CASE(selfdestruct) } )"; u256 amount(130); - u160 address(23); + h160 address(23); ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, amount); ABI_CHECK(callContractFunction("a(address)", address), bytes()); BOOST_CHECK(!addressHasCode(m_contractAddress)); @@ -1251,14 +915,17 @@ BOOST_AUTO_TEST_CASE(keccak256) } } )"; - compileAndRun(sourceCode); - auto f = [&](u256 const& _x) -> u256 - { - return util::keccak256(toBigEndian(_x)); - }; - testContractAgainstCpp("a(bytes32)", f, u256(4)); - testContractAgainstCpp("a(bytes32)", f, u256(5)); - testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + auto f = [&](u256 const& _x) -> u256 + { + return util::keccak256(toBigEndian(_x)); + }; + testContractAgainstCpp("a(bytes32)", f, u256(4)); + testContractAgainstCpp("a(bytes32)", f, u256(5)); + testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ); } BOOST_AUTO_TEST_CASE(sha256) @@ -1270,7 +937,6 @@ BOOST_AUTO_TEST_CASE(sha256) } } )"; - compileAndRun(sourceCode); auto f = [&](u256 const& _x) -> bytes { if (_x == u256(4)) @@ -1281,9 +947,13 @@ BOOST_AUTO_TEST_CASE(sha256) return fromHex("af9613760f72635fbdb44a5a0a63c39f12af30f950a6ee5c971be188e89c4051"); return fromHex(""); }; - testContractAgainstCpp("a(bytes32)", f, u256(4)); - testContractAgainstCpp("a(bytes32)", f, u256(5)); - testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + testContractAgainstCpp("a(bytes32)", f, u256(4)); + testContractAgainstCpp("a(bytes32)", f, u256(5)); + testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ) } BOOST_AUTO_TEST_CASE(ripemd) @@ -1295,7 +965,6 @@ BOOST_AUTO_TEST_CASE(ripemd) } } )"; - compileAndRun(sourceCode); auto f = [&](u256 const& _x) -> bytes { if (_x == u256(4)) @@ -1306,9 +975,13 @@ BOOST_AUTO_TEST_CASE(ripemd) return fromHex("1cf4e77f5966e13e109703cd8a0df7ceda7f3dc3000000000000000000000000"); return fromHex(""); }; - testContractAgainstCpp("a(bytes32)", f, u256(4)); - testContractAgainstCpp("a(bytes32)", f, u256(5)); - testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + testContractAgainstCpp("a(bytes32)", f, u256(4)); + testContractAgainstCpp("a(bytes32)", f, u256(5)); + testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ) } BOOST_AUTO_TEST_CASE(packed_keccak256) @@ -1322,7 +995,6 @@ BOOST_AUTO_TEST_CASE(packed_keccak256) } } )"; - compileAndRun(sourceCode); auto f = [&](u256 const& _x) -> u256 { return util::keccak256( @@ -1333,9 +1005,13 @@ BOOST_AUTO_TEST_CASE(packed_keccak256) toBigEndian(u256(256)) ); }; - testContractAgainstCpp("a(bytes32)", f, u256(4)); - testContractAgainstCpp("a(bytes32)", f, u256(5)); - testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + testContractAgainstCpp("a(bytes32)", f, u256(4)); + testContractAgainstCpp("a(bytes32)", f, u256(5)); + testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ) } BOOST_AUTO_TEST_CASE(packed_keccak256_complex_types) @@ -1345,22 +1021,25 @@ BOOST_AUTO_TEST_CASE(packed_keccak256_complex_types) uint120[3] x; function f() public returns (bytes32 hash1, bytes32 hash2, bytes32 hash3) { uint120[] memory y = new uint120[](3); - x[0] = y[0] = uint120(-2); - x[1] = y[1] = uint120(-3); - x[2] = y[2] = uint120(-4); + x[0] = y[0] = uint120(type(uint).max - 1); + x[1] = y[1] = uint120(type(uint).max - 2); + x[2] = y[2] = uint120(type(uint).max - 3); hash1 = keccak256(abi.encodePacked(x)); hash2 = keccak256(abi.encodePacked(y)); hash3 = keccak256(abi.encodePacked(this.f)); } } )"; - compileAndRun(sourceCode); - // Strangely, arrays are encoded with intra-element padding. - ABI_CHECK(callContractFunction("f()"), encodeArgs( - util::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), - util::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), - util::keccak256(fromHex(m_contractAddress.hex() + "26121ff0")) - )); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + // Strangely, arrays are encoded with intra-element padding. + ABI_CHECK(callContractFunction("f()"), encodeArgs( + util::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), + util::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), + util::keccak256(fromHex(m_contractAddress.hex() + "26121ff0")) + )); + ) } BOOST_AUTO_TEST_CASE(packed_sha256) @@ -1374,7 +1053,6 @@ BOOST_AUTO_TEST_CASE(packed_sha256) } } )"; - compileAndRun(sourceCode); auto f = [&](u256 const& _x) -> bytes { if (_x == u256(4)) @@ -1385,9 +1063,13 @@ BOOST_AUTO_TEST_CASE(packed_sha256) return fromHex("f14def4d07cd185ddd8b10a81b2238326196a38867e6e6adbcc956dc913488c7"); return fromHex(""); }; - testContractAgainstCpp("a(bytes32)", f, u256(4)); - testContractAgainstCpp("a(bytes32)", f, u256(5)); - testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + testContractAgainstCpp("a(bytes32)", f, u256(4)); + testContractAgainstCpp("a(bytes32)", f, u256(5)); + testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ) } BOOST_AUTO_TEST_CASE(packed_ripemd160) @@ -1401,7 +1083,6 @@ BOOST_AUTO_TEST_CASE(packed_ripemd160) } } )"; - compileAndRun(sourceCode); auto f = [&](u256 const& _x) -> bytes { if (_x == u256(4)) @@ -1412,9 +1093,13 @@ BOOST_AUTO_TEST_CASE(packed_ripemd160) return fromHex("c0a2e4b1f3ff766a9a0089e7a410391730872495000000000000000000000000"); return fromHex(""); }; - testContractAgainstCpp("a(bytes32)", f, u256(4)); - testContractAgainstCpp("a(bytes32)", f, u256(5)); - testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + testContractAgainstCpp("a(bytes32)", f, u256(4)); + testContractAgainstCpp("a(bytes32)", f, u256(5)); + testContractAgainstCpp("a(bytes32)", f, u256(-1)); + ) } BOOST_AUTO_TEST_CASE(inter_contract_calls) @@ -1439,7 +1124,7 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls) } )"; compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; + h160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); @@ -1470,7 +1155,7 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters) } )"; compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; + h160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); @@ -1502,7 +1187,7 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this) } )"; compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; + h160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); @@ -1534,7 +1219,7 @@ BOOST_AUTO_TEST_CASE(calls_to_this) } )"; compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; + h160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); @@ -1569,7 +1254,7 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars) } )"; compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; + h160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); @@ -1600,7 +1285,7 @@ BOOST_AUTO_TEST_CASE(fixed_bytes_in_calls) } )"; compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; + h160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); BOOST_REQUIRE(callContractFunction("setHelper(address)", c_helperAddress) == bytes()); BOOST_REQUIRE(callContractFunction("getHelper()", c_helperAddress) == encodeArgs(c_helperAddress)); @@ -1643,7 +1328,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) } contract test { helper h; - constructor() payable { h = new helper(); address(h).send(5); } + constructor() payable { h = new helper(); payable(h).send(5); } function getBalance() public returns (uint256 myBalance, uint256 helperBalance) { myBalance = address(this).balance; helperBalance = address(h).balance; @@ -1655,37 +1340,6 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) BOOST_REQUIRE(callContractFunction("getBalance()") == encodeArgs(u256(20 - 5), u256(5))); } -BOOST_AUTO_TEST_CASE(gaslimit) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return block.gaslimit; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - auto result = callContractFunction("f()"); - ABI_CHECK(result, encodeArgs(gasLimit())); - ) -} - -BOOST_AUTO_TEST_CASE(gasprice) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { - return tx.gasprice; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(gasPrice())); - ) -} - BOOST_AUTO_TEST_CASE(blockhash) { char const* sourceCode = R"( @@ -1694,8 +1348,11 @@ BOOST_AUTO_TEST_CASE(blockhash) function g() public returns (bool) { counter++; return true; } function f() public returns (bytes32[] memory r) { r = new bytes32[](259); - for (uint i = 0; i < 259; i++) - r[i] = blockhash(block.number - 257 + i); + for (uint i = 0; i < 259; i++) { + unchecked { + r[i] = blockhash(block.number - 257 + i); + } + } } } )"; @@ -1764,7 +1421,12 @@ BOOST_AUTO_TEST_CASE(event) function deposit(bytes32 _id, bool _manually) public payable { if (_manually) { bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f; - log3(bytes32(msg.value), s, bytes32(uint256(msg.sender)), _id); + uint value = msg.value; + address sender = msg.sender; + assembly { + mstore(0, value) + log3(0, 0x20, s, sender, _id) + } } else { emit Deposit(msg.sender, _id, msg.value); } @@ -1772,6 +1434,8 @@ BOOST_AUTO_TEST_CASE(event) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); @@ -1800,6 +1464,8 @@ BOOST_AUTO_TEST_CASE(event_emit) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); @@ -1814,6 +1480,31 @@ BOOST_AUTO_TEST_CASE(event_emit) ) } +BOOST_AUTO_TEST_CASE(event_constructor) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + constructor() { + emit Deposit(msg.sender, bytes32("abc"), 7); + } + } + )"; + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + + compileAndRun(sourceCode); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(7))); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(logTopic(0, 1), h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(logTopic(0, 2), h256(string{"abc"}, h256::FromBinary, h256::AlignLeft)); + ) +} + + BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( @@ -1826,6 +1517,8 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1850,6 +1543,8 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("f()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1886,9 +1581,11 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) } } )"; - u160 const c_loggedAddress = m_contractAddress; + h160 const c_loggedAddress = m_contractAddress; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1947,9 +1644,11 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) } } )"; - u160 const c_loggedAddress = m_contractAddress; + h160 const c_loggedAddress = m_contractAddress; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -1985,6 +1684,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(numLogTopics(0), 0); @@ -2002,6 +1703,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); @@ -2028,13 +1731,15 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 value(18); u256 id(0x1234); callContractFunctionWithValue("deposit(bytes32)", value, id); BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); - BOOST_CHECK(logData(0) == encodeArgs((u160)m_sender, id, value, true)); + BOOST_CHECK(logData(0) == encodeArgs(m_sender, id, value, true)); BOOST_REQUIRE_EQUAL(numLogTopics(0), 1); BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Deposit(address,bytes32,uint256,bool)"))); ) @@ -2110,7 +1815,7 @@ BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage) BOOST_AUTO_TEST_CASE(event_struct_memory_v2) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { struct S { uint a; } event E(S); @@ -2132,7 +1837,7 @@ BOOST_AUTO_TEST_CASE(event_struct_memory_v2) BOOST_AUTO_TEST_CASE(event_struct_storage_v2) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { struct S { uint a; } event E(S); @@ -2180,7 +1885,7 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_memory) BOOST_AUTO_TEST_CASE(event_dynamic_array_memory_v2) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { event E(uint[]); function createEvent(uint x) public { @@ -2205,7 +1910,7 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_memory_v2) BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_memory_v2) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { event E(uint[][]); function createEvent(uint x) public { @@ -2247,6 +1952,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 x(42); callContractFunction("createEvent(uint256)", x); @@ -2261,7 +1968,7 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage) BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { event E(uint[]); uint[] arr; @@ -2276,6 +1983,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); u256 x(42); callContractFunction("createEvent(uint256)", x); @@ -2290,7 +1999,7 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2) BOOST_AUTO_TEST_CASE(event_dynamic_nested_array_storage_v2) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { event E(uint[][]); uint[][] arr; @@ -2328,7 +2037,7 @@ BOOST_AUTO_TEST_CASE(event_indexed_string) for (uint i = 0; i < 90; i++) bytes(x).push(0); for (uint8 i = 0; i < 90; i++) - bytes(x)[i] = byte(i); + bytes(x)[i] = bytes1(i); y[0] = 4; y[1] = 5; y[2] = 6; @@ -2342,8 +2051,7 @@ BOOST_AUTO_TEST_CASE(event_indexed_string) BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); string dynx(90, 0); - for (size_t i = 0; i < dynx.size(); ++i) - dynx[i] = i; + std::iota(dynx.begin(), dynx.end(), 0); BOOST_CHECK(logData(0) == bytes()); BOOST_REQUIRE_EQUAL(numLogTopics(0), 3); BOOST_CHECK_EQUAL(logTopic(0, 1), util::keccak256(dynx)); @@ -2353,6 +2061,38 @@ BOOST_AUTO_TEST_CASE(event_indexed_string) BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("E(string,uint256[4])"))); } +BOOST_AUTO_TEST_CASE(event_indexed_function) +{ + char const* sourceCode = R"( + contract C { + event Test(function() external indexed); + function f() public { + emit Test(this.f); + } + } + )"; + + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(numLogs(), 1); + BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); + BOOST_CHECK(logData(0) == bytes()); + BOOST_REQUIRE_EQUAL(numLogTopics(0), 2); + + bytes functionHash = util::keccak256("f()").asBytes(); + bytes address = m_contractAddress.asBytes(); + bytes selector = bytes(functionHash.cbegin(), functionHash.cbegin() + 4); + bytes padding = bytes(8, 0); + bytes functionABI = address + selector + padding; + + BOOST_CHECK_EQUAL(logTopic(0, 1).hex(), util::toHex(functionABI)); + BOOST_CHECK_EQUAL(logTopic(0, 0), util::keccak256(string("Test(function)"))); + ) +} + BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) { char const* sourceCode = R"( @@ -2365,6 +2105,8 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) } )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8)); ABI_CHECK(callContractFunction("f(uint256,uint256)", 5, 9), encodeArgs(9, 8)); @@ -2389,7 +2131,7 @@ BOOST_AUTO_TEST_CASE(generic_call) } )**"; compileAndRun(sourceCode, 0, "receiver"); - u160 const c_receiverAddress = m_contractAddress; + h160 const c_receiverAddress = m_contractAddress; compileAndRun(sourceCode, 50, "sender"); BOOST_REQUIRE(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(23)); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 50 - 2); @@ -2421,16 +2163,16 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) for (auto v2: {false, true}) { - string source = (v2 ? "pragma experimental ABIEncoderV2;\n" : "") + string(sourceCode); + string source = "pragma abicoder " + string(v2 ? "v2" : "v1") + ";\n" + string(sourceCode); compileAndRun(source, 0, "Receiver"); - u160 const c_receiverAddress = m_contractAddress; + h160 const c_receiverAddress = m_contractAddress; compileAndRun(source, 50, "Sender"); - u160 const c_senderAddress = m_contractAddress; + h160 const c_senderAddress = m_contractAddress; BOOST_CHECK(m_sender != c_senderAddress); // just for sanity ABI_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress), encodeArgs()); ABI_CHECK(callContractFunction("received()"), encodeArgs(u256(23))); - ABI_CHECK(callContractFunction("sender()"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("sender()"), encodeArgs(m_sender)); ABI_CHECK(callContractFunction("value()"), encodeArgs(u256(11))); m_contractAddress = c_receiverAddress; ABI_CHECK(callContractFunction("received()"), encodeArgs(u256(0))); @@ -2476,13 +2218,13 @@ BOOST_AUTO_TEST_CASE(generic_staticcall) } )**"; compileAndRun(sourceCode, 0, "A"); - u160 const c_addressA = m_contractAddress; + h160 const c_addressA = m_contractAddress; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23)); ABI_CHECK(callContractFunction("g(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23 + 42)); ABI_CHECK(callContractFunction("h(address)", c_addressA), encodeArgs(false, 0x40, 0x00)); ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 42), encodeArgs(true, 0x40, 0x20, 42)); - ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false, 0x40, 0x00)); + ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false, 0x40, 0x24) + panicData(PanicCode::Assert) + bytes(32 - 4, 0)); } } @@ -2498,9 +2240,9 @@ BOOST_AUTO_TEST_CASE(library_call_in_homestead) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("sender()"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("sender()"), encodeArgs(m_sender)); } BOOST_AUTO_TEST_CASE(library_call_protection) @@ -2526,13 +2268,13 @@ BOOST_AUTO_TEST_CASE(library_call_protection) )"; compileAndRun(sourceCode, 0, "Lib"); ABI_CHECK(callContractFunction("np(Lib.S storage)", 0), encodeArgs()); - ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(m_sender)); ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("s()"), encodeArgs(0)); - ABI_CHECK(callContractFunction("np()"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("np()"), encodeArgs(m_sender)); ABI_CHECK(callContractFunction("s()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("v()"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("v()"), encodeArgs(m_sender)); ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); } @@ -2556,7 +2298,7 @@ BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); } @@ -2682,22 +2424,6 @@ BOOST_AUTO_TEST_CASE(copying_bytes_multiassign) ABI_CHECK(callContractFunction("val()"), encodeArgs(0x80)); } -BOOST_AUTO_TEST_CASE(delete_removes_bytes_data) -{ - char const* sourceCode = R"( - contract c { - fallback() external { data = msg.data; } - function del() public returns (bool) { delete data; return true; } - bytes data; - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("---", 7), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("del()", 7), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) { char const* sourceCode = R"( @@ -2707,90 +2433,16 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) bytes data; } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - sendMessage(bytes(), false); - BOOST_CHECK(m_transactionSuccessful); - BOOST_CHECK(m_output.empty()); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(copy_removes_bytes_data) -{ - char const* sourceCode = R"( - contract c { - function set() public returns (bool) { data1 = msg.data; return true; } - function reset() public returns (bool) { data1 = data2; return true; } - bytes data1; - bytes data2; - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("reset()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(bytes_inside_mappings) -{ - char const* sourceCode = R"( - contract c { - function set(uint key) public returns (bool) { data[key] = msg.data; return true; } - function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } - mapping(uint => bytes) data; - } - )"; - compileAndRun(sourceCode); - // store a short byte array at 1 and a longer one at 2 - ABI_CHECK(callContractFunction("set(uint256)", 1, 2), encodeArgs(true)); - ABI_CHECK(callContractFunction("set(uint256)", 2, 2, 3, 4, 5), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - // copy shorter to longer - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 1, 2), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - // copy empty to both - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 1), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("copy(uint256,uint256)", 99, 2), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) -{ - char const* sourceCode = R"( - contract c { - struct Struct { uint a; bytes data; uint b; } - Struct data1; - Struct data2; - function set(uint _a, bytes calldata _data, uint _b) external returns (bool) { - data1.a = _a; - data1.b = _b; - data1.data = _data; - return true; - } - function copy() public returns (bool) { - data1 = data2; - return true; - } - function del() public returns (bool) { - delete data1; - return true; - } - } - )"; - compileAndRun(sourceCode); - string data = "123456789012345678901234567890123"; - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("copy()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("set(uint256,bytes,uint256)", 12, 0x60, 13, u256(data.length()), data), encodeArgs(true)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("del()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); + BOOST_CHECK(!storageEmpty(m_contractAddress)); + sendMessage(bytes(), false); + BOOST_CHECK(m_transactionSuccessful); + BOOST_CHECK(m_output.empty()); + BOOST_CHECK(storageEmpty(m_contractAddress)); + ); } BOOST_AUTO_TEST_CASE(storing_invalid_boolean) @@ -2839,7 +2491,7 @@ BOOST_AUTO_TEST_CASE(storing_invalid_boolean) BOOST_AUTO_TEST_CASE(struct_referencing) { static char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; interface I { struct S { uint a; } } @@ -2888,7 +2540,7 @@ BOOST_AUTO_TEST_CASE(struct_referencing) compileAndRun(sourceCode, 0, "L"); ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 3)); ABI_CHECK(callContractFunction("g()"), encodeArgs(4)); - compileAndRun(sourceCode, 0, "C", bytes(), map{ {"L", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{ {"L", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 5)); @@ -2924,592 +2576,113 @@ BOOST_AUTO_TEST_CASE(enum_referencing) return L.Direction.Right; } function x() public pure returns (L.Direction) { - return L.f(); - } - function y() public pure returns (I.Direction) { - return L.g(); - } - } - )"; - compileAndRun(sourceCode, 0, "L"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); - ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); - ABI_CHECK(callContractFunction("h()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("x()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("y()"), encodeArgs(3)); -} - -BOOST_AUTO_TEST_CASE(bytes_in_arguments) -{ - char const* sourceCode = R"( - contract c { - uint result; - function f(uint a, uint b) public { result += a + b; } - function g(uint a) public { result *= a; } - function test(uint a, bytes calldata data1, bytes calldata data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) { - r_a = a; - address(this).call(data1); - address(this).call(data2); - r = result; - r_b = b; - l = data1.length; - } - } - )"; - compileAndRun(sourceCode); - - string innercalldata1 = asString(FixedHash<4>(util::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); - string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3)); - bytes calldata = encodeArgs( - 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13, - u256(innercalldata1.length()), innercalldata1, - u256(innercalldata2.length()), innercalldata2); - ABI_CHECK( - callContractFunction("test(uint256,bytes,bytes,uint256)", calldata), - encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length())) - ); -} - -BOOST_AUTO_TEST_CASE(fixed_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - uint spacer1; - uint spacer2; - uint[20] data; - function fill() public { - for (uint i = 0; i < data.length; ++i) data[i] = i+1; - } - function clear() public { delete data; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - uint spacer1; - uint spacer2; - uint[3] data; - function fill() public { - for (uint i = 0; i < data.length; ++i) data[i] = i+1; - } - function clear() public { delete data; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(dynamic_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - uint[20] spacer; - uint[] dynamic; - function fill() public { - for (uint i = 0; i < 21; ++i) - dynamic.push(i + 1); - } - function halfClear() public { - while (dynamic.length > 5) - dynamic.pop(); - } - function fullClear() public { delete dynamic; } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("halfClear()"), bytes()); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fullClear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ); -} - -BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup) -{ - char const* sourceCode = R"( - contract c { - struct s { uint[][] d; } - s[] data; - function fill() public returns (uint) { - while (data.length < 3) - data.push(); - while (data[2].d.length < 4) - data[2].d.push(); - while (data[2].d[3].length < 5) - data[2].d[3].push(); - data[2].d[3][4] = 8; - return data[2].d[3][4]; - } - function clear() public { delete data; } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("fill()"), encodeArgs(8)); - BOOST_CHECK(!storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), bytes()); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn) -{ - char const* sourceCode = R"( - contract c { - uint[] data1; - uint[] data2; - function setData1(uint length, uint index, uint value) public { - data1 = new uint[](length); - if (index < length) - data1[index] = value; - } - function copyStorageStorage() public { data2 = data1; } - function getData2(uint index) public returns (uint len, uint val) { - len = data2.length; if (index < len) val = data2[index]; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 10, 5, 4), bytes()); - ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); - ABI_CHECK(callContractFunction("getData2(uint256)", 5), encodeArgs(10, 4)); - ABI_CHECK(callContractFunction("setData1(uint256,uint256,uint256)", 0, 0, 0), bytes()); - ABI_CHECK(callContractFunction("copyStorageStorage()"), bytes()); - ABI_CHECK(callContractFunction("getData2(uint256)", 0), encodeArgs(0, 0)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_copy_target_leftover) -{ - // test that leftover elements in the last slot of target are correctly cleared during assignment - char const* sourceCode = R"( - contract c { - byte[10] data1; - bytes2[32] data2; - function test() public returns (uint check, uint res1, uint res2) { - uint i; - for (i = 0; i < data2.length; ++i) - data2[i] = 0xffff; - check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14])); - for (i = 0; i < data1.length; ++i) - data1[i] = byte(uint8(1 + i)); - data2 = data1; - for (i = 0; i < 16; ++i) - res1 |= uint(uint16(data2[i])) * 0x10000**i; - for (i = 0; i < 16; ++i) - res2 |= uint(uint16(data2[16 + i])) * 0x10000**i; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(u256("0xffffffff"), asString(fromHex("0000000000000000000000000a00090008000700060005000400030002000100")), asString(fromHex("0000000000000000000000000000000000000000000000000000000000000000")))); -} - -BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct) -{ - char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[] data1; - Data[] data2; - function test() public returns (uint x, uint y) { - while (data1.length < 9) - data1.push(); - data1[8].x = 4; - data1[8].y = 5; - data2 = data1; - x = data2[8].x; - y = data2[8].y; - while (data1.length > 0) - data1.pop(); - data2 = data1; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(4, 5)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_copy_storage_abi) -{ - // NOTE: This does not really test copying from storage to ABI directly, - // because it will always copy to memory first. - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract c { - uint8[] x; - uint16[] y; - uint24[] z; - uint24[][] w; - function test1() public returns (uint8[] memory) { - for (uint i = 0; i < 101; ++i) - x.push(uint8(i)); - return x; - } - function test2() public returns (uint16[] memory) { - for (uint i = 0; i < 101; ++i) - y.push(uint16(i)); - return y; - } - function test3() public returns (uint24[] memory) { - for (uint i = 0; i < 101; ++i) - z.push(uint24(i)); - return z; - } - function test4() public returns (uint24[][] memory) { - w = new uint24[][](5); - for (uint i = 0; i < 5; ++i) - for (uint j = 0; j < 101; ++j) - w[i].push(uint24(j)); - return w; - } - } - )"; - compileAndRun(sourceCode); - bytes valueSequence; - for (size_t i = 0; i < 101; ++i) - valueSequence += toBigEndian(u256(i)); - ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence); - ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence); - ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence); - ABI_CHECK(callContractFunction("test4()"), - encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence + - encodeArgs(101) + valueSequence - ); -} - -BOOST_AUTO_TEST_CASE(array_pop_uint16_transition) -{ - char const* sourceCode = R"( - contract c { - uint16[] data; - function test() public returns (uint16 x, uint16 y, uint16 z) { - for (uint i = 1; i <= 48; i++) - data.push(uint16(i)); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - z = data[data.length - 1]; - for (uint m = 1; m <= 18; m++) - data.pop(); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_pop_uint24_transition) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - uint24[] data; - function test() public returns (uint24 x, uint24 y) { - for (uint i = 1; i <= 30; i++) - data.push(uint24(i)); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_pop_array_transition) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; - uint16[][] data; - function test() public returns (uint x, uint y, uint z) { - for (uint i = 1; i <= 48; i++) - data.push(inner); - for (uint j = 1; j <= 10; j++) - data.pop(); - x = data[data.length - 1][0]; - for (uint k = 1; k <= 10; k++) - data.pop(); - y = data[data.length - 1][1]; - for (uint l = 1; l <= 10; l++) - data.pop(); - z = data[data.length - 1][2]; - for (uint m = 1; m <= 18; m++) - data.pop(); - delete inner; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(array_pop_storage_empty) -{ - char const* sourceCode = R"( - contract c { - uint[] data; - function test() public { - data.push(7); - data.pop(); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty) -{ - char const* sourceCode = R"( - contract c { - bytes data; - function test() public { - data.push(0x07); - data.push(0x05); - data.push(0x03); - data.pop(); - data.pop(); - data.pop(); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - uint256 c; - bytes data; - function test() public returns (bool) { - for (uint8 i = 0; i <= 40; i++) - data.push(byte(i+1)); - for (int8 j = 40; j >= 0; j--) { - require(data[uint8(j)] == byte(j+1)); - require(data.length == uint8(j+1)); - data.pop(); - } - return true; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); - BOOST_CHECK(storageEmpty(m_contractAddress)); -} - -BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref) -{ - char const* sourceCode = R"( - contract c { - uint256 a; - uint256 b; - bytes data; - function test() public { - for (uint8 i = 0; i <= 40; i++) - data.push(0x03); - for (uint8 j = 0; j <= 40; j++) { - assembly { - mstore(0, "garbage") - } - data.pop(); - } + return L.f(); + } + function y() public pure returns (I.Direction) { + return L.g(); } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs()); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode, 0, "L"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("x()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("y()"), encodeArgs(3)); } -BOOST_AUTO_TEST_CASE(external_array_args) +BOOST_AUTO_TEST_CASE(bytes_in_arguments) { char const* sourceCode = R"( contract c { - function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index) - external returns (uint av, uint bv, uint cv) { - av = a[a_index]; - bv = b[b_index]; - cv = c[c_index]; + uint result; + function f(uint a, uint b) public { result += a + b; } + function g(uint a) public { result *= a; } + function test(uint a, bytes calldata data1, bytes calldata data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) { + r_a = a; + address(this).call(data1); + address(this).call(data2); + r = result; + r_b = b; + l = data1.length; } } )"; - compileAndRun(sourceCode); - bytes params = encodeArgs( - 1, 2, 3, 4, 5, 6, 7, 8, // a - 32 * (8 + 1 + 5 + 1 + 1 + 1), // offset to b - 21, 22, 23, 24, 25, // c - 0, 1, 2, // (a,b,c)_index - 3, // b.length - 11, 12, 13 // b + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + + compileAndRun(sourceCode); + + string innercalldata1 = asString(FixedHash<4>(util::keccak256("f(uint256,uint256)")).asBytes() + encodeArgs(8, 9)); + string innercalldata2 = asString(FixedHash<4>(util::keccak256("g(uint256)")).asBytes() + encodeArgs(3)); + bytes calldata = encodeArgs( + 12, 32 * 4, u256(32 * 4 + 32 + (innercalldata1.length() + 31) / 32 * 32), 13, + u256(innercalldata1.length()), innercalldata1, + u256(innercalldata2.length()), innercalldata2); + ABI_CHECK( + callContractFunction("test(uint256,bytes,bytes,uint256)", calldata), + encodeArgs(12, (8 + 9) * 3, 13, u256(innercalldata1.length())) ); - ABI_CHECK(callContractFunction("test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256)", params), encodeArgs(1, 12, 23)); + ); } -BOOST_AUTO_TEST_CASE(bytes_index_access) +BOOST_AUTO_TEST_CASE(array_copy_storage_abi) { + // NOTE: This does not really test copying from storage to ABI directly, + // because it will always copy to memory first. char const* sourceCode = R"( + pragma abicoder v2; contract c { - bytes data; - function direct(bytes calldata arg, uint index) external returns (uint) { - return uint(uint8(arg[index])); + uint8[] x; + uint16[] y; + uint24[] z; + uint24[][] w; + function test1() public returns (uint8[] memory) { + for (uint i = 0; i < 101; ++i) + x.push(uint8(i)); + return x; } - function storageCopyRead(bytes calldata arg, uint index) external returns (uint) { - data = arg; - return uint(uint8(data[index])); + function test2() public returns (uint16[] memory) { + for (uint i = 0; i < 101; ++i) + y.push(uint16(i)); + return y; } - function storageWrite() external returns (uint) { - data = new bytes(35); - data[31] = 0x77; - data[32] = 0x14; - - data[31] = 0x01; - data[31] |= 0x08; - data[30] = 0x01; - data[32] = 0x03; - return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32])); + function test3() public returns (uint24[] memory) { + for (uint i = 0; i < 101; ++i) + z.push(uint24(i)); + return z; } - } - )"; - compileAndRun(sourceCode); - string array{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33}; - ABI_CHECK(callContractFunction("direct(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); - ABI_CHECK(callContractFunction("storageCopyRead(bytes,uint256)", 64, 33, u256(array.length()), array), encodeArgs(33)); - ABI_CHECK(callContractFunction("storageWrite()"), encodeArgs(0x193)); -} - -BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) -{ - char const* sourceCode = R"( - contract c { - uint[9] m_data; - uint[] m_data_dyn; - uint8[][] m_byte_data; - function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) { - m_data = a; - m_data_dyn = a; - m_byte_data = b; - return b[3][1]; // note that access and declaration are reversed to each other - } - function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { - a = m_data.length; - b = m_data[7]; - c = m_data_dyn.length; - d = m_data_dyn[7]; - e = m_byte_data.length; - f = m_byte_data[3].length; - g = m_byte_data[3][1]; + function test4() public returns (uint24[][] memory) { + w = new uint24[][](5); + for (uint i = 0; i < 5; ++i) + for (uint j = 0; j < 101; ++j) + w[i].push(uint24(j)); + return w; } } )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("store(uint256[9],uint8[3][])", encodeArgs(21, 22, 23, 24, 25, 26, 27, 28, 29, u256(32 * (9 + 1)), 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 )), encodeArgs(32)); - ABI_CHECK(callContractFunction("retrieve()"), encodeArgs(9, 28, 9, 28, 4, 3, 32)); -} + ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() -BOOST_AUTO_TEST_CASE(array_copy_including_array) -{ - char const* sourceCode = R"( - contract c { - uint[3][90][] large; - uint[3][3][] small; - function test() public returns (uint r) { - for (uint i = 0; i < 7; i++) { - large.push(); - small.push(); - } - large[3][2][0] = 2; - large[1] = large[3]; - small[3][2][0] = 2; - small[1] = small[2]; - r = (( - small[3][2][0] * 0x100 | - small[1][2][0]) * 0x100 | - large[3][2][0]) * 0x100 | - large[1][2][0]; - delete small; - delete large; - - } - function clear() public returns (uint, uint) { - for (uint i = 0; i < 7; i++) { - large.push(); - small.push(); - } - small[3][2][0] = 0; - large[3][2][0] = 0; - while (small.length > 0) - small.pop(); - while (large.length > 0) - large.pop(); - return (small.length, large.length); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("test()"), encodeArgs(0x02000202)); - BOOST_CHECK(storageEmpty(m_contractAddress)); - ABI_CHECK(callContractFunction("clear()"), encodeArgs(0, 0)); - BOOST_CHECK(storageEmpty(m_contractAddress)); + compileAndRun(sourceCode); + bytes valueSequence; + for (size_t i = 0; i < 101; ++i) + valueSequence += toBigEndian(u256(i)); + ABI_CHECK(callContractFunction("test1()"), encodeArgs(0x20, 101) + valueSequence); + ABI_CHECK(callContractFunction("test2()"), encodeArgs(0x20, 101) + valueSequence); + ABI_CHECK(callContractFunction("test3()"), encodeArgs(0x20, 101) + valueSequence); + ABI_CHECK(callContractFunction("test4()"), + encodeArgs(0x20, 5, 0xa0, 0xa0 + 102 * 32 * 1, 0xa0 + 102 * 32 * 2, 0xa0 + 102 * 32 * 3, 0xa0 + 102 * 32 * 4) + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + + encodeArgs(101) + valueSequence + ); + ); } //BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) @@ -3600,8 +2773,7 @@ BOOST_AUTO_TEST_CASE(invalid_enum_logged) BOOST_REQUIRE_EQUAL(logTopic(0, 0), util::keccak256(string("Log(uint8)"))); BOOST_CHECK_EQUAL(h256(logData(0)), h256(u256(0))); - // should throw - ABI_CHECK(callContractFunction("test_log()"), encodeArgs()); + ABI_CHECK(callContractFunction("test_log()"), panicData(PanicCode::EnumConversionError)); } BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) @@ -3618,7 +2790,7 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) } } )"; - ABI_CHECK(compileAndRunWithoutCheck({{"", sourceCode}}, 0, "A"), encodeArgs()); + ABI_CHECK(compileAndRunWithoutCheck({{"", sourceCode}}, 0, "A"), panicData(PanicCode::ArrayOutOfBounds)); BOOST_CHECK(!m_transactionSuccessful); } @@ -3640,7 +2812,7 @@ BOOST_AUTO_TEST_CASE(failing_send) } )"; compileAndRun(sourceCode, 0, "Helper"); - u160 const c_helperAddress = m_contractAddress; + h160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 20, "Main"); BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); } @@ -3824,159 +2996,6 @@ BOOST_AUTO_TEST_CASE(return_bytes_internal) } } -BOOST_AUTO_TEST_CASE(bytes_index_access_memory) -{ - char const* sourceCode = R"( - contract Main { - function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) { - c1 = _s1[i1]; - c2 = intern(_s1, i2); - c3 = internIndirect(_s1)[i3]; - } - function intern(bytes memory _s1, uint i) public returns (byte c) { - return _s1[i]; - } - function internIndirect(bytes memory _s1) public returns (bytes memory) { - return _s1; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - bytes args1 = encodeArgs(u256(0x80), u256(3), u256(4), u256(5)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(bytes,uint256,uint256,uint256)", asString(args1)) == - encodeArgs(string{s1[3]}, string{s1[4]}, string{s1[5]}) - ); -} - -BOOST_AUTO_TEST_CASE(bytes_in_constructors_unpacker) -{ - char const* sourceCode = R"( - contract Test { - uint public m_x; - bytes public m_s; - constructor(uint x, bytes memory s) { - m_x = x; - m_s = s; - } - } - )"; - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - compileAndRun(sourceCode, 0, "Test", args1); - BOOST_REQUIRE(callContractFunction("m_x()") == encodeArgs(x)); - BOOST_REQUIRE(callContractFunction("m_s()") == encodeArgs(u256(0x20)) + dyn1); -} - -BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer) -{ - char const* sourceCode = R"( - contract Base { - uint public m_x; - bytes m_s; - constructor(uint x, bytes memory s) { - m_x = x; - m_s = s; - } - function part(uint i) public returns (byte) { - return m_s[i]; - } - } - contract Main is Base { - constructor(bytes memory s, uint x) Base(x, f(s)) {} - function f(bytes memory s) public returns (bytes memory) { - return s; - } - } - contract Creator { - function f(uint x, bytes memory s) public returns (uint r, byte ch) { - Main c = new Main(s, x); - r = c.m_x(); - ch = c.part(x); - } - } - )"; - compileAndRun(sourceCode, 0, "Creator"); - string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - bytes dyn1 = encodeArgs(u256(s1.length()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(uint256,bytes)", asString(args1)) == - encodeArgs(x, string{s1[unsigned(x)]}) - ); -} - -BOOST_AUTO_TEST_CASE(arrays_in_constructors) -{ - char const* sourceCode = R"( - contract Base { - uint public m_x; - address[] m_s; - constructor(uint x, address[] memory s) { - m_x = x; - m_s = s; - } - function part(uint i) public returns (address) { - return m_s[i]; - } - } - contract Main is Base { - constructor(address[] memory s, uint x) Base(x, f(s)) {} - function f(address[] memory s) public returns (address[] memory) { - return s; - } - } - contract Creator { - function f(uint x, address[] memory s) public returns (uint r, address ch) { - Main c = new Main(s, x); - r = c.m_x(); - ch = c.part(x); - } - } - )"; - compileAndRun(sourceCode, 0, "Creator"); - vector s1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - bytes dyn1 = encodeArgs(u256(s1.size()), s1); - u256 x = 7; - bytes args1 = encodeArgs(x, u256(0x40)) + dyn1; - BOOST_REQUIRE( - callContractFunction("f(uint256,address[])", asString(args1)) == - encodeArgs(x, s1[unsigned(x)]) - ); -} - -BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) -{ - char const* sourceCode = R"( - contract Test { - uint24[] public data; - function set(uint24[] memory _data) public returns (uint) { - data = _data; - return data.length; - } - function get() public returns (uint24[] memory) { - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - BOOST_REQUIRE( - callContractFunction("set(uint24[])", u256(0x20), u256(data.size()), data) == - encodeArgs(u256(data.size())) - ); - ABI_CHECK(callContractFunction("data(uint256)", u256(7)), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("data(uint256)", u256(15)), encodeArgs(u256(16))); - ABI_CHECK(callContractFunction("data(uint256)", u256(18)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size()), data)); -} - BOOST_AUTO_TEST_CASE(memory_types_initialisation) { char const* sourceCode = R"( @@ -3999,39 +3018,10 @@ BOOST_AUTO_TEST_CASE(memory_types_initialisation) ABI_CHECK(callContractFunction("nestedStat()"), encodeArgs(vector(3 * 7))); } -BOOST_AUTO_TEST_CASE(memory_arrays_delete) -{ - char const* sourceCode = R"( - contract Test { - function del() public returns (uint24[3][4] memory) { - uint24[3][4] memory x; - for (uint24 i = 0; i < x.length; i ++) - for (uint24 j = 0; j < x[i].length; j ++) - x[i][j] = i * 0x10 + j; - delete x[1]; - delete x[3][2]; - return x; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - for (unsigned i = 0; i < 4; i++) - for (unsigned j = 0; j < 3; j++) - { - u256 v = 0; - if (!(i == 1 || (i == 3 && j == 2))) - v = i * 0x10 + j; - data[i * 3 + j] = v; - } - ABI_CHECK(callContractFunction("del()"), encodeArgs(data)); -} - BOOST_AUTO_TEST_CASE(calldata_struct_short) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { struct S { uint256 a; uint256 b; } function f(S calldata) external pure returns (uint256) { @@ -4054,7 +3044,7 @@ BOOST_AUTO_TEST_CASE(calldata_struct_short) BOOST_AUTO_TEST_CASE(calldata_struct_function_type) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { struct S { function (uint) external returns (uint) fn; } function f(S calldata s) external returns (uint256) { @@ -4076,86 +3066,6 @@ BOOST_AUTO_TEST_CASE(calldata_struct_function_type) ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23)); } -BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(bytes[] calldata a, uint256 i) external returns (uint) { - return uint8(a[0][i]); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 0, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs('a') - ); - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 1, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs('b') - ); - ABI_CHECK( - callContractFunction("f(bytes[],uint256)", 0x40, 2, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs() - ); -} - -BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) -{ - vector> data { - { 0x0A01, 0x0A02, 0x0A03 }, - { 0x0B01, 0x0B02, 0x0B03, 0x0B04 } - }; - - for (bool outerDynamicallySized: { true, false }) - { - string arrayType = outerDynamicallySized ? "uint256[][]" : "uint256[][2]"; - string sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function test()" + arrayType + R"( calldata a) external returns (uint256) { - return a.length; - } - function test()" + arrayType + R"( calldata a, uint256 i) external returns (uint256) { - return a[i].length; - } - function test()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { - return a[i][j]; - } - function reenc()" + arrayType + R"( calldata a, uint256 i, uint256 j) external returns (uint256) { - return this.test(a, i, j); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes encoding = encodeArray( - outerDynamicallySized, - true, - data | boost::adaptors::transformed([&](vector const& _values) { - return encodeArray(true, false, _values); - }) - ); - - ABI_CHECK(callContractFunction("test(" + arrayType + ")", 0x20, encoding), encodeArgs(data.size())); - for (size_t i = 0; i < data.size(); i++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, i, encoding), encodeArgs(data[i].size())); - for (size_t j = 0; j < data[i].size(); j++) - { - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); - ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256)", 0x60, i, j, encoding), encodeArgs(data[i][j])); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); - } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), encodeArgs()); - } -} - BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) { vector>> data { @@ -4183,7 +3093,7 @@ BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) arrayType += outerDynamicallySized ? "[]" : "[2]"; string sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { function test()" + arrayType + R"( calldata a) external returns (uint256) { return a.length; @@ -4230,14 +3140,11 @@ BOOST_AUTO_TEST_CASE(calldata_array_dynamic_three_dimensional) ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); ABI_CHECK(callContractFunction("reenc(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, k, encoding), encodeArgs(data[i][j][k])); } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, data[i][j].size(), encoding), encodeArgs()); + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256,uint256)", 0x80, i, j, data[i][j].size(), encoding), panicData(PanicCode::ArrayOutOfBounds)); } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), encodeArgs()); + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256,uint256)", 0x60, i, data[i].size(), encoding), panicData(PanicCode::ArrayOutOfBounds)); } - // out of bounds access - ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), encodeArgs()); + ABI_CHECK(callContractFunction("test(" + arrayType + ",uint256)", 0x40, data.size(), encoding), panicData(PanicCode::ArrayOutOfBounds)); } } @@ -4305,6 +3212,8 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key) }; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, 0, "Test"); for (unsigned i = 0; i < strings.size(); i++) ABI_CHECK(callContractFunction( @@ -4377,7 +3286,7 @@ BOOST_AUTO_TEST_CASE(nested_string_as_public_mapping_key) ABI_CHECK(callContractFunction( "set(string,string,uint256)", u256(0x60), - u256(roundTo32(0x80 + strings[i].size())), + u256(roundTo32(static_cast(0x80 + strings[i].size()))), u256(7 + i), u256(strings[i].size()), strings[i], @@ -4388,7 +3297,7 @@ BOOST_AUTO_TEST_CASE(nested_string_as_public_mapping_key) ABI_CHECK(callContractFunction( "data(string,string)", u256(0x40), - u256(roundTo32(0x60 + strings[i].size())), + u256(roundTo32(static_cast(0x60 + strings[i].size()))), u256(strings[i].size()), strings[i], u256(strings[i+1].size()), @@ -4441,7 +3350,7 @@ BOOST_AUTO_TEST_CASE(nested_mixed_string_as_public_mapping_key) u256(0xA0), u256(data[i].s2), u256(data[i].s3), - u256(roundTo32(0xC0 + data[i].s1.size())), + u256(roundTo32(static_cast(0xC0 + data[i].s1.size()))), u256(i - 3), u256(data[i].s1.size()), data[i].s1, @@ -4454,7 +3363,7 @@ BOOST_AUTO_TEST_CASE(nested_mixed_string_as_public_mapping_key) u256(0x80), u256(data[i].s2), u256(data[i].s3), - u256(roundTo32(0xA0 + data[i].s1.size())), + u256(roundTo32(static_cast(0xA0 + data[i].s1.size()))), u256(data[i].s1.size()), data[i].s1, u256(data[i].s4.size()), @@ -4505,22 +3414,22 @@ BOOST_AUTO_TEST_CASE(library_call) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(33) * 9)); } BOOST_AUTO_TEST_CASE(library_function_external) { char const* sourceCode = R"( - library Lib { function m(bytes calldata b) external pure returns (byte) { return b[2]; } } + library Lib { function m(bytes calldata b) external pure returns (bytes1) { return b[2]; } } contract Test { - function f(bytes memory b) public pure returns (byte) { + function f(bytes memory b) public pure returns (bytes1) { return Lib.m(b); } } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), "abcde"), encodeArgs("c")); } @@ -4537,7 +3446,7 @@ BOOST_AUTO_TEST_CASE(library_stray_values) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(42))); } @@ -4550,7 +3459,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) for (uint i = 0; i < _haystack.length; ++i) if (_haystack[i] == _needle) return i; - return uint(-1); + return type(uint).max; } } contract Test { @@ -4567,7 +3476,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(4), u256(17))); } @@ -4598,7 +3507,7 @@ BOOST_AUTO_TEST_CASE(mapping_arguments_in_library) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(1), u256(42)), encodeArgs(u256(0))); ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(2), u256(84)), encodeArgs(u256(0))); ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(21), u256(7)), encodeArgs(u256(0))); @@ -4646,7 +3555,7 @@ BOOST_AUTO_TEST_CASE(mapping_returns_in_library) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(1), u256(42)), encodeArgs(u256(0))); ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(2), u256(84)), encodeArgs(u256(0))); ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(21), u256(7)), encodeArgs(u256(0))); @@ -4722,7 +3631,7 @@ BOOST_AUTO_TEST_CASE(mapping_returns_in_library_named) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(84))); ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(17))); } @@ -4750,7 +3659,7 @@ BOOST_AUTO_TEST_CASE(using_library_mappings_public) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); } @@ -4783,9 +3692,9 @@ BOOST_AUTO_TEST_CASE(using_library_mappings_external) )"; for (auto v2: {false, true}) { - string prefix = v2 ? "pragma experimental ABIEncoderV2;\n" : ""; + string prefix = "pragma abicoder " + string(v2 ? "v2" : "v1") + ";\n"; compileAndRun(prefix + libSourceCode, 0, "Lib"); - compileAndRun(prefix + sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(prefix + sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(2), u256(0), u256(84), u256(46), u256(0), u256(198))); } } @@ -4811,7 +3720,7 @@ BOOST_AUTO_TEST_CASE(using_library_mappings_return) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), u256(0), u256(42), u256(23), u256(0), u256(99))); } @@ -4839,7 +3748,7 @@ BOOST_AUTO_TEST_CASE(using_library_structs) } )"; compileAndRun(sourceCode, 0, "Lib"); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7), u256(8))); } @@ -4877,7 +3786,7 @@ BOOST_AUTO_TEST_CASE(short_strings) if (data1[0] != "1") return 10; if (data1[4] != "4") return 11; for (uint i = 0; i < data1.length; i ++) - data1[i] = byte(uint8(i * 3)); + data1[i] = bytes1(uint8(i * 3)); if (uint8(data1[4]) != 4 * 3) return 12; if (uint8(data1[67]) != 67 * 3) return 13; // change length: long -> short @@ -4997,10 +3906,10 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) compileAndRun(sourceCode, 10, "c"); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); - ABI_CHECK(callContractFunction("f(address)", encodeArgs(u160(libraryAddress))), encodeArgs(false)); + ABI_CHECK(callContractFunction("f(address)", encodeArgs(libraryAddress)), encodeArgs(false)); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); - ABI_CHECK(callContractFunction("f(address)", encodeArgs(u160(m_contractAddress))), encodeArgs(true)); + ABI_CHECK(callContractFunction("f(address)", encodeArgs(m_contractAddress)), encodeArgs(true)); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 10); BOOST_CHECK_EQUAL(balanceAt(libraryAddress), 0); } @@ -5044,7 +3953,7 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_int) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); ABI_CHECK(callContractFunction("f(uint256)", u256(9)), encodeArgs(u256(2 * 9))); } @@ -5062,7 +3971,7 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_struct) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(3 * 7))); ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(3 * 7))); } @@ -5085,7 +3994,7 @@ BOOST_AUTO_TEST_CASE(using_for_overload) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); } @@ -5104,7 +4013,7 @@ BOOST_AUTO_TEST_CASE(using_for_by_name) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); } @@ -5124,7 +4033,7 @@ BOOST_AUTO_TEST_CASE(bound_function_in_function) } )"; compileAndRun(sourceCode, 0, "L"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); } @@ -5142,7 +4051,7 @@ BOOST_AUTO_TEST_CASE(bound_function_in_var) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); ABI_CHECK(callContractFunction("f(uint256)", u256(7)), encodeArgs(u256(6 * 7))); ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(6 * 7))); } @@ -5165,7 +4074,7 @@ BOOST_AUTO_TEST_CASE(bound_function_to_string) } )"; compileAndRun(sourceCode, 0, "D"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"D", m_contractAddress}}); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(3))); ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(3))); } @@ -5317,9 +4226,9 @@ BOOST_AUTO_TEST_CASE(mutex) } )"; compileAndRun(sourceCode, 500, "Fund"); - auto fund = m_contractAddress; + h160 const fund = m_contractAddress; BOOST_CHECK_EQUAL(balanceAt(fund), 500); - compileAndRun(sourceCode, 0, "Attacker", encodeArgs(u160(fund))); + compileAndRun(sourceCode, 0, "Attacker", encodeArgs(fund)); ABI_CHECK(callContractFunction("setProtected(bool)", true), encodeArgs()); ABI_CHECK(callContractFunction("attack()"), encodeArgs()); BOOST_CHECK_EQUAL(balanceAt(fund), 500); @@ -5363,7 +4272,7 @@ BOOST_AUTO_TEST_CASE(payable_function_calls_library) } )"; compileAndRun(sourceCode, 0, "L"); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); + compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs(u256(7))); } @@ -5399,28 +4308,6 @@ BOOST_AUTO_TEST_CASE(non_payable_throw) BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); } -BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier) -{ - char const* sourceCode = R"( - contract C { - modifier tryCircumvent { - if (false) _; // avoid the function, we should still not accept ether - } - function f() tryCircumvent public returns (uint) { - return msgvalue(); - } - function msgvalue() internal returns (uint) { - return msg.value; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs()); - BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); - ) -} - BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) { // This tests that memory resize for return values is not paid during the call, which would @@ -5437,7 +4324,7 @@ BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) )"; compileAndRun(sourceCode, 0, "C"); - u160 cAddr = m_contractAddress; + h160 const cAddr = m_contractAddress; compileAndRun(sourceCode, 0, "D"); ABI_CHECK(callContractFunction("f(address)", cAddr), encodeArgs(u256(7))); } @@ -5527,6 +4414,8 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) contract C2 {} )"; ALSO_VIA_YUL( + DISABLE_EWASM_TESTRUN() + compileAndRun(sourceCode, 0, "C1"); compileAndRun(sourceCode, 0, "C2"); ) @@ -5751,7 +4640,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) revert("message"); } function f() public { - address(this).transfer(0); + payable(this).transfer(0); } } contract C { @@ -5849,7 +4738,7 @@ BOOST_AUTO_TEST_CASE(interface_contract) } )"; compileAndRun(sourceCode, 0, "A"); - u160 const recipient = m_contractAddress; + h160 const recipient = m_contractAddress; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(address)", recipient), encodeArgs(true)); } @@ -6051,7 +4940,8 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked) )"; for (auto v2: {false, true}) { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + string prefix = "pragma abicoder " + string(v2 ? "v2" : "v1") + ";\n"; + compileAndRun(prefix + sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 2, "\x01\x02")); ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); @@ -6125,7 +5015,8 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked_from_storage) )"; for (auto v2: {false, true}) { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + string prefix = "pragma abicoder " + string(v2 ? "v2" : "v1") + ";\n"; + compileAndRun(prefix + sourceCode, 0, "C"); bytes payload = encodeArgs(0xfffff1, 0, 0xfffff2, 0, 0, 0xfffff3, 0, 0, 0xfffff4); bytes encoded = encodeArgs(0x20, 0x122, "\x01" + asString(payload) + "\x02"); ABI_CHECK(callContractFunction("sf()"), encoded); @@ -6195,7 +5086,8 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked_from_memory) )"; for (auto v2: {false, true}) { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + string prefix = "pragma abicoder " + string(v2 ? "v2" : "v1") + ";\n"; + compileAndRun(prefix + sourceCode, 0, "C"); bytes payload = encodeArgs(0xfffff1, 0, 0xfffff2, 0, 0, 0xfffff3, 0, 0, 0xfffff4); bytes encoded = encodeArgs(0x20, 0x122, "\x01" + asString(payload) + "\x02"); ABI_CHECK(callContractFunction("sf()"), encoded); @@ -6238,7 +5130,8 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked_functionPtr) )"; for (auto v2: {false, true}) { - compileAndRun(string(v2 ? "pragma experimental ABIEncoderV2;\n" : "") + sourceCode, 0, "C"); + string prefix = "pragma abicoder " + string(v2 ? "v2" : "v1") + ";\n"; + compileAndRun(prefix + sourceCode, 0, "C"); string directEncoding = asString(fromHex("08" "1112131400000000000011121314000000000087" "26121ff0" "02")); ABI_CHECK(callContractFunction("testDirect()"), encodeArgs(0x20, directEncoding.size(), directEncoding)); string arrayEncoding = asString(fromHex("08" "1112131400000000000011121314000000000087" "26121ff0" "0000000000000000" "02")); @@ -6250,7 +5143,7 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked_functionPtr) BOOST_AUTO_TEST_CASE(abi_encodePackedV2_structs) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { struct S { uint8 a; @@ -6292,7 +5185,7 @@ BOOST_AUTO_TEST_CASE(abi_encodePackedV2_structs) BOOST_AUTO_TEST_CASE(abi_encodePackedV2_nestedArray) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { struct S { uint8 a; @@ -6321,7 +5214,7 @@ BOOST_AUTO_TEST_CASE(abi_encodePackedV2_nestedArray) BOOST_AUTO_TEST_CASE(abi_encodePackedV2_arrayOfStrings) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { string[] x; event E(string[] indexed); @@ -6355,7 +5248,7 @@ BOOST_AUTO_TEST_CASE(event_signature_in_library) // This tests a bug that was present where the "internal signature" // for structs was also used for events. char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; library L { struct S { uint8 a; @@ -6394,7 +5287,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selector) } function f3() public pure returns (bytes memory) { bytes4 x = 0x12345678; - return abi.encodeWithSelector(x, uint(-1)); + return abi.encodeWithSelector(x, type(uint).max); } } )"; @@ -6412,7 +5305,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selector) BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) { char const* sourceCode = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { function f0() public pure returns (bytes memory) { return abi.encodeWithSelector(0x12345678); @@ -6426,7 +5319,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) } function f3() public pure returns (bytes memory) { bytes4 x = 0x12345678; - return abi.encodeWithSelector(x, uint(-1)); + return abi.encodeWithSelector(x, type(uint).max); } struct S { uint a; string b; uint16 c; } function f4() public pure returns (bytes memory) { @@ -6435,7 +5328,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) s.a = 0x1234567; s.b = "Lorem ipsum dolor sit ethereum........"; s.c = 0x1234; - return abi.encodeWithSelector(x, uint(-1), s, uint(3)); + return abi.encodeWithSelector(x, type(uint).max, s, uint(3)); } } )"; @@ -6475,10 +5368,10 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signature) function f2() public pure returns (bytes memory r, uint[] memory ar) { string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; uint[] memory y = new uint[](4); - y[0] = uint(-1); - y[1] = uint(-2); - y[2] = uint(-3); - y[3] = uint(-4); + y[0] = type(uint).max; + y[1] = type(uint).max - 1; + y[2] = type(uint).max - 2; + y[3] = type(uint).max - 3; r = abi.encodeWithSignature(x, y); // The hash uses temporary memory. This allocation re-uses the memory // and should initialize it properly. @@ -6502,7 +5395,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signature) BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) { char const* sourceCode = R"T( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract C { function f0() public pure returns (bytes memory) { return abi.encodeWithSignature("f(uint256)"); @@ -6519,10 +5412,10 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) function f2() public pure returns (bytes memory r, uint[] memory ar) { string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; uint[] memory y = new uint[](4); - y[0] = uint(-1); - y[1] = uint(-2); - y[2] = uint(-3); - y[3] = uint(-4); + y[0] = type(uint).max; + y[1] = type(uint).max - 1; + y[2] = type(uint).max - 2; + y[3] = type(uint).max - 3; r = abi.encodeWithSignature(x, y); // The hash uses temporary memory. This allocation re-uses the memory // and should initialize it properly. @@ -6535,7 +5428,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) s.a = 0x1234567; s.b = "Lorem ipsum dolor sit ethereum........"; s.c = 0x1234; - return abi.encodeWithSignature(s.b, uint(-1), s, uint(3)); + return abi.encodeWithSignature(s.b, type(uint).max, s, uint(3)); } } )T"; @@ -6664,7 +5557,7 @@ BOOST_AUTO_TEST_CASE(event_wrong_abi_name) } )"; compileAndRun(sourceCode, 0, "ClientReceipt", bytes()); - compileAndRun(sourceCode, 0, "Test", bytes(), map{{"ClientReceipt", m_contractAddress}}); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"ClientReceipt", m_contractAddress}}); callContractFunction("f()"); BOOST_REQUIRE_EQUAL(numLogs(), 1); @@ -6703,7 +5596,7 @@ BOOST_AUTO_TEST_CASE(dirty_scratch_space_prior_to_constant_optimiser) } uint x = 0x0000000000001234123412431234123412412342112341234124312341234124; // This is just to create many instances of x - emit X(x + f() * g(tx.origin) ^ h(block.number)); + unchecked { emit X(x + f() * g(tx.origin) ^ h(block.number)); } assembly { // make scratch space dirty mstore(0, 0x4242424242424242424242424242424242424242424242424242424242424242) @@ -6714,10 +5607,10 @@ BOOST_AUTO_TEST_CASE(dirty_scratch_space_prior_to_constant_optimiser) return 0x0000000000001234123412431234123412412342112341234124312341234124; } function g(address a) internal pure returns (uint) { - return uint(a) * 0x0000000000001234123412431234123412412342112341234124312341234124; + unchecked { return uint(uint160(a)) * 0x0000000000001234123412431234123412412342112341234124312341234124; } } function h(uint a) internal pure returns (uint) { - return a * 0x0000000000001234123412431234123412412342112341234124312341234124; + unchecked { return a * 0x0000000000001234123412431234123412412342112341234124312341234124; } } } )"; @@ -6730,59 +5623,6 @@ BOOST_AUTO_TEST_CASE(dirty_scratch_space_prior_to_constant_optimiser) ); } -BOOST_AUTO_TEST_CASE(try_catch_library_call) -{ - char const* sourceCode = R"( - library L { - struct S { uint x; } - function integer(uint t, bool b) public view returns (uint) { - if (b) { - return t; - } else { - revert("failure"); - } - } - function stru(S storage t, bool b) public view returns (uint) { - if (b) { - return t.x; - } else { - revert("failure"); - } - } - } - contract C { - using L for L.S; - L.S t; - function f(bool b) public returns (uint, string memory) { - uint x = 8; - try L.integer(x, b) returns (uint _x) { - return (_x, ""); - } catch Error(string memory message) { - return (18, message); - } - } - function g(bool b) public returns (uint, string memory) { - t.x = 9; - try t.stru(b) returns (uint x) { - return (x, ""); - } catch Error(string memory message) { - return (19, message); - } - } - } - )"; - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - compileAndRun(sourceCode, 0, "L", bytes()); - compileAndRun(sourceCode, 0, "C", bytes(), map{{"L", m_contractAddress}}); - - ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(8, 0x40, 0)); - ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(18, 0x40, 7, "failure")); - ABI_CHECK(callContractFunction("g(bool)", true), encodeArgs(9, 0x40, 0)); - ABI_CHECK(callContractFunction("g(bool)", false), encodeArgs(19, 0x40, 7, "failure")); - } -} - BOOST_AUTO_TEST_CASE(strip_reason_strings) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index d0f4227cc1e7..ab0bba27d164 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include using namespace solidity; using namespace solidity::test; @@ -43,16 +45,24 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( entry.second = addPreamble(entry.second); m_compiler.reset(); + m_compiler.enableEwasmGeneration(m_compileToEwasm); m_compiler.setSources(sourcesWithPreamble); m_compiler.setLibraries(_libraryAddresses); m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setEVMVersion(m_evmVersion); m_compiler.setOptimiserSettings(m_optimiserSettings); + m_compiler.enableEvmBytecodeGeneration(!m_compileViaYul); m_compiler.enableIRGeneration(m_compileViaYul); m_compiler.setRevertStringBehaviour(m_revertStrings); if (!m_compiler.compile()) { - langutil::SourceReferenceFormatter formatter(std::cerr); + // The testing framework expects an exception for + // "unimplemented" yul IR generation. + if (m_compileViaYul) + for (auto const& error: m_compiler.errors()) + if (error->type() == langutil::Error::Type::CodeGenerationError) + BOOST_THROW_EXCEPTION(*error); + langutil::SourceReferenceFormatter formatter(std::cerr, true, false); for (auto const& error: m_compiler.errors()) formatter.printErrorInformation(*error); @@ -62,38 +72,41 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( evmasm::LinkerObject obj; if (m_compileViaYul) { - // Try compiling twice: If the first run fails due to stack errors, forcefully enable - // the optimizer. - for (bool forceEnableOptimizer: {false, true}) + if (m_compileToEwasm) + obj = m_compiler.ewasmObject(contractName); + else { - OptimiserSettings optimiserSettings = m_optimiserSettings; - if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser) + // Try compiling twice: If the first run fails due to stack errors, forcefully enable + // the optimizer. + for (bool forceEnableOptimizer: {false, true}) { - // Enable some optimizations on the first run - optimiserSettings.runYulOptimiser = true; - optimiserSettings.yulOptimiserSteps = "uljmul jmul"; - } - else if (forceEnableOptimizer) - optimiserSettings = OptimiserSettings::full(); + OptimiserSettings optimiserSettings = m_optimiserSettings; + if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser) + { + // Enable some optimizations on the first run + optimiserSettings.runYulOptimiser = true; + optimiserSettings.yulOptimiserSteps = "uljmul jmul"; + } + else if (forceEnableOptimizer) + optimiserSettings = OptimiserSettings::full(); - yul::AssemblyStack asmStack( - m_evmVersion, - yul::AssemblyStack::Language::StrictAssembly, - optimiserSettings - ); - bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); - solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); + yul::AssemblyStack + asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, optimiserSettings); + bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); + solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); - try - { - asmStack.optimize(); - obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); - break; - } - catch (...) - { - if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full()) - throw; + try + { + asmStack.optimize(); + obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); + obj.link(_libraryAddresses); + break; + } + catch (...) + { + if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full()) + throw; + } } } } @@ -122,10 +135,13 @@ string SolidityExecutionFramework::addPreamble(string const& _sourceCode) { // Silence compiler version warning string preamble = "pragma solidity >=0.0;\n"; + if (_sourceCode.find("// SPDX-License-Identifier:") == string::npos) + preamble += "// SPDX-License-Identifier: unlicensed\n"; if ( - solidity::test::CommonOptions::get().useABIEncoderV2 && - _sourceCode.find("pragma experimental ABIEncoderV2;") == string::npos + solidity::test::CommonOptions::get().useABIEncoderV1 && + _sourceCode.find("pragma experimental ABIEncoderV2;") == string::npos && + _sourceCode.find("pragma abicoder") == string::npos ) - preamble += "pragma experimental ABIEncoderV2;\n"; + preamble += "pragma abicoder v1;\n"; return preamble + _sourceCode; } diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 9efe22b2096e..f616b2419140 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -32,9 +32,6 @@ #include -#include -#include - namespace solidity::frontend::test { @@ -43,8 +40,8 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework public: SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} - explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion): - ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) + explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion, std::vector const& _vmPaths): + ExecutionFramework(_evmVersion, _vmPaths), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} bytes const& compileAndRunWithoutCheck( @@ -72,12 +69,14 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework std::map const& _libraryAddresses = {} ); - /// Returns @param _sourceCode prefixed with the version pragma and the ABIEncoderV2 pragma, - /// the latter only if it is required. + /// Returns @param _sourceCode prefixed with the version pragma and the abi coder v1 pragma, + /// the latter only if it is forced. static std::string addPreamble(std::string const& _sourceCode); protected: + solidity::frontend::CompilerStack m_compiler; bool m_compileViaYul = false; + bool m_compileToEwasm = false; bool m_showMetadata = false; RevertStrings m_revertStrings = RevertStrings::Default; }; diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index c11c01b50f95..ec4c9fd2c96b 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include @@ -33,6 +35,7 @@ #include #include #include +#include #include #include @@ -92,18 +95,20 @@ Declaration const& resolveDeclaration( } bytes compileFirstExpression( - const string& _sourceCode, + string const& _sourceCode, vector> _functions = {}, vector> _localVariables = {} ) { + string sourceCode = "pragma solidity >=0.0; // SPDX-License-Identifier: GPL-3\n" + _sourceCode; + ASTPointer sourceUnit; try { ErrorList errors; ErrorReporter errorReporter(errors); sourceUnit = Parser(errorReporter, solidity::test::CommonOptions::get().evmVersion()).parse( - make_shared(CharStream(_sourceCode, "")) + make_shared(CharStream(sourceCode, "")) ); if (!sourceUnit) return bytes(); @@ -117,6 +122,8 @@ bytes compileFirstExpression( ErrorList errors; ErrorReporter errorReporter(errors); GlobalContext globalContext; + Scoper::assignScopes(*sourceUnit); + BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); resolver.registerDeclarations(*sourceUnit); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); @@ -137,12 +144,13 @@ bytes compileFirstExpression( ); context.resetVisitedNodes(contract); context.setMostDerivedContract(*contract); + context.setArithmetic(Arithmetic::Wrapping); size_t parametersSize = _localVariables.size(); // assume they are all one slot on the stack context.adjustStackOffset(static_cast(parametersSize)); for (vector const& variable: _localVariables) context.addVariable( dynamic_cast(resolveDeclaration(*sourceUnit, variable, resolver)), - parametersSize-- + static_cast(parametersSize--) ); ExpressionCompiler( @@ -154,7 +162,10 @@ bytes compileFirstExpression( context << context.functionEntryLabel(dynamic_cast( resolveDeclaration(*sourceUnit, function, resolver) )); - bytes instructions = context.assembledObject().bytecode; + BOOST_REQUIRE(context.assemblyPtr()); + LinkerObject const& object = context.assemblyPtr()->assemble(); + BOOST_REQUIRE(object.immutableReferences.empty()); + bytes instructions = object.bytecode; // debug // cout << evmasm::disassemble(instructions) << endl; return instructions; @@ -184,7 +195,7 @@ BOOST_AUTO_TEST_CASE(literal_false) { char const* sourceCode = R"( contract test { - function f() { bool x = false; } + function f() public { bool x = false; } } )"; bytes code = compileFirstExpression(sourceCode); @@ -197,7 +208,7 @@ BOOST_AUTO_TEST_CASE(int_literal) { char const* sourceCode = R"( contract test { - function f() { uint x = 0x12345678901234567890; } + function f() public { uint x = 0x12345678901234567890; } } )"; bytes code = compileFirstExpression(sourceCode); @@ -256,7 +267,7 @@ BOOST_AUTO_TEST_CASE(comparison) { char const* sourceCode = R"( contract test { - function f() { bool x = (0x10aa < 0x11aa) != true; } + function f() public { bool x = (0x10aa < 0x11aa) != true; } } )"; bytes code = compileFirstExpression(sourceCode); @@ -288,7 +299,7 @@ BOOST_AUTO_TEST_CASE(short_circuiting) { char const* sourceCode = R"( contract test { - function f() { bool x = true != (4 <= 8 + 10 || 9 != 2); } + function f() public { bool x = true != (4 <= 8 + 10 || 9 != 2); } } )"; bytes code = compileFirstExpression(sourceCode); @@ -319,14 +330,28 @@ BOOST_AUTO_TEST_CASE(arithmetic) { char const* sourceCode = R"( contract test { - function f(uint y) { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } + function f(uint y) public { unchecked { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); + bytes panic = + bytes{uint8_t(Instruction::PUSH32)} + + fromHex("4E487B7100000000000000000000000000000000000000000000000000000000") + + bytes{ + uint8_t(Instruction::PUSH1), 0x0, + uint8_t(Instruction::MSTORE), + uint8_t(Instruction::PUSH1), 0x12, + uint8_t(Instruction::PUSH1), 0x4, + uint8_t(Instruction::MSTORE), + uint8_t(Instruction::PUSH1), 0x24, + uint8_t(Instruction::PUSH1), 0x0, + uint8_t(Instruction::REVERT) + }; + bytes expectation; if (solidity::test::CommonOptions::get().optimize) - expectation = { + expectation = bytes{ uint8_t(Instruction::PUSH1), 0x2, uint8_t(Instruction::PUSH1), 0x3, uint8_t(Instruction::PUSH1), 0x5, @@ -343,24 +368,24 @@ BOOST_AUTO_TEST_CASE(arithmetic) uint8_t(Instruction::DUP2), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), - uint8_t(Instruction::PUSH1), 0x1b, - uint8_t(Instruction::JUMPI), - uint8_t(Instruction::INVALID), + uint8_t(Instruction::PUSH1), 0x48, + uint8_t(Instruction::JUMPI) + } + panic + bytes{ uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::MOD), uint8_t(Instruction::DUP2), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), - uint8_t(Instruction::PUSH1), 0x24, - uint8_t(Instruction::JUMPI), - uint8_t(Instruction::INVALID), + uint8_t(Instruction::PUSH1), 0x7e, + uint8_t(Instruction::JUMPI) + } + panic + bytes{ uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::DIV), uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::MUL) }; else - expectation = { + expectation = bytes{ uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::PUSH1), 0x2, uint8_t(Instruction::PUSH1), 0x3, @@ -378,21 +403,22 @@ BOOST_AUTO_TEST_CASE(arithmetic) uint8_t(Instruction::DUP2), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), - uint8_t(Instruction::PUSH1), 0x1d, - uint8_t(Instruction::JUMPI), - uint8_t(Instruction::INVALID), + uint8_t(Instruction::PUSH1), 0x4a, + uint8_t(Instruction::JUMPI) + } + panic + bytes{ uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::MOD), uint8_t(Instruction::DUP2), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), - uint8_t(Instruction::PUSH1), 0x26, - uint8_t(Instruction::JUMPI), - uint8_t(Instruction::INVALID), + uint8_t(Instruction::PUSH1), 0x80, + uint8_t(Instruction::JUMPI) + } + panic + bytes{ uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::DIV), uint8_t(Instruction::MUL) }; + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -400,7 +426,7 @@ BOOST_AUTO_TEST_CASE(unary_operators) { char const* sourceCode = R"( contract test { - function f(int y) { !(~- y == 2); } + function f(int y) public { unchecked { !(~- y == 2); } } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); @@ -433,7 +459,7 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) { char const* sourceCode = R"( contract test { - function f(uint a) public returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); } + function f(uint a) public returns (uint x) { unchecked { x = --a ^ (a-- ^ (++a ^ a++)); } } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); @@ -490,7 +516,7 @@ BOOST_AUTO_TEST_CASE(assignment) { char const* sourceCode = R"( contract test { - function f(uint a, uint b) { (a += b) * 2; } + function f(uint a, uint b) public { unchecked { (a += b) * 2; } } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}}); @@ -528,7 +554,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_8bits) { char const* sourceCode = R"( contract test { - function f() { int8 x = -0x80; } + function f() public { int8 x = -0x80; } } )"; bytes code = compileFirstExpression(sourceCode); @@ -541,7 +567,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_16bits) { char const* sourceCode = R"( contract test { - function f() { int64 x = ~0xabc; } + function f() public { int64 x = ~0xabc; } } )"; bytes code = compileFirstExpression(sourceCode); @@ -556,7 +582,7 @@ BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals) // have been applied char const* sourceCode = R"( contract test { - function f() { uint8 x = (0x00ffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; } + function f() public { uint8 x = (0x00ffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; } } )"; bytes code = compileFirstExpression(sourceCode); @@ -569,7 +595,7 @@ BOOST_AUTO_TEST_CASE(blockhash) { char const* sourceCode = R"( contract test { - function f() { + function f() public { blockhash(3); } } @@ -601,7 +627,7 @@ BOOST_AUTO_TEST_CASE(selfbalance) { char const* sourceCode = R"( contract test { - function f() returns (uint) { + function f() public returns (uint) { return address(this).balance; } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 65a8026a48e0..fb520227fa04 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -52,7 +52,7 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) std::vector> nodes = sourceUnit->nodes(); ContractDefinition* contract = dynamic_cast(nodes[1].get()); BOOST_REQUIRE(contract); - BOOST_CHECK(!contract->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!contract->annotation().unimplementedDeclarations->empty()); BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented()); } @@ -68,10 +68,10 @@ BOOST_AUTO_TEST_CASE(abstract_contract) ContractDefinition* base = dynamic_cast(nodes[1].get()); ContractDefinition* derived = dynamic_cast(nodes[2].get()); BOOST_REQUIRE(base); - BOOST_CHECK(!base->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty()); BOOST_CHECK(!base->definedFunctions()[0]->isImplemented()); BOOST_REQUIRE(derived); - BOOST_CHECK(derived->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(derived->annotation().unimplementedDeclarations->empty()); BOOST_CHECK(derived->definedFunctions()[0]->isImplemented()); } @@ -87,9 +87,9 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) ContractDefinition* base = dynamic_cast(nodes[1].get()); ContractDefinition* derived = dynamic_cast(nodes[2].get()); BOOST_REQUIRE(base); - BOOST_CHECK(!base->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!base->annotation().unimplementedDeclarations->empty()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty()); } BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) @@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) BOOST_CHECK_EQUAL(nodes.size(), 3); ContractDefinition* derived = dynamic_cast(nodes[2].get()); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->annotation().unimplementedDeclarations.empty()); + BOOST_CHECK(!derived->annotation().unimplementedDeclarations->empty()); } BOOST_AUTO_TEST_CASE(function_canonical_signature) @@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(enum_external_type) BOOST_AUTO_TEST_CASE(external_struct_signatures) { char const* text = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Simple { uint i; } @@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(external_struct_signatures) BOOST_AUTO_TEST_CASE(external_struct_signatures_in_libraries) { char const* text = R"( - pragma experimental ABIEncoderV2; + pragma abicoder v2; library Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Simple { uint i; } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index fa1e7903d487..0c84517506e8 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -1748,6 +1748,7 @@ BOOST_AUTO_TEST_CASE(user_explicit_inherit_partial2) checkNatspec(sourceCode, "ERC20", natspec, true); checkNatspec(sourceCode, "Token", natspec2, true); } + BOOST_AUTO_TEST_CASE(dev_explicit_inherit_partial) { char const *sourceCode = R"( @@ -2022,6 +2023,230 @@ BOOST_AUTO_TEST_CASE(dev_explicit_inehrit_complex) ); } +BOOST_AUTO_TEST_CASE(dev_different_return_name) +{ + char const *sourceCode = R"( + contract A { + /// @return y value + function g(int x) public pure virtual returns (int y) { return x; } + } + + contract B is A { + function g(int x) public pure override returns (int z) { return x; } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "y": "value" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple) +{ + char const *sourceCode = R"( + contract A { + /// @return a value A + /// @return b value B + function g(int x) public pure virtual returns (int a, int b) { return (1, 2); } + } + + contract B is A { + function g(int x) public pure override returns (int z, int y) { return (1, 2); } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "a": "value A", + "b": "value B" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value A", + "y": "value B" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple_partly_unnamed) +{ + char const *sourceCode = R"( + contract A { + /// @return value A + /// @return b value B + function g(int x) public pure virtual returns (int, int b) { return (1, 2); } + } + + contract B is A { + function g(int x) public pure override returns (int z, int) { return (1, 2); } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "_0": "value A", + "b": "value B" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value A", + "_1": "value B" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_different_return_name_multiple_unnamed) +{ + char const *sourceCode = R"( + contract A { + /// @return value A + /// @return value B + function g(int x) public pure virtual returns (int, int) { return (1, 2); } + } + + contract B is A { + function g(int x) public pure override returns (int z, int y) { return (1, 2); } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "_0": "value A", + "_1": "value B" + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "z": "value A", + "y": "value B" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + +BOOST_AUTO_TEST_CASE(dev_return_name_no_description) +{ + char const *sourceCode = R"( + contract A { + /// @return a + function g(int x) public pure virtual returns (int a) { return 2; } + } + + contract B is A { + function g(int x) public pure override returns (int b) { return 2; } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "a": "a", + } + } + } + })ABCDEF"; + + char const *natspec2 = R"ABCDEF({ + "methods": + { + "g(int256)": + { + "returns": + { + "b": "a", + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "A", natspec, false); + checkNatspec(sourceCode, "B", natspec2, false); +} + } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 24428c5ad97b..b9accf7bb1d9 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -119,8 +119,8 @@ class OptimizerTestFramework: public SolidityExecutionFramework u256 m_gasUsedNonOptimized; bytes m_nonOptimizedBytecode; bytes m_optimizedBytecode; - Address m_optimizedContract; - Address m_nonOptimizedContract; + h160 m_optimizedContract; + h160 m_nonOptimizedContract; }; BOOST_FIXTURE_TEST_SUITE(SolidityOptimizer, OptimizerTestFramework) @@ -224,8 +224,8 @@ BOOST_AUTO_TEST_CASE(function_calls) { char const* sourceCode = R"( contract test { - function f1(uint x) public returns (uint) { return x*x; } - function f(uint x) public returns (uint) { return f1(7+x) - this.f1(x**9); } + function f1(uint x) public returns (uint) { unchecked { return x*x; } } + function f(uint x) public returns (uint) { unchecked { return f1(7+x) - this.f1(x**9); } } } )"; compileBothVersions(sourceCode); @@ -348,7 +348,7 @@ BOOST_AUTO_TEST_CASE(incorrect_storage_access_bug) function f() public returns (uint) { if (data[block.timestamp] == 0) - data[uint(-7)] = 5; + data[type(uint).max - 6] = 5; return data[block.timestamp]; } } @@ -437,7 +437,7 @@ BOOST_AUTO_TEST_CASE(constant_optimization_early_exit) char const* sourceCode = R"( contract HexEncoding { function hexEncodeTest(address addr) public returns (bytes32 ret) { - uint x = uint(addr) / 2**32; + uint x = uint(uint160(addr)) / 2**32; // Nibble interleave x = x & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; @@ -457,7 +457,7 @@ BOOST_AUTO_TEST_CASE(constant_optimization_early_exit) assembly { mstore(0, x) } - x = uint(addr) * 2**96; + x = uint160(addr) * 2**96; // Nibble interleave x = x & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff; @@ -495,7 +495,7 @@ BOOST_AUTO_TEST_CASE(constant_optimization_early_exit) maxDuration = numeric_limits::max(); BOOST_TEST_MESSAGE("Disabled constant optimizer run time check for address sanitizer build."); #endif - BOOST_CHECK_MESSAGE(duration <= maxDuration, "Compilation of constants took longer than 20 seconds."); + BOOST_CHECK_MESSAGE(duration <= double(maxDuration), "Compilation of constants took longer than 20 seconds."); compareVersions("hexEncodeTest(address)", u256(0x123456789)); } @@ -633,8 +633,8 @@ BOOST_AUTO_TEST_CASE(optimise_multi_stores) )"; compileBothVersions(sourceCode); compareVersions("f()"); - BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 9); - BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 8); + BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 8); + BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 7); } BOOST_AUTO_TEST_CASE(optimise_constant_to_codecopy) @@ -679,7 +679,7 @@ BOOST_AUTO_TEST_CASE(byte_access) char const* sourceCode = R"( contract C { - function f(bytes32 x) public returns (byte r) + function f(bytes32 x) public returns (bytes1 r) { assembly { r := and(byte(x, 31), 0xff) } } @@ -696,11 +696,11 @@ BOOST_AUTO_TEST_CASE(shift_optimizer_bug) { function f(uint x) public returns (uint) { - return (x << 1) << uint(-1); + return (x << 1) << type(uint).max; } function g(uint x) public returns (uint) { - return (x >> 1) >> uint(-1); + return (x >> 1) >> type(uint).max; } } )"; diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 6ad68d85bb9a..5443f7b39665 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -120,39 +120,12 @@ BOOST_AUTO_TEST_CASE(reserved_keywords) { BOOST_CHECK(!TokenTraits::isReservedKeyword(Token::Identifier)); BOOST_CHECK(TokenTraits::isReservedKeyword(Token::After)); - BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Unchecked)); + BOOST_CHECK(!TokenTraits::isReservedKeyword(Token::Unchecked)); + BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Var)); + BOOST_CHECK(TokenTraits::isReservedKeyword(Token::Reference)); BOOST_CHECK(!TokenTraits::isReservedKeyword(Token::Illegal)); } -BOOST_AUTO_TEST_CASE(unsatisfied_version) -{ - char const* text = R"( - pragma solidity ^99.99.0; - )"; - CHECK_PARSE_ERROR(text, "Source file requires different compiler version"); -} - -BOOST_AUTO_TEST_CASE(unsatisfied_version_followed_by_invalid_syntax) -{ - char const* text = R"( - pragma solidity ^99.99.0; - this is surely invalid - )"; - CHECK_PARSE_ERROR(text, "Source file requires different compiler version"); -} - -BOOST_AUTO_TEST_CASE(unsatisfied_version_with_recovery) -{ - char const* text = R"( - pragma solidity ^99.99.0; - contract test { - uint ; - } - )"; - Error err = getError(text, true); - BOOST_CHECK(searchErrorMessage(err, "Expected identifier but got ';'")); -} - BOOST_AUTO_TEST_CASE(function_natspec_documentation) { char const* text = R"( @@ -511,22 +484,6 @@ BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) -{ - char const* text = R"( - contract c { - uint private internal a; - } - )"; - CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); - text = R"( - contract c { - function f() private external {} - } - )"; - CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); -} - BOOST_AUTO_TEST_CASE(keyword_is_reserved) { auto keywords = { @@ -534,6 +491,7 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved) "alias", "apply", "auto", + "byte", "case", "copyof", "default", @@ -559,10 +517,10 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved) "switch", "typedef", "typeof", - "unchecked" + "var" }; - BOOST_CHECK_EQUAL(std::size(keywords), static_cast(Token::Unchecked) - static_cast(Token::After) + 1); + BOOST_CHECK_EQUAL(std::size(keywords), static_cast(Token::Var) - static_cast(Token::After) + 1); for (auto const& keyword: keywords) { @@ -615,42 +573,6 @@ BOOST_AUTO_TEST_CASE(complex_import) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(recursion_depth1) -{ - string text("contract C { bytes"); - for (size_t i = 0; i < 30000; i++) - text += "["; - CHECK_PARSE_ERROR(text.c_str(), "Maximum recursion depth reached during parsing"); -} - -BOOST_AUTO_TEST_CASE(recursion_depth2) -{ - string text("contract C { function f() {"); - for (size_t i = 0; i < 30000; i++) - text += "{"; - CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); -} - -BOOST_AUTO_TEST_CASE(recursion_depth3) -{ - string text("contract C { function f() { uint x = f("); - for (size_t i = 0; i < 30000; i++) - text += "("; - CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); -} - -BOOST_AUTO_TEST_CASE(recursion_depth4) -{ - string text("contract C { function f() { uint a;"); - for (size_t i = 0; i < 30000; i++) - text += "("; - text += "a"; - for (size_t i = 0; i < 30000; i++) - text += "++)"; - text += "}}"; - CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); -} - BOOST_AUTO_TEST_CASE(inline_asm_end_location) { auto sourceCode = std::string(R"( diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index b837b3878af8..95288d85ca77 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -51,7 +51,6 @@ BOOST_AUTO_TEST_CASE(uint_types) BOOST_AUTO_TEST_CASE(byte_types) { - BOOST_CHECK(*TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken(Token::Byte, 0, 0)) == *TypeProvider::fixedBytes(1)); for (unsigned i = 1; i <= 32; i++) BOOST_CHECK(*TypeProvider::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, i, 0)) == *TypeProvider::fixedBytes(i)); } @@ -162,7 +161,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers) StringLiteralType(Literal(++id, SourceLocation{}, Token::StringLiteral, make_shared("abc - def"))).identifier(), "t_stringliteral_196a9142ee0d40e274a6482393c762b16dd8315713207365e1e13d8d85b74fc4" ); - BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("byte")->identifier(), "t_bytes1"); + BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("bytes1")->identifier(), "t_bytes1"); BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("bytes8")->identifier(), "t_bytes8"); BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("bytes32")->identifier(), "t_bytes32"); BOOST_CHECK_EQUAL(TypeProvider::fromElementaryTypeName("bool")->identifier(), "t_bool"); @@ -262,13 +261,6 @@ BOOST_AUTO_TEST_CASE(helper_bool_result) r5.merge(r6, logical_and()); BOOST_REQUIRE_EQUAL(r5.get(), true); BOOST_REQUIRE_EQUAL(r5.message(), ""); - - BoolResult r7{true}; - // Attention: this will implicitly convert to bool. - BoolResult r8{"true"}; - r7.merge(r8, logical_and()); - BOOST_REQUIRE_EQUAL(r7.get(), true); - BOOST_REQUIRE_EQUAL(r7.message(), ""); } BOOST_AUTO_TEST_CASE(helper_string_result) diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 570350466356..9ed17c0af2c5 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -29,6 +29,7 @@ #include #include +#include #include using namespace std; @@ -85,6 +86,48 @@ Json::Value getContractResult(Json::Value const& _compilerResult, string const& return _compilerResult["contracts"][_file][_name]; } +void checkLinkReferencesSchema(Json::Value const& _contractResult) +{ + BOOST_TEST_REQUIRE(_contractResult.isObject()); + BOOST_TEST_REQUIRE(_contractResult["evm"]["bytecode"].isObject()); + + Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"]; + BOOST_TEST_REQUIRE(linkReferenceResult.isObject()); + + for (string const& fileName: linkReferenceResult.getMemberNames()) + { + BOOST_TEST_REQUIRE(linkReferenceResult[fileName].isObject()); + for (string const& libraryName: linkReferenceResult[fileName].getMemberNames()) + { + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName].isArray()); + BOOST_TEST_REQUIRE(!linkReferenceResult[fileName][libraryName].empty()); + for (int i = 0; i < static_cast(linkReferenceResult.size()); ++i) + { + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].isObject()); + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i].size() == 2); + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["length"].isUInt()); + BOOST_TEST_REQUIRE(linkReferenceResult[fileName][libraryName][i]["start"].isUInt()); + } + } + } +} + +void expectLinkReferences(Json::Value const& _contractResult, map> const& _expectedLinkReferences) +{ + checkLinkReferencesSchema(_contractResult); + + Json::Value const& linkReferenceResult = _contractResult["evm"]["bytecode"]["linkReferences"]; + BOOST_TEST(linkReferenceResult.size() == _expectedLinkReferences.size()); + + for (auto const& [fileName, libraries]: _expectedLinkReferences) + { + BOOST_TEST(linkReferenceResult.isMember(fileName)); + BOOST_TEST(linkReferenceResult[fileName].size() == libraries.size()); + for (string const& libraryName: libraries) + BOOST_TEST(linkReferenceResult[fileName].isMember(libraryName)); + } +} + Json::Value compile(string _input) { StandardCompiler compiler; @@ -342,7 +385,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) "outputSelection": { "fileA": { "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], - "": [ "legacyAST" ] + "": [ "ast" ] } } } @@ -425,13 +468,13 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(solidity::test::isValidMetadata(contract["metadata"].asString())); BOOST_CHECK(result["sources"].isObject()); BOOST_CHECK(result["sources"]["fileA"].isObject()); - BOOST_CHECK(result["sources"]["fileA"]["legacyAST"].isObject()); + BOOST_CHECK(result["sources"]["fileA"]["ast"].isObject()); BOOST_CHECK_EQUAL( - util::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]), - "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]},\"license\":null},\"children\":" - "[{\"attributes\":{\"abstract\":false,\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\"," - "\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," - "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" + util::jsonCompactPrint(result["sources"]["fileA"]["ast"]), + "{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]},\"id\":2,\"nodeType\":\"SourceUnit\",\"nodes\":[{\"abstract\":false," + "\"baseContracts\":[],\"contractDependencies\":[],\"contractKind\":\"contract\",\"fullyImplemented\":true,\"id\":1," + "\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodeType\":\"ContractDefinition\",\"nodes\":[],\"scope\":2," + "\"src\":\"0:14:0\"}],\"src\":\"0:14:0\"}" ); } @@ -467,8 +510,8 @@ BOOST_AUTO_TEST_CASE(compilation_error) { BOOST_CHECK_EQUAL( util::jsonCompactPrint(error), - "{\"component\":\"general\",\"errorCode\":\"2314\",\"formattedMessage\":\"fileA:1:23: ParserError: Expected identifier but got '}'\\n" - "contract A { function }\\n ^\\n\",\"message\":\"Expected identifier but got '}'\"," + "{\"component\":\"general\",\"errorCode\":\"2314\",\"formattedMessage\":\"ParserError: Expected identifier but got '}'\\n" + " --> fileA:1:23:\\n |\\n1 | contract A { function }\\n | ^\\n\\n\",\"message\":\"Expected identifier but got '}'\"," "\"severity\":\"error\",\"sourceLocation\":{\"end\":23,\"file\":\"fileA\",\"start\":22},\"type\":\"ParserError\"}" ); } @@ -709,11 +752,7 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon) BOOST_CHECK(containsAtMostWarnings(result)); Json::Value contract = getContractResult(result, "fileA", "A"); BOOST_CHECK(contract.isObject()); - BOOST_CHECK(contract["evm"]["bytecode"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"].isArray()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); + expectLinkReferences(contract, {{"git:library.sol", {"L"}}}); } BOOST_AUTO_TEST_CASE(libraries_invalid_top_level) @@ -859,15 +898,137 @@ BOOST_AUTO_TEST_CASE(library_linking) } )"; Json::Value result = compile(input); - BOOST_CHECK(containsAtMostWarnings(result)); - Json::Value contract = getContractResult(result, "fileA", "A"); - BOOST_CHECK(contract.isObject()); - BOOST_CHECK(contract["evm"]["bytecode"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); - BOOST_CHECK(!contract["evm"]["bytecode"]["linkReferences"]["library.sol"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray()); - BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "A"); + expectLinkReferences(contractResult, {{"library2.sol", {"L2"}}}); +} + +BOOST_AUTO_TEST_CASE(linking_yul) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "fileB": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"fileB:L\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {}); +} + +BOOST_AUTO_TEST_CASE(linking_yul_empty_link_reference) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "": { + "": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {{"", {""}}}); +} + +BOOST_AUTO_TEST_CASE(linking_yul_no_filename_in_link_reference) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"L\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {{"", {"L"}}}); +} + +BOOST_AUTO_TEST_CASE(linking_yul_same_library_name_different_files) +{ + char const* input = R"( + { + "language": "Yul", + "settings": { + "libraries": { + "fileB": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "*": [ + "evm.bytecode.linkReferences" + ] + } + } + }, + "sources": { + "fileA": { + "content": "object \"a\" { code { let addr := linkersymbol(\"fileC:L\") } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_TEST(containsAtMostWarnings(result)); + Json::Value contractResult = getContractResult(result, "fileA", "a"); + expectLinkReferences(contractResult, {{"fileC", {"L"}}}); } BOOST_AUTO_TEST_CASE(evm_version) @@ -1235,9 +1396,16 @@ BOOST_AUTO_TEST_CASE(use_stack_optimization) BOOST_REQUIRE(contract["evm"]["bytecode"]["object"].isString()); BOOST_CHECK(contract["evm"]["bytecode"]["object"].asString().length() > 20); - // Now disable stack optimizations + // Now disable stack optimizations and UnusedFunctionParameterPruner (p) // results in "stack too deep" + string optimiserSteps = OptimiserSettings::DefaultYulOptimiserSteps; + optimiserSteps.erase( + remove_if(optimiserSteps.begin(), optimiserSteps.end(), [](char ch) { return ch == 'p'; }), + optimiserSteps.end() + ); parsedInput["settings"]["optimizer"]["details"]["yulDetails"]["stackAllocation"] = false; + parsedInput["settings"]["optimizer"]["details"]["yulDetails"]["optimizerSteps"] = optimiserSteps; + result = compiler.compile(parsedInput); BOOST_REQUIRE(result["errors"].isArray()); BOOST_CHECK(result["errors"][0]["severity"] == "error"); @@ -1412,6 +1580,181 @@ BOOST_AUTO_TEST_CASE(standard_output_selection_wildcard_multiple_sources) BOOST_REQUIRE(result["sources"]["B"].isObject()); } +BOOST_AUTO_TEST_CASE(stopAfter_invalid_value) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": + { "": { "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" } }, + "settings": + { + "stopAfter": "rrr", + "outputSelection": + { + "*": { "C": ["evm.bytecode"] } + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Invalid value for \"settings.stopAfter\". Only valid value is \"parsing\".")); +} + +BOOST_AUTO_TEST_CASE(stopAfter_invalid_type) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": + { "": { "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" } }, + "settings": + { + "stopAfter": 3, + "outputSelection": + { + "*": { "C": ["evm.bytecode"] } + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"settings.stopAfter\" must be a string.")); +} + +BOOST_AUTO_TEST_CASE(stopAfter_bin_conflict) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": + { "": { "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" } }, + "settings": + { + "stopAfter": "parsing", + "outputSelection": + { + "*": { "C": ["evm.bytecode"] } + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Requested output selection conflicts with \"settings.stopAfter\".")); +} + +BOOST_AUTO_TEST_CASE(stopAfter_ast_output) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": { + "a.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0\nimport \"tes32.sol\";\n contract C is X { constructor() {} }" + } + }, + "settings": { + "stopAfter": "parsing", + "outputSelection": { "*": { "": [ "ast" ] } } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(result["sources"].isObject()); + BOOST_CHECK(result["sources"]["a.sol"].isObject()); + BOOST_CHECK(result["sources"]["a.sol"]["ast"].isObject()); +} + +BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": { + "BlockRewardAuRaBase.sol": { + "content": " contract Sacrifice { constructor() payable {} } abstract contract BlockRewardAuRaBase { function _transferNativeReward() internal { new Sacrifice(); } function _distributeTokenRewards() internal virtual; } " + }, + "BlockRewardAuRaCoins.sol": { + "content": " import \"./BlockRewardAuRaBase.sol\"; contract BlockRewardAuRaCoins is BlockRewardAuRaBase { function transferReward() public { _transferNativeReward(); } function _distributeTokenRewards() internal override {} } " + } + }, + "settings": { + "outputSelection": { + "BlockRewardAuRaCoins.sol": { + "BlockRewardAuRaCoins": ["ir", "evm.bytecode.sourceMap"] + } + } + } + } + )"; + + Json::Value parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + + solidity::frontend::StandardCompiler compiler; + Json::Value result = compiler.compile(parsedInput); + + BOOST_REQUIRE(result["contracts"].isObject()); + BOOST_REQUIRE(result["contracts"].size() == 1); + BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].isObject()); + BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"].size() == 1); + BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"].isObject()); + BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"].isObject()); + BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["ir"].isString()); + BOOST_REQUIRE(result["contracts"]["BlockRewardAuRaCoins.sol"]["BlockRewardAuRaCoins"]["evm"]["bytecode"].isObject()); + BOOST_REQUIRE(result["sources"].isObject()); + BOOST_REQUIRE(result["sources"].size() == 2); +} + +BOOST_AUTO_TEST_CASE(dependency_tracking_of_abstract_contract_yul) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A {} contract B {} contract C { constructor() { new B(); } } contract D {}" + } + }, + "settings": { + "outputSelection": { + "A.sol": { + "C": ["ir"] + } + } + } + } + )"; + + Json::Value parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + + solidity::frontend::StandardCompiler compiler; + Json::Value result = compiler.compile(parsedInput); + + BOOST_REQUIRE(result["contracts"].isObject()); + BOOST_REQUIRE(result["contracts"].size() == 1); + BOOST_REQUIRE(result["contracts"]["A.sol"].isObject()); + BOOST_REQUIRE(result["contracts"]["A.sol"].size() == 1); + BOOST_REQUIRE(result["contracts"]["A.sol"]["C"].isObject()); + BOOST_REQUIRE(result["contracts"]["A.sol"]["C"]["ir"].isString()); + + const string& irCode = result["contracts"]["A.sol"]["C"]["ir"].asString(); + + // Make sure C and B contracts are deployed + BOOST_REQUIRE(irCode.find("object \"C") != string::npos); + BOOST_REQUIRE(irCode.find("object \"B") != string::npos); + + // Make sure A and D are NOT deployed as they were not requested and are not + // in any dependency + BOOST_REQUIRE(irCode.find("object \"A") == string::npos); + BOOST_REQUIRE(irCode.find("object \"D") == string::npos); + + + BOOST_REQUIRE(result["sources"].isObject()); + BOOST_REQUIRE(result["sources"].size() == 1); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index b71691bea121..0df95f7a604c 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -52,7 +52,7 @@ class SyntaxTest: public AnalysisFramework, public solidity::test::CommonSyntaxT protected: void setupCompiler(); void parseAndAnalyze() override; - void filterObtainedErrors(); + virtual void filterObtainedErrors(); bool m_optimiseYul = true; bool m_parserErrorRecovery = false; diff --git a/test/libsolidity/constructor_inheritance_init_order_3.sol b/test/libsolidity/constructor_inheritance_init_order_3.sol new file mode 100644 index 000000000000..c9719a108bee --- /dev/null +++ b/test/libsolidity/constructor_inheritance_init_order_3.sol @@ -0,0 +1,12 @@ +contract A { + uint public x; + constructor(uint) {} + function f() public { x = 4; } +} +contract B is A { + constructor() A(f()) {} +} +// ==== +// compileViaYul: also +// ---- +// x() -> 4 diff --git a/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol b/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol new file mode 100644 index 000000000000..67c4cf7b4c00 --- /dev/null +++ b/test/libsolidity/errorRecoveryTests/recovery_failed_eos.sol @@ -0,0 +1,10 @@ +pragma solidity >=0.0.0; + +contract Error7 { + constructor() { + a = +// ---- +// ParserError 6933: (76-76): Expected primary expression. +// ParserError 1957: (76-76): In Statement, ';'is expected; got end of source instead. +// ParserError 1957: (76-76): In Block, '}'is expected; got end of source instead. +// ParserError 1957: (76-76): In ContractDefinition, '}'is expected; got end of source instead. diff --git a/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_1.sol b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_1.sol new file mode 100644 index 000000000000..9baa5638ecd1 --- /dev/null +++ b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_1.sol @@ -0,0 +1,3 @@ +pragma solidity ^99.99.0; +// ---- +// SyntaxError 3997: (0-25): Source file requires different compiler version (current compiler is .... diff --git a/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_2.sol b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_2.sol new file mode 100644 index 000000000000..1dcc4bc79ae2 --- /dev/null +++ b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_2.sol @@ -0,0 +1,6 @@ +pragma solidity ^99.99.0; +this is surely invalid +// ---- +// ParserError 6635: (31-33): Expected identifier but got 'is' +// ParserError 6635: (34-40): Expected ';' but got identifier +// ParserError 6635: (49-49): Expected ';' but got end of source diff --git a/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_3.sol b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_3.sol new file mode 100644 index 000000000000..40fae8116270 --- /dev/null +++ b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_3.sol @@ -0,0 +1,7 @@ +pragma solidity ^99.99.0; +contract C { + uint ; +} +// ---- +// ParserError 6635: (48-49): Expected identifier but got ';' +// ParserError 6635: (50-51): Expected ';' but got '}' diff --git a/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_4.sol b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_4.sol new file mode 100644 index 000000000000..fadf021c7bde --- /dev/null +++ b/test/libsolidity/errorRecoveryTests/wrong_compiler_recovers_4.sol @@ -0,0 +1,7 @@ +pragma solidity ^99.99.0; +contract C { + function f() {} +} +// ---- +// SyntaxError 3997: (0-25): Source file requires different compiler version (current compiler is .... +// SyntaxError 4937: (43-58): No visibility specified. Did you intend to add "public"? diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index 1fd96ff7603a..387278e8f6a8 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { uint public a; @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1094400 -// executionCost: 1134 -// totalCost: 1095534 +// codeDepositCost: 1173600 +// executionCost: 1221 +// totalCost: 1174821 // external: // a(): 1130 // b(uint256): infinite diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index b1bc1ff5d328..0eacba1b4172 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { uint public a; @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 603000 -// executionCost: 638 -// totalCost: 603638 +// codeDepositCost: 587400 +// executionCost: 619 +// totalCost: 588019 // external: // a(): 1029 // b(uint256): 2084 diff --git a/test/libsolidity/gasTests/data_storage.sol b/test/libsolidity/gasTests/data_storage.sol index 29cade28c8fa..de74c40376d4 100644 --- a/test/libsolidity/gasTests/data_storage.sol +++ b/test/libsolidity/gasTests/data_storage.sol @@ -13,8 +13,8 @@ contract C { } // ---- // creation: -// codeDepositCost: 257000 -// executionCost: 300 -// totalCost: 257300 +// codeDepositCost: 376800 +// executionCost: 411 +// totalCost: 377211 // external: -// f(): 252 +// f(): 399 diff --git a/test/libsolidity/gasTests/dispatch_large.sol b/test/libsolidity/gasTests/dispatch_large.sol index 3fcd62eab206..8413957cd7a4 100644 --- a/test/libsolidity/gasTests/dispatch_large.sol +++ b/test/libsolidity/gasTests/dispatch_large.sol @@ -24,29 +24,29 @@ contract Large { } // ---- // creation: -// codeDepositCost: 637000 -// executionCost: 670 -// totalCost: 637670 +// codeDepositCost: 913400 +// executionCost: 948 +// totalCost: 914348 // external: -// a(): 1051 -// b(uint256): 2046 -// f0(uint256): 427 -// f1(uint256): 41352 -// f2(uint256): 21293 -// f3(uint256): 21381 -// f4(uint256): 21359 -// f5(uint256): 21337 -// f6(uint256): 21360 -// f7(uint256): 21272 -// f8(uint256): 21272 -// f9(uint256): 21294 -// g0(uint256): 313 -// g1(uint256): 41307 -// g2(uint256): 21270 -// g3(uint256): 21358 -// g4(uint256): 21336 -// g5(uint256): 21292 -// g6(uint256): 21315 -// g7(uint256): 21314 -// g8(uint256): 21292 -// g9(uint256): 21249 +// a(): 1175 +// b(uint256): infinite +// f0(uint256): infinite +// f1(uint256): infinite +// f2(uint256): infinite +// f3(uint256): infinite +// f4(uint256): infinite +// f5(uint256): infinite +// f6(uint256): infinite +// f7(uint256): infinite +// f8(uint256): infinite +// f9(uint256): infinite +// g0(uint256): infinite +// g1(uint256): infinite +// g2(uint256): infinite +// g3(uint256): infinite +// g4(uint256): infinite +// g5(uint256): infinite +// g6(uint256): infinite +// g7(uint256): infinite +// g8(uint256): infinite +// g9(uint256): infinite diff --git a/test/libsolidity/gasTests/dispatch_large_optimised.sol b/test/libsolidity/gasTests/dispatch_large_optimised.sol index 49073a37ba24..4108ca121f87 100644 --- a/test/libsolidity/gasTests/dispatch_large_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_large_optimised.sol @@ -27,29 +27,29 @@ contract Large { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 260600 -// executionCost: 300 -// totalCost: 260900 +// codeDepositCost: 270600 +// executionCost: 312 +// totalCost: 270912 // external: -// a(): 998 -// b(uint256): 2305 -// f0(uint256): 334 -// f1(uint256): 41474 -// f2(uint256): 21540 -// f3(uint256): 21628 -// f4(uint256): 21606 -// f5(uint256): 21584 -// f6(uint256): 21496 -// f7(uint256): 21276 -// f8(uint256): 21408 -// f9(uint256): 21430 -// g0(uint256): 574 -// g1(uint256): 41186 -// g2(uint256): 21274 -// g3(uint256): 21362 -// g4(uint256): 21340 -// g5(uint256): 21428 -// g6(uint256): 21208 -// g7(uint256): 21318 -// g8(uint256): 21296 -// g9(uint256): 21142 +// a(): 1028 +// b(uint256): 2370 +// f0(uint256): 399 +// f1(uint256): 41539 +// f2(uint256): 21605 +// f3(uint256): 21693 +// f4(uint256): 21671 +// f5(uint256): 21649 +// f6(uint256): 21561 +// f7(uint256): 21341 +// f8(uint256): 21473 +// f9(uint256): 21495 +// g0(uint256): 639 +// g1(uint256): 41251 +// g2(uint256): 21339 +// g3(uint256): 21427 +// g4(uint256): 21405 +// g5(uint256): 21493 +// g6(uint256): 21273 +// g7(uint256): 21383 +// g8(uint256): 21361 +// g9(uint256): 21207 diff --git a/test/libsolidity/gasTests/dispatch_medium.sol b/test/libsolidity/gasTests/dispatch_medium.sol index 5d1fb1b87215..b9076c9cdff4 100644 --- a/test/libsolidity/gasTests/dispatch_medium.sol +++ b/test/libsolidity/gasTests/dispatch_medium.sol @@ -11,16 +11,16 @@ contract Medium { } // ---- // creation: -// codeDepositCost: 253200 -// executionCost: 294 -// totalCost: 253494 +// codeDepositCost: 360400 +// executionCost: 399 +// totalCost: 360799 // external: -// a(): 1028 -// b(uint256): 2046 -// f1(uint256): 41263 -// f2(uint256): 21293 -// f3(uint256): 21337 -// g0(uint256): 313 -// g7(uint256): 21292 -// g8(uint256): 21270 -// g9(uint256): 21226 +// a(): 1152 +// b(uint256): infinite +// f1(uint256): infinite +// f2(uint256): infinite +// f3(uint256): infinite +// g0(uint256): infinite +// g7(uint256): infinite +// g8(uint256): infinite +// g9(uint256): infinite diff --git a/test/libsolidity/gasTests/dispatch_medium_optimised.sol b/test/libsolidity/gasTests/dispatch_medium_optimised.sol index 7fe4f8897da1..2bc22cc582ed 100644 --- a/test/libsolidity/gasTests/dispatch_medium_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_medium_optimised.sol @@ -14,16 +14,16 @@ contract Medium { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 141000 -// executionCost: 190 -// totalCost: 141190 +// codeDepositCost: 161000 +// executionCost: 208 +// totalCost: 161208 // external: -// a(): 998 -// b(uint256): 2063 -// f1(uint256): 41254 -// f2(uint256): 21298 -// f3(uint256): 21342 -// g0(uint256): 332 -// g7(uint256): 21208 -// g8(uint256): 21186 -// g9(uint256): 21142 +// a(): 1028 +// b(uint256): 2128 +// f1(uint256): 41319 +// f2(uint256): 21363 +// f3(uint256): 21407 +// g0(uint256): 397 +// g7(uint256): 21273 +// g8(uint256): 21251 +// g9(uint256): 21207 diff --git a/test/libsolidity/gasTests/dispatch_small.sol b/test/libsolidity/gasTests/dispatch_small.sol index 52581c5fac29..6a062e530501 100644 --- a/test/libsolidity/gasTests/dispatch_small.sol +++ b/test/libsolidity/gasTests/dispatch_small.sol @@ -6,11 +6,11 @@ contract Small { } // ---- // creation: -// codeDepositCost: 84800 -// executionCost: 135 -// totalCost: 84935 +// codeDepositCost: 123600 +// executionCost: 171 +// totalCost: 123771 // external: // fallback: 129 -// a(): 983 -// b(uint256): 2002 -// f1(uint256): 41263 +// a(): 1107 +// b(uint256): infinite +// f1(uint256): infinite diff --git a/test/libsolidity/gasTests/dispatch_small_optimised.sol b/test/libsolidity/gasTests/dispatch_small_optimised.sol index cd967ea19daf..3f595229a7bf 100644 --- a/test/libsolidity/gasTests/dispatch_small_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_small_optimised.sol @@ -9,11 +9,11 @@ contract Small { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 60600 -// executionCost: 111 -// totalCost: 60711 +// codeDepositCost: 76200 +// executionCost: 123 +// totalCost: 76323 // external: // fallback: 118 -// a(): 976 -// b(uint256): 1953 -// f1(uint256): 41188 +// a(): 1006 +// b(uint256): 2018 +// f1(uint256): 41253 diff --git a/test/libsolidity/gasTests/exp.sol b/test/libsolidity/gasTests/exp.sol new file mode 100644 index 000000000000..4d2e79634684 --- /dev/null +++ b/test/libsolidity/gasTests/exp.sol @@ -0,0 +1,29 @@ +pragma abicoder v2; + +contract C { + function exp_neg_one(uint exponent) public returns(int) { + unchecked { return (-1)**exponent; } + } + function exp_two(uint exponent) public returns(uint) { + unchecked { return 2**exponent; } + } + function exp_zero(uint exponent) public returns(uint) { + unchecked { return 0**exponent; } + } + function exp_one(uint exponent) public returns(uint) { + unchecked { return 1**exponent; } + } +} +// ==== +// optimize: false +// optimize-yul: false +// ---- +// creation: +// codeDepositCost: 119800 +// executionCost: 165 +// totalCost: 119965 +// external: +// exp_neg_one(uint256): 2259 +// exp_one(uint256): infinite +// exp_two(uint256): infinite +// exp_zero(uint256): infinite diff --git a/test/libsolidity/gasTests/exp_optimized.sol b/test/libsolidity/gasTests/exp_optimized.sol new file mode 100644 index 000000000000..d2c02378d3ba --- /dev/null +++ b/test/libsolidity/gasTests/exp_optimized.sol @@ -0,0 +1,29 @@ +pragma abicoder v2; + +contract C { + function exp_neg_one(uint exponent) public returns(int) { + unchecked { return (-1)**exponent; } + } + function exp_two(uint exponent) public returns(uint) { + unchecked { return 2**exponent; } + } + function exp_zero(uint exponent) public returns(uint) { + unchecked { return 0**exponent; } + } + function exp_one(uint exponent) public returns(uint) { + unchecked { return 1**exponent; } + } +} +// ==== +// optimize: true +// optimize-yul: true +// ---- +// creation: +// codeDepositCost: 53200 +// executionCost: 105 +// totalCost: 53305 +// external: +// exp_neg_one(uint256): 1962 +// exp_one(uint256): 1915 +// exp_two(uint256): 1893 +// exp_zero(uint256): 1937 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_fixed_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_fixed_arrays.sol new file mode 100644 index 000000000000..b421f7bd0139 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_fixed_arrays.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint16[3] memory a, uint16[2][3] memory b, uint i, uint j, uint k) + public pure returns (uint, uint) { + return (a[i], b[j][k]); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint16[3],uint16[2][3],uint256,uint256,uint256): 1, 2, 3, 11, 12, 21, 22, 31, 32, 1, 2, 1 -> 2, 32 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol index 27c022cfd329..08467feb8609 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_static_array_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol index 6d7b27f58efb..6f1c652a004a 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol index 9ed9ada784e9..bcb14d6845ef 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_calldata.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol index 95b667e474cc..cdedf048e088 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -20,5 +20,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol index 61c6658ac9b2..3db8b0faa361 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode.sol @@ -30,6 +30,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f0() -> 0x20, 0x0 // f1() -> 0x20, 0x40, 0x1, 0x2 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol index b0205606f710..a79dc80c5389 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_decode_simple.sol @@ -7,5 +7,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_empty_string.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_empty_string.sol new file mode 100644 index 000000000000..a4202fab3c78 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_empty_string.sol @@ -0,0 +1,34 @@ +contract C { + function f1() public returns (bytes memory) { + return abi.encode(""); + } + function f2(string calldata msg) public returns (bytes memory) { + return abi.encode(msg); + } + function g1() public returns (bytes memory) { + return abi.encodePacked(""); + } + function g2(string calldata msg) public returns (bytes memory) { + return abi.encodePacked(msg); + } + function h1() public returns (bytes memory) { + return abi.encodeWithSelector(0x00000001, ""); + } + function h2(string calldata msg) public returns (bytes memory) { + return abi.encodeWithSelector(0x00000001, msg); + } +} + +// ==== +// ABIEncoderV1Only: true +// compileViaYul: false +// ---- +// f1() -> 0x20, 0x40, 0x20, 0 +// f2(string): 0x20, 0 -> 0x20, 0x40, 0x20, 0 +// f2(string): 0x20, 0, 0 -> 0x20, 0x40, 0x20, 0 +// g1() -> 32, 0 +// g2(string): 0x20, 0 -> 0x20, 0 +// g2(string): 0x20, 0, 0 -> 0x20, 0 +// h1() -> 0x20, 0x44, 26959946667150639794667015087019630673637144422540572481103610249216, 862718293348820473429344482784628181556388621521298319395315527974912, 0 +// h2(string): 0x20, 0 -> 0x20, 0x44, 26959946667150639794667015087019630673637144422540572481103610249216, 862718293348820473429344482784628181556388621521298319395315527974912, 0 +// h2(string): 0x20, 0, 0 -> 0x20, 0x44, 26959946667150639794667015087019630673637144422540572481103610249216, 862718293348820473429344482784628181556388621521298319395315527974912, 0 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol index 133645cff443..659c3c1928be 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_rational.sol @@ -7,5 +7,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/bool_out_of_bounds.sol b/test/libsolidity/semanticTests/abiEncoderV1/bool_out_of_bounds.sol new file mode 100644 index 000000000000..575d8012cecb --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/bool_out_of_bounds.sol @@ -0,0 +1,10 @@ +contract C { + function f(bool b) public pure returns (bool) { return b; } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(bool): true -> true +// f(bool): false -> false +// f(bool): 0x000000 -> false +// f(bool): 0xffffff -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/abiEncoderV1/byte_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV1/byte_arrays.sol new file mode 100644 index 000000000000..ce2dbf86109c --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/byte_arrays.sol @@ -0,0 +1,16 @@ +contract C { + function f(uint a, bytes memory b, uint c) + public pure returns (uint, uint, bytes1, uint) { + return (a, b.length, b[3], c); + } + + function f_external(uint a, bytes calldata b, uint c) + external pure returns (uint, uint, bytes1, uint) { + return (a, b.length, b[3], c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9 +// f_external(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/abiEncoderV1/calldata_arrays_too_large.sol b/test/libsolidity/semanticTests/abiEncoderV1/calldata_arrays_too_large.sol new file mode 100644 index 000000000000..190bf9dc6f3b --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/calldata_arrays_too_large.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint a, uint[] calldata b, uint c) external pure returns (uint) { + return 7; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint256,uint256[],uint256): 6, 0x60, 9, 0x8000000000000000000000000000000000000000000000000000000000000002, 1, 2 -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV1/cleanup/cleanup.sol b/test/libsolidity/semanticTests/abiEncoderV1/cleanup/cleanup.sol new file mode 100644 index 000000000000..a97ad107d95b --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/cleanup/cleanup.sol @@ -0,0 +1,16 @@ +contract C { + function f(uint16 a, int16 b, address c, bytes3 d, bool e) + public pure returns (uint v, uint w, uint x, uint y, uint z) { + assembly { v := a w := b x := c y := d z := e} + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(uint16,int16,address,bytes3,bool): 1, 2, 3, "a", true -> 1, 2, 3, "a", true +// f(uint16,int16,address,bytes3,bool): 0xffffff, 0x1ffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "abcd", 1 -> 0xffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffffffffffff, "abc", true +// f(uint16,int16,address,bytes3,bool): 0xffffff, 0, 0, "bcd", 1 -> 0xffff, 0, 0, "bcd", true +// f(uint16,int16,address,bytes3,bool): 0, 0x1ffff, 0, "ab", 1 -> 0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0, "ab", true +// f(uint16,int16,address,bytes3,bool): 0, 0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "ad", 1 -> 0, 0, 0xffffffffffffffffffffffffffffffffffffffff, "ad", true +// f(uint16,int16,address,bytes3,bool): 0, 0, 0, "abcd", 1 -> 0, 0, 0, "abc", true +// f(uint16,int16,address,bytes3,bool): 0, 0, 0, "abc", 2 -> 0, 0, 0, "abc", true diff --git a/test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol b/test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol index 33e32e065eab..fb482e0edcca 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/decode_slice.sol @@ -8,5 +8,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint256): 42, 23 -> 42, 23, 42, 23 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/dynamic_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV1/dynamic_arrays.sol new file mode 100644 index 000000000000..8813be109bc5 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/dynamic_arrays.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint a, uint16[] memory b, uint c) + public pure returns (uint, uint, uint) { + return (b.length, b[a], c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint16[],uint256): 6, 0x60, 9, 7, 11, 12, 13, 14, 15, 16, 17 -> 7, 17, 9 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/enums.sol b/test/libsolidity/semanticTests/abiEncoderV1/enums.sol new file mode 100644 index 000000000000..ad69e268f1ba --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/enums.sol @@ -0,0 +1,13 @@ +contract C { + enum E { A, B } + function f(E e) public pure returns (uint x) { + assembly { x := e } + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(uint8): 0 -> 0 +// f(uint8): 1 -> 1 +// f(uint8): 2 -> 2 +// f(uint8): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xff diff --git a/test/libsolidity/semanticTests/abiEncoderV1/memory_params_in_external_function.sol b/test/libsolidity/semanticTests/abiEncoderV1/memory_params_in_external_function.sol index b0ba6dfaf0ff..b86129d77334 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/memory_params_in_external_function.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/memory_params_in_external_function.sol @@ -2,11 +2,11 @@ contract C { function f(bytes memory a, bytes calldata b, uint[] memory c) external pure - returns (uint, byte, uint, byte, uint, uint) + returns (uint, bytes1, uint, bytes1, uint, uint) { return (a.length, a[1], b.length, b[2], c.length, c[3]); } - function g() public returns (uint, byte, uint, byte, uint, uint) { + function g() public returns (uint, bytes1, uint, bytes1, uint, uint) { uint[] memory x = new uint[](4); x[3] = 7; return this.f("abc", "def", x); diff --git a/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_advanced.sol b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_advanced.sol new file mode 100644 index 000000000000..e7255435687c --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_advanced.sol @@ -0,0 +1,18 @@ +contract C { + function dyn() public returns (bytes memory a, uint b, bytes20[] memory c, uint d) { + a = "1234567890123456789012345678901234567890"; + b = type(uint).max; + c = new bytes20[](4); + c[0] = bytes20(uint160(1234)); + c[3] = bytes20(uint160(6789)); + d = 0x1234; + } + function f() public returns (bytes memory, uint, bytes20[] memory, uint) { + return this.dyn(); + } +} +// ==== +// compileViaYul: also +// EVMVersion: >homestead +// ---- +// f() -> 0x80, -1, 0xe0, 0x1234, 40, "12345678901234567890123456789012", "34567890", 4, 97767552542602192590433234714624, 0, 0, 537879995309340587922569878831104 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_out_of_range_1.sol b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_out_of_range_1.sol new file mode 100644 index 000000000000..c90180f49405 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_out_of_range_1.sol @@ -0,0 +1,20 @@ +contract C { + function dyn(uint x) public returns (bytes memory a) { + assembly { + mstore(0, 0x20) + mstore(0x20, 0x21) + return(0, x) + } + } + function f(uint x) public returns (bool) { + this.dyn(x); + return true; + } +} +// ==== +// compileViaYul: also +// EVMVersion: =homestead +// ---- +// f(uint256): 0x60 -> true +// f(uint256): 0x7f -> true +// f(uint256): 0x80 -> true \ No newline at end of file diff --git a/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_out_of_range_2.sol b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_out_of_range_2.sol new file mode 100644 index 000000000000..b5bca26d3c4c --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_out_of_range_2.sol @@ -0,0 +1,20 @@ +contract C { + function dyn(uint x) public returns (bytes memory a) { + assembly { + mstore(0, 0x20) + mstore(0x20, 0x21) + return(0, x) + } + } + function f(uint x) public returns (bool) { + this.dyn(x); + return true; + } +} +// ==== +// compileViaYul: also +// EVMVersion: >homestead +// ---- +// f(uint256): 0x60 -> FAILURE +// f(uint256): 0x61 -> true +// f(uint256): 0x80 -> true diff --git a/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_simple.sol b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_simple.sol new file mode 100644 index 000000000000..360eb7af0a60 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/return_dynamic_types_cross_call_simple.sol @@ -0,0 +1,13 @@ +contract C { + function dyn() public returns (bytes memory) { + return "1234567890123456789012345678901234567890"; + } + function f() public returns (bytes memory) { + return this.dyn(); + } +} +// ==== +// compileViaYul: also +// EVMVersion: >homestead +// ---- +// f() -> 0x20, 40, "12345678901234567890123456789012", "34567890" diff --git a/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol b/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol new file mode 100644 index 000000000000..eb67bf38d0f6 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol @@ -0,0 +1,26 @@ +library L { + struct S { uint x; uint y; } + function f(uint[] storage r, S storage s) public returns (uint, uint, uint, uint) { + r[2] = 8; + s.x = 7; + return (r[0], r[1], s.x, s.y); + } +} +contract C { + uint8 x = 3; + L.S s; + uint[] r; + function f() public returns (uint, uint, uint, uint, uint, uint) { + r = new uint[](6); + r[0] = 1; + r[1] = 2; + r[2] = 3; + s.x = 11; + s.y = 12; + (uint a, uint b, uint c, uint d) = L.f(r, s); + return (r[2], s.x, a, b, c, d); + } +} +// ---- +// library: L +// f() -> 8, 7, 1, 2, 7, 12 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol index 6253039f125b..9e5985cbca27 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function enc_packed_bytes(bytes calldata data, uint256 start, uint256 end) external returns (bytes memory) { return abi.encodePacked(data[start:end]); diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol index 405d34c5a137..c3f472ba2369 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_empty_string_v2.sol @@ -1,6 +1,6 @@ // Tests that this will not end up using a "bytes0" type // (which would assert) -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -11,5 +11,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol index 1074a1d0438f..f2013f6c8ab1 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_rational_v2.sol @@ -1,5 +1,5 @@ // Tests that rational numbers (even negative ones) are encoded properly. -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -10,5 +10,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x20, 0x40, 0x1, -2 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol index bfa7507120d1..8fb565d3f226 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol new file mode 100644 index 000000000000..8c33411890ff --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol @@ -0,0 +1,32 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Data { + uint a; + uint[2] b; + uint c; +} + +contract A { + function get() public view returns (Data memory) { + return Data(5, [uint(66), 77], 8); + } +} + +contract B { + function foo(A _a) public returns (uint) { + return _a.get().b[1]; + } +} +==== Source: B ==== +import "A"; + +contract C is B { + function test() public returns (uint) { + return foo(new A()); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 77 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol new file mode 100644 index 000000000000..81095f83cf05 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol @@ -0,0 +1,38 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Data { + uint value; +} + +contract A { + function get() public view returns (Data memory) { + return Data(5); + } +} + +contract B { + uint x = 10; + uint y = 10; + + modifier updateStorage() { + A a = new A(); + x = a.get().value; + _; + y = a.get().value; + } +} +==== Source: B ==== +import "A"; + +contract C is B { + function test() + public + updateStorage + returns (uint, uint) + { + return (x, y); + } +} +// ---- +// test() -> 5, 10 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/bool_out_of_bounds.sol b/test/libsolidity/semanticTests/abiEncoderV2/bool_out_of_bounds.sol new file mode 100644 index 000000000000..773795336ebf --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/bool_out_of_bounds.sol @@ -0,0 +1,13 @@ +pragma abicoder v2; + +contract C { + function f(bool b) public pure returns (bool) { return b; } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(bool): true -> true +// f(bool): false -> false +// f(bool): 0x000000 -> false +// f(bool): 0xffffff -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV2/byte_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV2/byte_arrays.sol new file mode 100644 index 000000000000..c66c1692de36 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/byte_arrays.sol @@ -0,0 +1,18 @@ +pragma abicoder v2; + +contract C { + function f(uint a, bytes memory b, uint c) + public pure returns (uint, uint, bytes1, uint) { + return (a, b.length, b[3], c); + } + + function f_external(uint a, bytes calldata b, uint c) + external pure returns (uint, uint, bytes1, uint) { + return (a, b.length, b[3], c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9 +// f_external(uint256,bytes,uint256): 6, 0x60, 9, 7, "abcdefg" -> 6, 7, "d", 9 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol index 6a1bbcffa0b4..d8bcd4601f52 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function g(uint256[] calldata) external pure returns (bytes memory) { @@ -15,6 +15,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // f(uint256[][1]): 32, 32, 0 -> true diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol index c47af87f4ad8..ef48518bad50 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol index 92067f8db540..8b7ecb49aaaf 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_index_access.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_dynamic.sol index 46d1122a951e..ffaedd40a99a 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_dynamic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint8[][1][] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol index 0ded29febf58..42e669567eba 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_decode.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][2][] calldata x) external returns (uint256) { x[0]; // trigger bounds checks @@ -7,6 +7,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 23 # this is the common encoding for x.length == 1 && x[0][0].length == 0 && x[0][1].length == 0 # // f(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 23 # exotic, but still valid encoding # diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol index c777b12fd541..2cf09b19b687 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_dynamic_static_short_reencode.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][2][] calldata x) external returns (uint256) { return 42; diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_function_types.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_function_types.sol index c9ccc88a8b3a..f5380901f113 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_function_types.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_function_types.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(function() external returns (uint)[] calldata s) external returns (uint, uint, uint) { @@ -25,6 +25,8 @@ contract C { return reenc ? this.f_reenc(a) : this.f(a); } } +// ==== +// compileViaYul: also // ---- // g(bool): false -> 23, 37, 71 // g(bool): true -> 23, 37, 71 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol index f50f59d1f76d..e01655fceddb 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_multi_dynamic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol index 3a6fd9151985..fbab06363b03 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[3] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_dynamic_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_dynamic_static.sol index 6e8361d4abd0..4cbdc9da79f5 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_dynamic_static.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_dynamic_static.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint8[1][][1] calldata s) external pure returns (bytes memory) { @@ -41,6 +41,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // g() -> 32, 132, hex"15cfcc01", 32, 32, 1, 42, hex"00000000000000000000000000000000000000000000000000000000" diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol index d23dfecfb3bc..ea4565e1e21b 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_static_index_access.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[3] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol index ce2f7a39067e..a1735c138ee6 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_struct_dynamic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint256[] a; } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol index 51674081e136..b077b66e4934 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_dynamic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[] calldata s1, uint256[] calldata s2, bool which) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol index 45a89c483a84..58ad531efa16 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array_two_static.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[3] calldata s1, uint256[2] calldata s2, bool which) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol index 0ae57dbed332..ad0f00dd667e 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_dynamic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint256[] a; } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_member_offset.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_member_offset.sol index 196967a441aa..7744fbf627ff 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_member_offset.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_member_offset.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct A { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol index 6d747fdf0b5e..413ea30707af 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_struct_simple.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint256 a; } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol index efb5045fc969..3ac82eee5e1c 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/address.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function g(address x) external pure returns (uint256 r) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol index eb9ce5962886..79cf68795164 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bool.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function gggg(bool x) external pure returns (bool) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol index 294a666e9024..26ff5fb697e1 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/bytesx.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function gg1(bytes1 x) external pure returns (bytes32) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/cleanup.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/cleanup.sol new file mode 100644 index 000000000000..cb04163b9752 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/cleanup.sol @@ -0,0 +1,19 @@ +pragma abicoder v2; + +contract C { + function f(uint16 a, int16 b, address c, bytes3 d, bool e) + public pure returns (uint v, uint w, uint x, uint y, uint z) { + assembly { v := a w := b x := c y := d z := e} + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint16,int16,address,bytes3,bool): 1, 2, 3, "a", true -> 1, 2, 3, "a", true +// f(uint16,int16,address,bytes3,bool): 0xffffff, 0x1ffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "abcd", 1 -> FAILURE +// f(uint16,int16,address,bytes3,bool): 0xffffff, 0, 0, "bcd", 1 -> FAILURE +// f(uint16,int16,address,bytes3,bool): 0, 0x1ffff, 0, "ab", 1 -> FAILURE +// f(uint16,int16,address,bytes3,bool): 0, 0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "ad", 1 -> FAILURE +// f(uint16,int16,address,bytes3,bool): 0, 0, 0, "abcd", 1 -> FAILURE +// f(uint16,int16,address,bytes3,bool): 0, 0, 0, "abc", 2 -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/dynamic_array.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/dynamic_array.sol index e56ff257b4e7..fc62f13d0fc8 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/dynamic_array.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/dynamic_array.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function ggg(uint8[] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/function.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/function.sol index 3221bb5732bb..7a4ec0ce5626 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/function.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/function.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { function() external f; } @@ -16,6 +16,8 @@ contract C { return (this.ggg(s.f), this.h(s)); } } +// ==== +// compileViaYul: also // ---- // ffff(uint256): 0 -> 0, 0 // ggg(function): 0 -> 0 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol index 15cd07d496e2..db636986e8bd 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/intx.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function ggg8(int8 x) external pure returns (int256) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/simple_struct.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/simple_struct.sol index 2a2327c082c0..b809feb595e8 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/simple_struct.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/simple_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint8 a; bytes1 b; } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/static_array.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/static_array.sol index d353fda9570b..9806dc019728 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/static_array.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/static_array.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function gggggggg(uint8[2] calldata s) external pure returns (bytes memory) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol index 9788431e1e40..cf17ef623d57 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/cleanup/uintx.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function ggg8(uint8 x) external pure returns (uint256) { diff --git a/test/libsolidity/semanticTests/abiEncoderV2/dynamic_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV2/dynamic_arrays.sol new file mode 100644 index 000000000000..0993ab59496b --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/dynamic_arrays.sol @@ -0,0 +1,12 @@ +pragma abicoder v2; + +contract C { + function f(uint a, uint16[] memory b, uint c) + public pure returns (uint, uint, uint) { + return (b.length, b[a], c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint16[],uint256): 6, 0x60, 9, 7, 11, 12, 13, 14, 15, 16, 17 -> 7, 17, 9 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/abiEncoderV2/dynamic_nested_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV2/dynamic_nested_arrays.sol new file mode 100644 index 000000000000..23faf0d895fd --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/dynamic_nested_arrays.sol @@ -0,0 +1,32 @@ +pragma abicoder v2; + +contract C { + function f(uint a, uint16[][] memory b, uint[2][][3] memory c, uint d) + public pure returns (uint, uint, uint, uint, uint, uint, uint) { + return (a, b.length, b[1].length, b[1][1], c[1].length, c[1][1][1], d); + } + function test() public view returns (uint, uint, uint, uint, uint, uint, uint) { + uint16[][] memory b = new uint16[][](3); + b[0] = new uint16[](2); + b[0][0] = 0x55; + b[0][1] = 0x56; + b[1] = new uint16[](4); + b[1][0] = 0x65; + b[1][1] = 0x66; + b[1][2] = 0x67; + b[1][3] = 0x68; + + uint[2][][3] memory c; + c[0] = new uint[2][](1); + c[0][0][1] = 0x75; + c[1] = new uint[2][](5); + c[1][1][1] = 0x85; + + return this.f(12, b, c, 13); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 12, 3, 4, 0x66, 5, 0x85, 13 +// f(uint256,uint16[][],uint256[2][][3],uint256): 12, 0x80, 0x220, 13, 3, 0x60, 0xC0, 0x160, 2, 85, 86, 4, 101, 102, 103, 104, 0, 0x60, 0xC0, 0x220, 1, 0, 117, 5, 0, 0, 0, 133, 0, 0, 0, 0, 0, 0, 0 -> 12, 3, 4, 0x66, 5, 0x85, 13 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/enums.sol b/test/libsolidity/semanticTests/abiEncoderV2/enums.sol new file mode 100644 index 000000000000..5538c55061d7 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/enums.sol @@ -0,0 +1,16 @@ +pragma abicoder v2; + +contract C { + enum E { A, B } + function f(E e) public pure returns (uint x) { + assembly { x := e } + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint8): 0 -> 0 +// f(uint8): 1 -> 1 +// f(uint8): 2 -> FAILURE +// f(uint8): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV2/memory_params_in_external_function.sol b/test/libsolidity/semanticTests/abiEncoderV2/memory_params_in_external_function.sol index 6c4845403b79..549838f955cf 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/memory_params_in_external_function.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/memory_params_in_external_function.sol @@ -1,14 +1,14 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(bytes memory a, bytes calldata b, uint[] memory c) external pure - returns (uint, byte, uint, byte, uint, uint) + returns (uint, bytes1, uint, bytes1, uint, uint) { return (a.length, a[1], b.length, b[2], c.length, c[3]); } - function g() public returns (uint, byte, uint, byte, uint, uint) { + function g() public returns (uint, bytes1, uint, bytes1, uint, uint) { uint[] memory x = new uint[](4); x[3] = 7; return this.f("abc", "def", x); diff --git a/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol b/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol index a046a4376820..1a233681f0cf 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; // tests encoding from storage arrays @@ -15,7 +15,8 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- -// h(uint256[2][]) : 0x20, 3, 123, 124, 223, 224, 323, 324 -> 32, 256, 0x20, 3, 123, 124, 223, 224, 323, 324 +// h(uint256[2][]): 0x20, 3, 123, 124, 223, 224, 323, 324 -> 32, 256, 0x20, 3, 123, 124, 223, 224, 323, 324 // i(uint256[2][2]): 123, 124, 223, 224 -> 32, 128, 123, 124, 223, 224 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/struct/mediocre2_struct.sol b/test/libsolidity/semanticTests/abiEncoderV2/struct/mediocre2_struct.sol new file mode 100644 index 000000000000..afa16826dc2a --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/struct/mediocre2_struct.sol @@ -0,0 +1,14 @@ +pragma abicoder v2; + +contract C { + struct S { C c; uint[] x; } + function f(uint a, S[2] memory s1, uint b) public returns (uint r1, C r2, uint r3) { + r1 = a; + r2 = s1[0].c; + r3 = b; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,(address,uint256[])[2],uint256): 7, 0x60, 8, 0x40, 0xE0, 0x0, 0x40, 2, 0x11, 0x12, 0x99, 0x40, 4, 0x31, 0x32, 0x34, 0x35 -> 7, 0x0, 8 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/struct/mediocre_struct.sol b/test/libsolidity/semanticTests/abiEncoderV2/struct/mediocre_struct.sol new file mode 100644 index 000000000000..7775423d47bf --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/struct/mediocre_struct.sol @@ -0,0 +1,14 @@ +pragma abicoder v2; + +contract C { + struct S { C c; } + function f(uint a, S[2] memory s1, uint b) public returns (uint r1, C r2, uint r3) { + r1 = a; + r2 = s1[0].c; + r3 = b; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,(address)[2],uint256): 7, 0, 0, 8 -> 7, 0, 8 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_function.sol b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_function.sol new file mode 100644 index 000000000000..7e3031ba553c --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_function.sol @@ -0,0 +1,16 @@ +pragma abicoder v2; + +contract C { + struct S { function () external returns (uint) f; uint b; } + function f(S memory s) public returns (uint, uint) { + return (s.f(), s.b); + } + function test() public returns (uint, uint) { + return this.f(S(this.g, 3)); + } + function g() public returns (uint) { return 7; } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 7, 3 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_short.sol b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_short.sol new file mode 100644 index 000000000000..9eceff81e54a --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_short.sol @@ -0,0 +1,14 @@ +pragma abicoder v2; + +contract C { + struct S { int a; uint b; bytes16 c; } + function f(S memory s) public pure returns (S memory q) { + q = s; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f((int256,uint256,bytes16)): 0xff010, 0xff0002, "abcd" -> 0xff010, 0xff0002, "abcd" +// f((int256,uint256,bytes16)): 0xff010, 0xff0002, 0x1111222233334444555566667777888800000000000000000000000000000000 -> 0xff010, 0xff0002, left(0x11112222333344445555666677778888) diff --git a/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_simple.sol b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_simple.sol new file mode 100644 index 000000000000..d8d8c04380d8 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_simple.sol @@ -0,0 +1,16 @@ +pragma abicoder v2; + +contract C { + struct S { uint a; uint8 b; uint8 c; bytes2 d; } + function f(S memory s) public pure returns (uint a, uint b, uint c, uint d) { + a = s.a; + b = s.b; + c = s.c; + d = uint16(s.d); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f((uint256,uint8,uint8,bytes2)): 1, 2, 3, "ab" -> 1, 2, 3, 0x6162 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_validation.sol b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_validation.sol new file mode 100644 index 000000000000..68fb477a94ef --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/struct/struct_validation.sol @@ -0,0 +1,19 @@ +pragma abicoder v2; + +contract C { + struct S { int16 a; uint8 b; bytes2 c; } + function f(S memory s) public pure returns (uint a, uint b, uint c) { + assembly { + a := mload(s) + b := mload(add(s, 0x20)) + c := mload(add(s, 0x40)) + } + } +} +// ==== +// compileViaYul: also +// ---- +// f((int16,uint8,bytes2)): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01, 0xff, "ab" -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01, 0xff, "ab" +// f((int16,uint8,bytes2)): 0xff010, 0xff, "ab" -> FAILURE +// f((int16,uint8,bytes2)): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01, 0xff0002, "ab" -> FAILURE +// f((int16,uint8,bytes2)): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01, 0xff, "abcd" -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV2/struct/validation_function_type_inside_struct.sol b/test/libsolidity/semanticTests/abiEncoderV2/struct/validation_function_type_inside_struct.sol new file mode 100644 index 000000000000..8c5350828fa0 --- /dev/null +++ b/test/libsolidity/semanticTests/abiEncoderV2/struct/validation_function_type_inside_struct.sol @@ -0,0 +1,17 @@ +pragma abicoder v2; + +contract C { + struct S { function () external x; } + function f(S memory) public pure returns (uint r) { r = 1; } + function g(S calldata) external pure returns (uint r) { r = 2; } + function h(S calldata s) external pure returns (uint r) { s.x; r = 3; } +} +// ==== +// compileViaYul: also +// ---- +// f((function)): "01234567890123456789abcd" -> 1 +// f((function)): "01234567890123456789abcdX" -> FAILURE +// g((function)): "01234567890123456789abcd" -> 2 +// g((function)): "01234567890123456789abcdX" -> 2 +// h((function)): "01234567890123456789abcd" -> 3 +// h((function)): "01234567890123456789abcdX" -> FAILURE diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol index d08e2ff822af..119c33aeb6b8 100644 --- a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol @@ -7,5 +7,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol index 6ca284908fa2..9597a6d64ac9 100644 --- a/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol @@ -6,6 +6,7 @@ contract C { } } // ==== +// compileViaYul: also // ABIEncoderV1Only: true // ---- -// f() -> 0x40, 0xc0, 0x60, 0x20, 0x0, 0x0, 0x0 +// f() -> 0x40, 0xa0, 0x40, 0x20, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/abiencodedecode/contract_array.sol b/test/libsolidity/semanticTests/abiencodedecode/contract_array.sol new file mode 100644 index 000000000000..c26aea83aefb --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/contract_array.sol @@ -0,0 +1,17 @@ +contract C { + function f(bytes calldata x) public returns (C[] memory) { + return abi.decode(x, (C[])); + } + function g() public returns (bytes memory) { + C[] memory c = new C[](3); + c[0] = C(address(0x42)); + c[1] = C(address(0x21)); + c[2] = C(address(0x23)); + return abi.encode(c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes): 0x20, 0xA0, 0x20, 3, 0x01, 0x02, 0x03 -> 0x20, 3, 0x01, 0x02, 0x03 +// g() -> 0x20, 0xa0, 0x20, 3, 0x42, 0x21, 0x23 diff --git a/test/libsolidity/semanticTests/abiencodedecode/contract_array_v2.sol b/test/libsolidity/semanticTests/abiencodedecode/contract_array_v2.sol new file mode 100644 index 000000000000..69360fe048f4 --- /dev/null +++ b/test/libsolidity/semanticTests/abiencodedecode/contract_array_v2.sol @@ -0,0 +1,20 @@ +pragma abicoder v2; +contract C { + function f(bytes calldata x) public returns (C[] memory) { + return abi.decode(x, (C[])); + } + function g() public returns (bytes memory) { + C[] memory c = new C[](3); + c[0] = C(address(0x42)); + c[1] = C(address(0x21)); + c[2] = C(address(0x23)); + return abi.encode(c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes): 0x20, 0xA0, 0x20, 3, 0x01, 0x02, 0x03 -> 0x20, 3, 0x01, 0x02, 0x03 +// f(bytes): 0x20, 0x60, 0x20, 1, 0x0102030405060708090a0b0c0d0e0f1011121314 -> 0x20, 1, 0x0102030405060708090a0b0c0d0e0f1011121314 +// f(bytes): 0x20, 0x60, 0x20, 1, 0x0102030405060708090a0b0c0d0e0f101112131415 -> FAILURE +// g() -> 0x20, 0xa0, 0x20, 3, 0x42, 0x21, 0x23 diff --git a/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol index a56956a90fe2..ae95757f228b 100644 --- a/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol +++ b/test/libsolidity/semanticTests/accessor/accessor_for_const_state_variable.sol @@ -3,5 +3,6 @@ contract Lotto { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // ticketPrice() -> 555 diff --git a/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol b/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol index e2b59b5303de..99933b1ca147 100644 --- a/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol +++ b/test/libsolidity/semanticTests/accessor/accessor_for_state_variable.sol @@ -4,5 +4,6 @@ contract Lotto { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // ticketPrice() -> 500 diff --git a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol index ea2aa3d8f485..87555cab5999 100644 --- a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol +++ b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod.sol @@ -10,5 +10,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 0 diff --git a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol index c70f195bab0d..ae50da74623e 100644 --- a/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol +++ b/test/libsolidity/semanticTests/arithmetics/addmod_mulmod_zero.sol @@ -20,7 +20,8 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// f(uint256): 0 -> FAILURE -// g(uint256): 0 -> FAILURE +// f(uint256): 0 -> FAILURE, hex"4e487b71", 0x12 +// g(uint256): 0 -> FAILURE, hex"4e487b71", 0x12 // h() -> 2 diff --git a/test/libsolidity/semanticTests/arithmetics/block_inside_unchecked.sol b/test/libsolidity/semanticTests/arithmetics/block_inside_unchecked.sol new file mode 100644 index 000000000000..59aa6ca56d2c --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/block_inside_unchecked.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (uint y) { + unchecked{{ + uint max = type(uint).max; + uint x = max + 1; + y = x; + }} + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x00 diff --git a/test/libsolidity/semanticTests/arithmetics/check_var_init.sol b/test/libsolidity/semanticTests/arithmetics/check_var_init.sol new file mode 100644 index 000000000000..96edd308a7be --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/check_var_init.sol @@ -0,0 +1,20 @@ +contract C { + uint public x = msg.value - 10; + constructor() payable {} +} + +contract D { + function f() public { + unchecked { + new C(); + } + } + function g() public payable returns (uint) { + return (new C{value: 11}()).x(); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE, hex"4e487b71", 0x11 +// g(), 100 wei -> 1 diff --git a/test/libsolidity/semanticTests/arithmetics/checked_add.sol b/test/libsolidity/semanticTests/arithmetics/checked_add.sol new file mode 100644 index 000000000000..34b9e74e7171 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/checked_add.sol @@ -0,0 +1,13 @@ +contract C { + // Input is still not checked - this needs ABIEncoderV2! + function f(uint16 a, uint16 b) public returns (uint16) { + return a + b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(uint16,uint16): 65534, 0 -> 0xfffe +// f(uint16,uint16): 65536, 0 -> 0x00 +// f(uint16,uint16): 65535, 0 -> 0xffff +// f(uint16,uint16): 65535, 1 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/arithmetics/checked_called_by_unchecked.sol b/test/libsolidity/semanticTests/arithmetics/checked_called_by_unchecked.sol new file mode 100644 index 000000000000..87e33ed4611a --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/checked_called_by_unchecked.sol @@ -0,0 +1,14 @@ +contract C { + function add(uint16 a, uint16 b) public returns (uint16) { + return a + b; + } + + function f(uint16 a, uint16 b, uint16 c) public returns (uint16) { + unchecked { return add(a, b) + c; } + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint16,uint16,uint16): 0xe000, 0xe500, 2 -> FAILURE, hex"4e487b71", 0x11 +// f(uint16,uint16,uint16): 0xe000, 0x1000, 0x1000 -> 0x00 diff --git a/test/libsolidity/semanticTests/arithmetics/checked_modifier_called_by_unchecked.sol b/test/libsolidity/semanticTests/arithmetics/checked_modifier_called_by_unchecked.sol new file mode 100644 index 000000000000..42658f83a4f2 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/checked_modifier_called_by_unchecked.sol @@ -0,0 +1,13 @@ +contract C { + modifier add(uint16 a, uint16 b) { + unchecked { a + b; } + _; + } + + function f(uint16 a, uint16 b, uint16 c) public add(a, b) returns (uint16) { + return b + c; + } +} +// ---- +// f(uint16,uint16,uint16): 0xe000, 0xe500, 2 -> 58626 +// f(uint16,uint16,uint16): 0x1000, 0xe500, 0xe000 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol index 0ccc0acb5e48..51aeb3d34227 100644 --- a/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol +++ b/test/libsolidity/semanticTests/arithmetics/divisiod_by_zero.sol @@ -9,8 +9,9 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // div(uint256,uint256): 7, 2 -> 3 -// div(uint256,uint256): 7, 0 -> FAILURE # throws # +// div(uint256,uint256): 7, 0 -> FAILURE, hex"4e487b71", 0x12 # throws # // mod(uint256,uint256): 7, 2 -> 1 -// mod(uint256,uint256): 7, 0 -> FAILURE # throws # +// mod(uint256,uint256): 7, 0 -> FAILURE, hex"4e487b71", 0x12 # throws # diff --git a/test/libsolidity/semanticTests/arithmetics/exp_associativity.sol b/test/libsolidity/semanticTests/arithmetics/exp_associativity.sol new file mode 100644 index 000000000000..4b2341949387 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/exp_associativity.sol @@ -0,0 +1,41 @@ +contract C { + // (2**3)**4 = 4096 + // 2**(3**4) = 2417851639229258349412352 + function test_hardcode1(uint a, uint b, uint c) public returns (uint256) { + return a**b**c; + } + + // (3**2)**2)**2 = 6561 + // 3**(2**(2**2) = 43046721 + function test_hardcode2(uint a, uint b, uint c, uint d) public returns (uint256) { + return a**b**c**d; + } + + function test_invariant(uint a, uint b, uint c) public returns (bool) { + return a**b**c == a**(b**c); + } + + function test_literal_mix(uint a, uint b) public returns (bool) { + return + (a**2**b == a**(2**b)) && + (2**a**b == 2**(a**b)) && + (a**b**2 == a**(b**2)); + } + + function test_other_operators(uint a, uint b) public returns (bool) { + return + (a**b/25 == (a**b)/25) && + (a**b*3**b == (a**b)*(3**b)) && + (b**a**a/b**a**b == (b**(a**a))/(b**(a**b))); + } +} + +// ==== +// compileViaYul: also +// ---- +// test_hardcode1(uint256,uint256,uint256): 2, 3, 4 -> 2417851639229258349412352 +// test_hardcode2(uint256,uint256,uint256,uint256): 3, 2, 2, 2 -> 43046721 +// test_invariant(uint256,uint256,uint256): 2, 3, 4 -> true +// test_invariant(uint256,uint256,uint256): 3, 4, 2 -> true +// test_literal_mix(uint256,uint256): 2, 3 -> true +// test_other_operators(uint256,uint256): 2, 4 -> true diff --git a/test/libsolidity/semanticTests/arithmetics/signed_mod.sol b/test/libsolidity/semanticTests/arithmetics/signed_mod.sol new file mode 100644 index 000000000000..c4356b0f5bbc --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/signed_mod.sol @@ -0,0 +1,24 @@ +contract C { + function f(int a, int b) public pure returns (int) { + return a % b; + } + function g(bool _check) public pure returns (int) { + int x = type(int).min; + if (_check) { + return x / -1; + } else { + unchecked { return x / -1; } + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f(int256,int256): 7, 5 -> 2 +// f(int256,int256): 7, -5 -> 2 +// f(int256,int256): -7, 5 -> -2 +// f(int256,int256): -7, 5 -> -2 +// f(int256,int256): -5, -5 -> 0 +// g(bool): true -> FAILURE, hex"4e487b71", 0x11 +// g(bool): false -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 diff --git a/test/libsolidity/semanticTests/arithmetics/unchecked_called_by_checked.sol b/test/libsolidity/semanticTests/arithmetics/unchecked_called_by_checked.sol new file mode 100644 index 000000000000..895a9b17cb02 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/unchecked_called_by_checked.sol @@ -0,0 +1,17 @@ +contract C { + function add(uint16 a, uint16 b) public returns (uint16) { + unchecked { + return a + b; + } + } + + function f(uint16 a) public returns (uint16) { + return add(a, 0x100) + 0x100; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint16): 7 -> 0x0207 +// f(uint16): 0xffff -> 511 +// f(uint16): 0xfeff -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/arithmetics/unchecked_div_by_zero.sol b/test/libsolidity/semanticTests/arithmetics/unchecked_div_by_zero.sol new file mode 100644 index 000000000000..83d57ce26467 --- /dev/null +++ b/test/libsolidity/semanticTests/arithmetics/unchecked_div_by_zero.sol @@ -0,0 +1,22 @@ +contract C { + function div(uint256 a, uint256 b) public returns (uint256) { + // Does not disable div by zero check + unchecked { + return a / b; + } + } + + function mod(uint256 a, uint256 b) public returns (uint256) { + // Does not disable div by zero check + unchecked { + return a % b; + } + } +} +// ==== +// compileViaYul: also +// ---- +// div(uint256,uint256): 7, 2 -> 3 +// div(uint256,uint256): 7, 0 -> FAILURE, hex"4e487b71", 0x12 # throws # +// mod(uint256,uint256): 7, 2 -> 1 +// mod(uint256,uint256): 7, 0 -> FAILURE, hex"4e487b71", 0x12 # throws # diff --git a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol index 9cec148c7b75..78d60913d452 100644 --- a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol +++ b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol @@ -10,6 +10,8 @@ contract Test { return data; } } +// ==== +// compileViaYul: also // ---- // set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06 // data(uint256,uint256): 0x02, 0x02 -> 0x09 diff --git a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol index 7277e8502f01..588b954aedae 100644 --- a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol +++ b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol @@ -32,6 +32,7 @@ contract c { data.push(bytes1(i)); } data.pop(); + data.pop(); assembly { r := sload(data.slot) } @@ -40,6 +41,10 @@ contract c { // ==== // compileViaYul: also // ---- +// storage: empty // test_short() -> 1780731860627700044960722568376587075150542249149356309979516913770823710 +// storage: nonempty // test_long() -> 67 -// test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979440702 +// storage: nonempty +// test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979433020 +// storage: nonempty diff --git a/test/libsolidity/semanticTests/array/bytes_length_member.sol b/test/libsolidity/semanticTests/array/bytes_length_member.sol index 8c48720175a0..fbff51f02a10 100644 --- a/test/libsolidity/semanticTests/array/bytes_length_member.sol +++ b/test/libsolidity/semanticTests/array/bytes_length_member.sol @@ -10,7 +10,8 @@ contract c { bytes data; } - +// ==== +// compileViaYul: also // ---- // getLength() -> 0 // set(): 1, 2 -> true diff --git a/test/libsolidity/semanticTests/array/calldata_array.sol b/test/libsolidity/semanticTests/array/calldata_array.sol index 9fdd8b44bd5b..7897a8eb6742 100644 --- a/test/libsolidity/semanticTests/array/calldata_array.sol +++ b/test/libsolidity/semanticTests/array/calldata_array.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -13,5 +13,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256[2]): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/array/calldata_array_as_argument_internal_function.sol b/test/libsolidity/semanticTests/array/calldata_array_as_argument_internal_function.sol new file mode 100644 index 000000000000..21085a7d0deb --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_as_argument_internal_function.sol @@ -0,0 +1,19 @@ +pragma abicoder v2; +contract Test { + function f(uint256[] calldata c) internal returns (uint a, uint b) { + return (c.length, c[0]); + } + + function g(uint256[] calldata c) external returns (uint a, uint b) { + return f(c); + } + + function h(uint256[] calldata c, uint start, uint end) external returns (uint a, uint b) { + return f(c[start: end]); + } +} +// ==== +// compileViaYul: also +// ---- +// g(uint256[]): 0x20, 4, 1, 2, 3, 4 -> 4, 1 +// h(uint256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 2, 2 diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol index cab938d6c7c3..5aacf790fb98 100644 --- a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -13,6 +13,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256[][]): 0x20, 0x0 -> 42 # valid access stub # // f(uint256[][]): 0x20, 0x1 -> FAILURE # invalid on argument decoding # diff --git a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol index d4c02df5ee5c..d63b14302c1f 100644 --- a/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol +++ b/test/libsolidity/semanticTests/array/calldata_array_dynamic_invalid_static_middle.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol b/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol index 170dee6ee556..c5aa08ddb9a6 100644 --- a/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol +++ b/test/libsolidity/semanticTests/array/calldata_array_of_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -22,5 +22,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/calldata_array_two_dimensional.sol b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional.sol new file mode 100644 index 000000000000..95e257beeb69 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional.sol @@ -0,0 +1,38 @@ +pragma abicoder v2; +contract C { + function test(uint256[][2] calldata a) external returns (uint256) { + return a.length; + } + function test(uint256[][2] calldata a, uint256 i) external returns (uint256) { + return a[i].length; + } + function test(uint256[][2] calldata a, uint256 i, uint256 j) external returns (uint256) { + return a[i][j]; + } + function reenc(uint256[][2] calldata a, uint256 i, uint256 j) external returns (uint256) { + return this.test(a, i, j); + } +} +// ==== +// compileViaYul: also +// ---- +// test(uint256[][2]): 0x20, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 2 +// test(uint256[][2],uint256): 0x40, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 3 +// test(uint256[][2],uint256): 0x40, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 4 +// test(uint256[][2],uint256,uint256): 0x60, 0, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// reenc(uint256[][2],uint256,uint256): 0x60, 0, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// test(uint256[][2],uint256,uint256): 0x60, 0, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// reenc(uint256[][2],uint256,uint256): 0x60, 0, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// test(uint256[][2],uint256,uint256): 0x60, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// reenc(uint256[][2],uint256,uint256): 0x60, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// test(uint256[][2],uint256,uint256): 0x60, 1, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 0, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// test(uint256[][2],uint256,uint256): 0x60, 1, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 1, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// test(uint256[][2],uint256,uint256): 0x60, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// test(uint256[][2],uint256,uint256): 0x60, 1, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// reenc(uint256[][2],uint256,uint256): 0x60, 1, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// test(uint256[][2],uint256,uint256): 0x60, 0, 3, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE, hex"4e487b71", 0x32 +// test(uint256[][2],uint256,uint256): 0x60, 1, 4, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE, hex"4e487b71", 0x32 +// test(uint256[][2],uint256): 0x40, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/array/calldata_array_two_dimensional_1.sol b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional_1.sol new file mode 100644 index 000000000000..23c251307168 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_array_two_dimensional_1.sol @@ -0,0 +1,38 @@ +pragma abicoder v2; +contract C { + function test(uint256[][] calldata a) external returns (uint256) { + return a.length; + } + function test(uint256[][] calldata a, uint256 i) external returns (uint256) { + return a[i].length; + } + function test(uint256[][] calldata a, uint256 i, uint256 j) external returns (uint256) { + return a[i][j]; + } + function reenc(uint256[][] calldata a, uint256 i, uint256 j) external returns (uint256) { + return this.test(a, i, j); + } +} +// ==== +// compileViaYul: also +// ---- +// test(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 2 +// test(uint256[][],uint256): 0x40, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 3 +// test(uint256[][],uint256): 0x40, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 4 +// test(uint256[][],uint256,uint256): 0x60, 0, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// reenc(uint256[][],uint256,uint256): 0x60, 0, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A01 +// test(uint256[][],uint256,uint256): 0x60, 0, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// reenc(uint256[][],uint256,uint256): 0x60, 0, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A02 +// test(uint256[][],uint256,uint256): 0x60, 0, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// reenc(uint256[][],uint256,uint256): 0x60, 0, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0A03 +// test(uint256[][],uint256,uint256): 0x60, 1, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 0, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B01 +// test(uint256[][],uint256,uint256): 0x60, 1, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 1, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B02 +// test(uint256[][],uint256,uint256): 0x60, 1, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B03 +// test(uint256[][],uint256,uint256): 0x60, 1, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// reenc(uint256[][],uint256,uint256): 0x60, 1, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> 0x0B04 +// test(uint256[][],uint256,uint256): 0x60, 0, 3, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE, hex"4e487b71", 0x32 +// test(uint256[][],uint256,uint256): 0x60, 1, 4, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE, hex"4e487b71", 0x32 +// test(uint256[][],uint256): 0x40, 2, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/array/calldata_bytes_array_bounds.sol b/test/libsolidity/semanticTests/array/calldata_bytes_array_bounds.sol new file mode 100644 index 000000000000..df15ad38bb40 --- /dev/null +++ b/test/libsolidity/semanticTests/array/calldata_bytes_array_bounds.sol @@ -0,0 +1,12 @@ +pragma abicoder v2; +contract C { + function f(bytes[] calldata a, uint256 i) external returns (uint) { + return uint8(a[0][i]); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes[],uint256): 0x40, 0, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> 0x61 +// f(bytes[],uint256): 0x40, 1, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> 0x62 +// f(bytes[],uint256): 0x40, 2, 1, 0x20, 2, 0x6162000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/array/calldata_slice_access.sol b/test/libsolidity/semanticTests/array/calldata_slice_access.sol index 7eb975677aa7..e58376dfb0f9 100644 --- a/test/libsolidity/semanticTests/array/calldata_slice_access.sol +++ b/test/libsolidity/semanticTests/array/calldata_slice_access.sol @@ -8,6 +8,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256[],uint256,uint256): 0x80, 0, 0, 0, 1, 42 -> // f(uint256[],uint256,uint256): 0x80, 0, 1, 0, 1, 42 -> @@ -24,23 +25,23 @@ contract C { // f(uint256[],uint256,uint256): 0x80, -1, 0, 0, 1, 42 -> FAILURE // f(uint256[],uint256,uint256): 0x80, -1, -1, 0, 1, 42 -> FAILURE // g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 0, 1, 42 -> 42, 42, 42 -// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 1, 42 -> FAILURE -// g(uint256[],uint256,uint256,uint256): 0x80, 0, 0, 0, 1, 42 -> FAILURE -// g(uint256[],uint256,uint256,uint256): 0x80, 1, 1, 0, 1, 42 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 1, 42 -> FAILURE, hex"4e487b71", 0x32 +// g(uint256[],uint256,uint256,uint256): 0x80, 0, 0, 0, 1, 42 -> FAILURE, hex"4e487b71", 0x32 +// g(uint256[],uint256,uint256,uint256): 0x80, 1, 1, 0, 1, 42 -> FAILURE, hex"4e487b71", 0x32 // g(uint256[],uint256,uint256,uint256): 0x80, 0, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4201, 0x4201, 0x4201 // g(uint256[],uint256,uint256,uint256): 0x80, 0, 5, 4, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205 -// g(uint256[],uint256,uint256,uint256): 0x80, 0, 5, 5, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 0, 5, 5, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 // g(uint256[],uint256,uint256,uint256): 0x80, 1, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4202, 0x4202, 0x4202 // g(uint256[],uint256,uint256,uint256): 0x80, 1, 5, 3, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205 -// g(uint256[],uint256,uint256,uint256): 0x80, 1, 5, 4, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 1, 5, 4, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 // g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205 -// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE -// g(uint256[],uint256,uint256,uint256): 0x80, 5, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 +// g(uint256[],uint256,uint256,uint256): 0x80, 5, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 // g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4201, 0x4201, 0x4201 -// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 // g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4201, 0x4201, 0x4201 -// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 0, 1, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 // g(uint256[],uint256,uint256,uint256): 0x80, 1, 2, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4202, 0x4202, 0x4202 -// g(uint256[],uint256,uint256,uint256): 0x80, 1, 2, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 1, 2, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 // g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 0, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> 0x4205, 0x4205, 0x4205 -// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 4, 5, 1, 5, 0x4201, 0x4202, 0x4203, 0x4204, 0x4205 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol index 6a34670ffe2f..c696002ad3a7 100644 --- a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -7,6 +7,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // constructor(): 1, 2, 3 -> // a(uint256): 0 -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol new file mode 100644 index 000000000000..20c934bc4f3d --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol @@ -0,0 +1,25 @@ +contract c { + uint[9] m_data; + uint[] m_data_dyn; + uint8[][] m_byte_data; + function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) { + m_data = a; + m_data_dyn = a; + m_byte_data = b; + return b[3][1]; // note that access and declaration are reversed to each other + } + function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { + a = m_data.length; + b = m_data[7]; + c = m_data_dyn.length; + d = m_data_dyn[7]; + e = m_byte_data.length; + f = m_byte_data[3].length; + g = m_byte_data[3][1]; + } +} +// ==== +// compileViaYul: also +// ---- +// store(uint256[9],uint8[3][]): 21, 22, 23, 24, 25, 26, 27, 28, 29, 0x140, 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 -> 32 +// retrieve() -> 9, 28, 9, 28, 4, 3, 32 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol new file mode 100644 index 000000000000..5cfe4865ce80 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol @@ -0,0 +1,25 @@ +// Test to see if cleanup is performed properly during array copying +contract C { + uint128[] x; + function f() public returns(bool) { + x.push(42); x.push(42); x.push(42); x.push(42); + uint128[] memory y = new uint128[](1); + y[0] = 23; + x = y; + assembly { sstore(x.slot, 4) } + + assert(x[0] == 23); + assert(x[1] == 0); + + assert(x[2] == 0); + // Issue 9832: the cleanup was only performed for the first packed type leaving the rest of + // the slot dirty. + assert(x[3] == 0); + + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol new file mode 100644 index 000000000000..37d78e6abe92 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol @@ -0,0 +1,50 @@ +// Issue 9832: Test to see if cleanup is performed properly after array copying +contract C { + uint40[] x; + function f() public returns(bool) { + + x.push(42); x.push(42); x.push(42); x.push(42); + x.push(42); x.push(42); x.push(42); x.push(42); + x.push(42); x.push(42); x.push(42); x.push(42); + x.push(42); x.push(42); x.push(42); x.push(42); + x.push(42); x.push(42); x.push(42); x.push(42); + + uint40[] memory y = new uint40[](1); + y[0] = 23; + x = y; + + assembly { sstore(x.slot, 20) } + + assert(x[0] == 23); + assert(x[1] == 0); + assert(x[2] == 0); + assert(x[3] == 0); + + assert(x[4] == 0); + assert(x[5] == 0); + assert(x[6] == 0); + assert(x[7] == 0); + + assert(x[8] == 0); + assert(x[9] == 0); + assert(x[10] == 0); + assert(x[11] == 0); + + assert(x[12] == 0); + assert(x[13] == 0); + assert(x[14] == 0); + assert(x[15] == 0); + + assert(x[16] == 0); + assert(x[17] == 0); + assert(x[18] == 0); + assert(x[19] == 0); + + return true; + + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol new file mode 100644 index 000000000000..98339880baf0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol @@ -0,0 +1,17 @@ +contract C { + uint256[] x; + function f() public returns(uint256) { + x.push(42); x.push(42); x.push(42); x.push(42); + uint256[] memory y = new uint256[](1); + y[0] = 23; + x = y; + assembly { sstore(x.slot, 4) } + assert(x[1] == 0); + assert(x[2] == 0); + return x[3]; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol new file mode 100644 index 000000000000..2118a854de69 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol @@ -0,0 +1,46 @@ +contract C { + uint128[] x; + uint64[] x1; + uint120[] x2; + function f() public returns(uint128) { + x.push(42); x.push(42); x.push(42); x.push(42); + uint128[] memory y = new uint128[](1); + y[0] = 23; + x = y; + assembly { sstore(x.slot, 4) } + assert(x[0] == 23); + assert(x[2] == 0); + assert(x[3] == 0); + return x[1]; + } + + function g() public returns(uint64) { + x1.push(42); x1.push(42); x1.push(42); x1.push(42); + uint64[] memory y = new uint64[](1); + y[0] = 23; + x1 = y; + assembly { sstore(x1.slot, 4) } + assert(x1[0] == 23); + assert(x1[2] == 0); + assert(x1[3] == 0); + return x1[1]; + } + + function h() public returns(uint120) { + x2.push(42); x2.push(42); x2.push(42); x2.push(42); + uint120[] memory y = new uint120[](1); + y[0] = 23; + x2 = y; + assembly { sstore(x2.slot, 4) } + assert(x2[0] == 23); + assert(x2[2] == 0); + assert(x2[3] == 0); + return x2[1]; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0 +// g() -> 0 +// h() -> 0 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/array_copy_different_packing.sol b/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_copy_different_packing.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol new file mode 100644 index 000000000000..a600bffddcb4 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -0,0 +1,42 @@ +contract c { + uint[3][90][] large; + uint[3][3][] small; + function test() public returns (uint r) { + for (uint i = 0; i < 7; i++) { + large.push(); + small.push(); + } + large[3][2][0] = 2; + large[1] = large[3]; + small[3][2][0] = 2; + small[1] = small[2]; + r = (( + small[3][2][0] * 0x100 | + small[1][2][0]) * 0x100 | + large[3][2][0]) * 0x100 | + large[1][2][0]; + delete small; + delete large; + + } + function clear() public returns (uint, uint) { + for (uint i = 0; i < 7; i++) { + large.push(); + small.push(); + } + small[3][2][0] = 0; + large[3][2][0] = 0; + while (small.length > 0) + small.pop(); + while (large.length > 0) + large.pop(); + return (small.length, large.length); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 0x02000202 +// storage: empty +// clear() -> 0, 0 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_memory_to_storage.sol new file mode 100644 index 000000000000..76dea2b18a7e --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_memory_to_storage.sol @@ -0,0 +1,30 @@ +contract C { + uint128[13] unused; + uint32[] a; + uint32[3] b; + function f() public returns (uint32, uint256) { + uint32[] memory m = new uint32[](3); + m[0] = 1; + m[1] = 2; + m[2] = 3; + a = m; + assert(a[0] == m[0]); + assert(a[1] == m[1]); + assert(a[2] == m[2]); + return (a[0], a.length); + } + function g() public returns (uint32, uint32, uint32) { + uint32[3] memory m; + m[0] = 1; m[1] = 2; m[2] = 3; + a = m; + b = m; + assert(a[0] == b[0] && a[1] == b[1] && a[2] == b[2]); + assert(a.length == b.length); + return (a[0], b[1], a[2]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 3 +// g() -> 1, 2, 3 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/array_copy_nested_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol similarity index 91% rename from test/libsolidity/semanticTests/array/array_copy_nested_array.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol index 9330d31623b3..bd4b1ea92a96 100644 --- a/test/libsolidity/semanticTests/array/array_copy_nested_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol @@ -11,5 +11,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_abi_signed.sol similarity index 94% rename from test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_storage_abi_signed.sol index 0e225f1e598c..1d2c978fb501 100644 --- a/test/libsolidity/semanticTests/array/array_copy_storage_abi_signed.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_abi_signed.sol @@ -16,5 +16,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol new file mode 100644 index 000000000000..e1b6cfeb1442 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol @@ -0,0 +1,19 @@ +contract c { + uint64[] data1; + uint256[] data2; + + function test() public returns (uint256 x, uint256 y) { + data2.push(11); + data1.push(0); + data1.push(1); + data1.push(2); + data1.push(3); + data1.push(4); + data2 = data1; + assert(data1[0] == data2[0]); + x = data2.length; + y = data2[4]; + } +} +// ---- +// test() -> 5, 4 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol new file mode 100644 index 000000000000..f2235bf0d00f --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol @@ -0,0 +1,24 @@ + +contract c { + uint[] data1; + uint[] data2; + function setData1(uint length, uint index, uint value) public { + data1 = new uint[](length); + if (index < length) + data1[index] = value; + } + function copyStorageStorage() public { data2 = data1; } + function getData2(uint index) public returns (uint len, uint val) { + len = data2.length; if (index < len) val = data2[index]; + } +} +// ==== +// compileViaYul: also +// ---- +// setData1(uint256,uint256,uint256): 10, 5, 4 -> +// copyStorageStorage() -> +// getData2(uint256): 5 -> 10, 4 +// setData1(uint256,uint256,uint256): 0, 0, 0 -> +// copyStorageStorage() -> +// getData2(uint256): 0 -> 0, 0 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol new file mode 100644 index 000000000000..7815f0a28cb5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol @@ -0,0 +1,22 @@ +contract c { + uint256[] data1; + uint256[] data2; + + function test() public returns (uint256 x, uint256 y) { + data2.push(11); + data1.push(0); + data1.push(1); + data1.push(2); + data1.push(3); + data1.push(4); + data2 = data1; + assert(data1[0] == data2[0]); + x = data2.length; + y = data2[4]; + } +} + +// ==== +// compileViaYul: also +// ---- +// test() -> 5, 4 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol similarity index 88% rename from test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol index 4432776fbc77..9b3ba326ed15 100644 --- a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol @@ -10,5 +10,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 9, 4 diff --git a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol similarity index 91% rename from test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol index 71601d477140..98642c9e1fae 100644 --- a/test/libsolidity/semanticTests/array/array_copy_storage_storage_static_static.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol @@ -13,5 +13,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 8, 0 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol new file mode 100644 index 000000000000..097c86dfe79a --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol @@ -0,0 +1,22 @@ +contract c { + struct Data { uint x; uint y; } + Data[] data1; + Data[] data2; + function test() public returns (uint x, uint y) { + while (data1.length < 9) + data1.push(); + data1[8].x = 4; + data1[8].y = 5; + data2 = data1; + x = data2[8].x; + y = data2[8].y; + while (data1.length > 0) + data1.pop(); + data2 = data1; + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 4, 5 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_to_memory.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_to_memory.sol new file mode 100644 index 000000000000..cd3c184d2c0a --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_to_memory.sol @@ -0,0 +1,12 @@ +contract C { + uint[] a; + function f() public returns (uint, uint) { + a.push(1); a.push(0); a.push(0); + uint[] memory b = a; + return (b[0], b.length); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 3 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol new file mode 100644 index 000000000000..5e0cae64d319 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol @@ -0,0 +1,19 @@ +contract c { + bytes1[10] data1; + bytes2[32] data2; + function test() public returns (uint check, uint res1, uint res2) { + uint i; + for (i = 0; i < data2.length; ++i) + data2[i] = 0xffff; + check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14])); + for (i = 0; i < data1.length; ++i) + data1[i] = bytes1(uint8(1 + i)); + data2 = data1; + for (i = 0; i < 16; ++i) + res1 |= uint(uint16(data2[i])) * 0x10000**i; + for (i = 0; i < 16; ++i) + res2 |= uint(uint16(data2[16 + i])) * 0x10000**i; + } +} +// ---- +// test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_copy_target_leftover2.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol diff --git a/test/libsolidity/semanticTests/array/array_copy_target_simple.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_copy_target_simple.sol rename to test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol diff --git a/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_memory.sol b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_memory.sol new file mode 100644 index 000000000000..7da4fc4ed769 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_memory.sol @@ -0,0 +1,38 @@ +pragma abicoder v2; + +contract c { + function test1(uint256[][] calldata c) external returns (uint256, uint256) { + uint256[][] memory a1 = c; + assert(a1[0][0] == c[0][0]); + assert(a1[0][1] == c[0][1]); + return (a1.length, a1[0][0] + a1[1][1]); + } + + function test2(uint256[][2] calldata c) external returns (uint256, uint256) { + uint256[][2] memory a2 = c; + assert(a2[0][0] == c[0][0]); + assert(a2[0][1] == c[0][1]); + return (a2[0].length, a2[0][0] + a2[1][1]); + } + + function test3(uint256[2][] calldata c) external returns (uint256, uint256) { + uint256[2][] memory a3 = c; + assert(a3[0][0] == c[0][0]); + assert(a3[0][1] == c[0][1]); + return (a3.length, a3[0][0] + a3[1][1]); + } + + function test4(uint256[2][2] calldata c) external returns (uint256) { + uint256[2][2] memory a4 = c; + assert(a4[0][0] == c[0][0]); + assert(a4[0][1] == c[0][1]); + return (a4[0][0] + a4[1][1]); + } +} +// ==== +// compileViaYul: also +// ---- +// test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65 +// test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65 +// test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65 +// test4(uint256[2][2]): 23, 42, 23, 42 -> 65 diff --git a/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol new file mode 100644 index 000000000000..efd90f88fecf --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol @@ -0,0 +1,43 @@ +pragma abicoder v2; + +contract c { + uint256[][] a1; + uint256[][2] a2; + uint256[2][] a3; + uint256[2][2] a4; + + function test1(uint256[][] calldata c) external returns (uint256, uint256) { + a1 = c; + assert(a1[0][0] == c[0][0]); + assert(a1[0][1] == c[0][1]); + return (a1.length, a1[0][0] + a1[1][1]); + } + + function test2(uint256[][2] calldata c) external returns (uint256, uint256) { + a2 = c; + assert(a2[0][0] == c[0][0]); + assert(a2[0][1] == c[0][1]); + return (a2[0].length, a2[0][0] + a2[1][1]); + } + + function test3(uint256[2][] calldata c) external returns (uint256, uint256) { + a3 = c; + assert(a3[0][0] == c[0][0]); + assert(a3[0][1] == c[0][1]); + return (a3.length, a3[0][0] + a3[1][1]); + } + + function test4(uint256[2][2] calldata c) external returns (uint256) { + a4 = c; + assert(a4[0][0] == c[0][0]); + assert(a4[0][1] == c[0][1]); + return (a4[0][0] + a4[1][1]); + } +} +// ==== +// compileViaYul: true +// ---- +// test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65 +// test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65 +// test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65 +// test4(uint256[2][2]): 23, 42, 23, 42 -> 65 diff --git a/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol new file mode 100644 index 000000000000..45714648b058 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol @@ -0,0 +1,45 @@ +contract Test { + uint128[13] unused; + uint256[][] a; + uint256[4][] b; + uint256[2][3] c; + + function test() external returns (uint256) { + uint256[][] memory m = new uint256[][](2); + m[0] = new uint256[](3); + m[0][0] = 7; m[0][1] = 8; m[0][2] = 9; + m[1] = new uint256[](4); + m[1][1] = 7; m[1][2] = 8; m[1][3] = 9; + a = m; + return a[0][0] + a[0][1] + a[1][3]; + } + + function test1() external returns (uint256) { + uint256[2][] memory m = new uint256[2][](1); + m[0][0] = 1; m[0][1] = 2; + b = m; + return b[0][0] + b[0][1]; + } + + function test2() external returns (uint256) { + uint256[2][2] memory m; + m[0][0] = 1; m[1][1] = 2; m[0][1] = 3; + c = m; + return c[0][0] + c[1][1] + c[0][1]; + } + + function test3() external returns (uint256) { + uint256[2][3] memory m; + m[0][0] = 7; m[1][0] = 8; m[2][0] = 9; + m[0][1] = 7; m[1][1] = 8; m[2][1] = 9; + a = m; + return a[0][0] + a[1][0] + a[2][1]; + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 24 +// test1() -> 3 +// test2() -> 6 +// test3() -> 24 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_memory.sol b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_memory.sol new file mode 100644 index 000000000000..82cde13af4d2 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_memory.sol @@ -0,0 +1,22 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 a; + uint64 b; + uint128 c; + } + function f(S[3] calldata c) public returns (uint128, uint64, uint128) { + S[3] memory m = c; + return (m[2].a, m[1].b, m[0].c); + } + function g(S[] calldata c) public returns (uint128, uint64, uint128) { + S[] memory m = c; + return (m[2].a, m[1].b, m[0].c); + } +} +// ==== +// compileViaYul: also +// ---- +// f((uint128, uint64, uint128)[3]): 0, 0, 12, 0, 11, 0, 10, 0, 0 -> 10, 11, 12 +// g((uint128, uint64, uint128)[]): 0x20, 3, 0, 0, 12, 0, 11, 0, 10, 0, 0 -> 10, 11, 12 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol new file mode 100644 index 000000000000..f8350c67e4a5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol @@ -0,0 +1,19 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 a; + uint64 b; + uint128 c; + } + uint128[137] unused; + S[] s; + function f(S[] calldata c) public returns (uint128, uint64, uint128) { + s = c; + return (s[2].a, s[1].b, s[0].c); + } +} +// ==== +// compileViaYul: true +// ---- +// f((uint128, uint64, uint128)[]): 0x20, 3, 0, 0, 12, 0, 11, 0, 10, 0, 0 -> 10, 11, 12 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/copying/array_of_struct_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_struct_memory_to_storage.sol new file mode 100644 index 000000000000..73fde3aba280 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_of_struct_memory_to_storage.sol @@ -0,0 +1,21 @@ +contract C { + struct S { + uint128 a; + uint64 b; + uint128 c; + } + uint128[137] unused; + S[] s; + function f() public returns (uint128, uint64, uint128) { + S[] memory m = new S[](3); + m[2].a = 10; + m[1].b = 11; + m[0].c = 12; + s = m; + return (s[2].a, s[1].b, s[0].c); + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> 10, 11, 12 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_memory.sol b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_memory.sol new file mode 100644 index 000000000000..6ec4c547124a --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_memory.sol @@ -0,0 +1,23 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256[] a; + } + + function f(S[] calldata c) external returns (uint256, uint256) { + S[] memory s = c; + assert(s.length == c.length); + for (uint i = 0; i < s.length; i++) { + assert(s[i].a.length == c[i].a.length); + for (uint j = 0; j < s[i].a.length; j++) { + assert(s[i].a[j] == c[i].a[j]); + } + } + return (s[1].a.length, s[1].a[0]); + } +} +// ==== +// compileViaYul: also +// ---- +// f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol new file mode 100644 index 000000000000..e930a8ea6b20 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol @@ -0,0 +1,25 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256[] a; + } + + S[] s; + + function f(S[] calldata c) external returns (uint256, uint256) { + s = c; + assert(s.length == c.length); + for (uint i = 0; i < s.length; i++) { + assert(s[i].a.length == c[i].a.length); + for (uint j = 0; j < s[i].a.length; j++) { + assert(s[i].a[j] == c[i].a[j]); + } + } + return (s[1].a.length, s[1].a[0]); + } +} +// ==== +// compileViaYul: true +// ---- +// f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_memory_to_storage.sol new file mode 100644 index 000000000000..5d332e6e8a4e --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_memory_to_storage.sol @@ -0,0 +1,28 @@ +pragma abicoder v2; + +contract C { + struct S { + uint136 p; + uint128[3] b; + uint128[] c; + } + + S[] s; + + function f() external returns (uint256, uint256, uint128, uint128) { + S[] memory m = new S[](3); + m[1] = S(0, [uint128(1), 2, 3], new uint128[](3)); + m[1].c[0] = 1; + m[1].c[1] = 2; + m[1].c[2] = 3; + s = m; + assert(s.length == m.length); + assert(s[1].b[1] == m[1].b[1]); + assert(s[1].c[0] == m[1].c[0]); + return (s[1].b.length, s[1].c.length, s[1].b[2], s[1].c[0]); + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> 3, 3, 3, 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_storage_multi_items_per_slot.sol b/test/libsolidity/semanticTests/array/copying/array_storage_multi_items_per_slot.sol new file mode 100644 index 000000000000..1a043545256a --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/array_storage_multi_items_per_slot.sol @@ -0,0 +1,17 @@ +contract C { + uint8[33] a; + uint32[9] b; + uint120[3] c; + + function f() public returns (uint8, uint32, uint120) { + a[32] = 1; a[31] = 2; a[30] = 3; + b[0] = 1; b[1] = 2; b[2] = 3; + c[2] = 3; c[1] = 1; + return (a[32], b[1], c[2]); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 1, 2, 3 diff --git a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol new file mode 100644 index 000000000000..8b99bf14cad8 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol @@ -0,0 +1,18 @@ +contract Test { + uint24[] public data; + function set(uint24[] memory _data) public returns (uint) { + data = _data; + return data.length; + } + function get() public returns (uint24[] memory) { + return data; + } +} +// ==== +// compileViaYul: also +// ---- +// set(uint24[]): 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -> 18 +// data(uint256): 7 -> 8 +// data(uint256): 15 -> 16 +// data(uint256): 18 -> FAILURE +// get() -> 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol new file mode 100644 index 000000000000..2decb15620cf --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol @@ -0,0 +1,17 @@ +contract c { + function set(uint key) public returns (bool) { data[key] = msg.data; return true; } + function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } + mapping(uint => bytes) data; +} +// ==== +// compileViaYul: also +// ---- +// set(uint256): 1, 2 -> true +// set(uint256): 2, 2, 3, 4, 5 -> true +// storage: nonempty +// copy(uint256,uint256): 1, 2 -> true +// storage: nonempty +// copy(uint256,uint256): 99, 1 -> true +// storage: nonempty +// copy(uint256,uint256): 99, 2 -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/bytes_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/bytes_memory_to_storage.sol new file mode 100644 index 000000000000..9cc10dfd5978 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/bytes_memory_to_storage.sol @@ -0,0 +1,13 @@ +contract C { + bytes s; + function f() external returns (bytes1) { + bytes memory data = "abcd"; + s = data; + return s[0]; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> "a" diff --git a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_memory.sol b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_memory.sol new file mode 100644 index 000000000000..7a52348c68b5 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_memory.sol @@ -0,0 +1,12 @@ +contract C { + bytes s = "abcd"; + function f() external returns (bytes1) { + bytes memory data = s; + return data[0]; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> "a" diff --git a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol new file mode 100644 index 000000000000..8db4787368f8 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol @@ -0,0 +1,26 @@ +contract c { + bytes a; + bytes b; + function f(uint len) public returns (bytes memory) { + bytes memory x = new bytes(len); + for (uint i = 0; i < len; i++) + x[i] = bytes1(uint8(i)); + a = x; + for (uint i = 0; i < len; i++) + assert(a[i] == x[i]); + b = a; + for (uint i = 0; i < len; i++) + assert(b[i] == x[i]); + return b; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0 -> 0x20, 0x00 +// f(uint256): 31 -> 0x20, 0x1f, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e00 +// f(uint256): 32 -> 0x20, 0x20, 1780731860627700044960722568376592200742329637303199754547598369979440671 +// f(uint256): 33 -> 0x20, 33, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x2000000000000000000000000000000000000000000000000000000000000000 +// f(uint256): 63 -> 0x20, 0x3f, 1780731860627700044960722568376592200742329637303199754547598369979440671, 14532552714582660066924456880521368950258152170031413196862950297402215316992 +// f(uint256): 12 -> 0x20, 0x0c, 0x0102030405060708090a0b0000000000000000000000000000000000000000 +// f(uint256): 129 -> 0x20, 0x81, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f, 29063324697304692433803953038474361308315562010425523193971352996434451193439, 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f, -57896044618658097711785492504343953926634992332820282019728792003956564819968 diff --git a/test/libsolidity/semanticTests/array/copying/calldata_2d_bytes_to_memory.sol b/test/libsolidity/semanticTests/array/copying/calldata_2d_bytes_to_memory.sol new file mode 100644 index 000000000000..1687fb54a741 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/calldata_2d_bytes_to_memory.sol @@ -0,0 +1,14 @@ +pragma abicoder v2; + +contract C { + function g(bytes[2] memory m) internal returns (bytes memory) { + return m[0]; + } + function f(bytes[2] calldata c) external returns (bytes memory) { + return g(c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes[2]): 0x20, 0x40, 0x40, 2, "ab" -> 0x20, 2, "ab" diff --git a/test/libsolidity/semanticTests/array/copying/calldata_2d_bytes_to_memory_2.sol b/test/libsolidity/semanticTests/array/copying/calldata_2d_bytes_to_memory_2.sol new file mode 100644 index 000000000000..4f43cc1ebdc3 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/calldata_2d_bytes_to_memory_2.sol @@ -0,0 +1,18 @@ +pragma abicoder v2; + +contract C { + function g(bytes[2] memory m) internal { + assert(m[0].length > 1); + assert(m[1].length > 1); + assert(m[0][0] == m[1][0]); + assert(m[0][1] == m[1][1]); + } + function f(bytes[2] calldata c) external { + g(c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes[2]): 0x20, 0x40, 0x40, 2, "ab" -> +// f(bytes[2]): 0x20, 0x40, 0x40, 1, "a" -> FAILURE, hex"4e487b71", 0x01 diff --git a/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol b/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol new file mode 100644 index 000000000000..9c102229862c --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol @@ -0,0 +1,13 @@ +pragma abicoder v2; + +contract C { + uint256[] s; + function f(uint256[] calldata data) external returns (uint) { + s = data; + return s[0]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[]): 0x20, 0x03, 0x1, 0x2, 0x3 -> 0x1 diff --git a/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol b/test/libsolidity/semanticTests/array/copying/calldata_array_of_struct_to_memory.sol similarity index 86% rename from test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol rename to test/libsolidity/semanticTests/array/copying/calldata_array_of_struct_to_memory.sol index f408746f5f15..eba9105e5b6f 100644 --- a/test/libsolidity/semanticTests/array/calldata_array_of_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/array/copying/calldata_array_of_struct_to_memory.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -21,5 +21,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256)[]): 0x20, 0x2, 0x1, 0x2, 0x3, 0x4 -> 2, 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/copying/calldata_array_static_to_memory.sol b/test/libsolidity/semanticTests/array/copying/calldata_array_static_to_memory.sol new file mode 100644 index 000000000000..0d7d6f1f8d46 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/calldata_array_static_to_memory.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint256[2] calldata c) public returns (uint256, uint256) { + uint256[2] memory m1 = c; + return (m1[0], m1[1]); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[2]): 43, 57 -> 43, 57 diff --git a/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol b/test/libsolidity/semanticTests/array/copying/calldata_bytes_array_to_memory.sol similarity index 97% rename from test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol rename to test/libsolidity/semanticTests/array/copying/calldata_bytes_array_to_memory.sol index d67a24741ac9..7a966f91f08a 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol +++ b/test/libsolidity/semanticTests/array/copying/calldata_bytes_array_to_memory.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/array/copying/calldata_bytes_to_storage.sol b/test/libsolidity/semanticTests/array/copying/calldata_bytes_to_storage.sol new file mode 100644 index 000000000000..88a54413938d --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/calldata_bytes_to_storage.sol @@ -0,0 +1,12 @@ +contract C { + bytes s; + function f(bytes calldata data) external returns (bytes1) { + s = data; + return s[0]; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(bytes): 0x20, 0x08, "abcdefgh" -> "a" diff --git a/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol b/test/libsolidity/semanticTests/array/copying/calldata_dynamic_array_to_memory.sol similarity index 81% rename from test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol rename to test/libsolidity/semanticTests/array/copying/calldata_dynamic_array_to_memory.sol index 7b225a33b13a..a1cd58194b5d 100644 --- a/test/libsolidity/semanticTests/array/calldata_dynamic_array_to_memory.sol +++ b/test/libsolidity/semanticTests/array/copying/calldata_dynamic_array_to_memory.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -11,5 +11,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(uint256[][]): 0x20, 0x1, 0x20, 0x2, 0x17, 0x2a -> 0x1, 0x40, 0x2, 0x17, 0x2a diff --git a/test/libsolidity/semanticTests/array/copying/calldata_nested_array_copy_to_memory.sol b/test/libsolidity/semanticTests/array/copying/calldata_nested_array_copy_to_memory.sol new file mode 100644 index 000000000000..bfcd5ead482e --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/calldata_nested_array_copy_to_memory.sol @@ -0,0 +1,14 @@ +pragma abicoder v2; + +contract Test { + struct shouldBug { + uint256[][2] deadly; + } + function killer(uint256[][2] calldata weapon) pure external returns (shouldBug memory) { + return shouldBug(weapon); + } +} +// ==== +// compileViaYul: also +// ---- +// killer(uint256[][2]): 0x20, 0x40, 0x40, 2, 1, 2 -> 0x20, 0x20, 0x40, 0xa0, 2, 1, 2, 2, 1, 2 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol new file mode 100644 index 000000000000..f318ee099955 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol @@ -0,0 +1,42 @@ +pragma abicoder v2; +struct S { + uint16 x; + bytes a; + uint16 y; + bytes b; +} +contract C { + uint padding; + S data; + + function f() public returns (bytes memory, bytes memory) { + S memory x; + x.x = 7; + x.b = "1234567890123456789012345678901 1234567890123456789012345678901 123456789"; + x.a = "abcdef"; + x.y = 9; + data = x; + return (data.a, data.b); + } + function g() public returns (bytes memory, bytes memory) { + S memory x; + x.x = 7; + x.b = "12345678923456789"; + x.a = "1234567890123456789012345678901 1234567890123456789012345678901 123456789"; + x.y = 9; + data = x; + return (data.a, data.b); + } + function h() public returns (bytes memory, bytes memory) { + S memory x; + data = x; + return (data.a, data.b); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x40, 0x80, 6, 0x6162636465660000000000000000000000000000000000000000000000000000, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000 +// g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000 +// h() -> 0x40, 0x60, 0x00, 0x00 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol new file mode 100644 index 000000000000..de36b0a290da --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol @@ -0,0 +1,50 @@ +function dataslot() pure returns (bytes32) { + return keccak256(abi.encode(1)); +} + +function readDataSlot(uint offset) view returns (bytes32 r) { + bytes32 s = dataslot(); + assembly { r := sload(add(s, offset)) } +} + +function readDataSlot() view returns (bytes32) { + return readDataSlot(0); +} + +function readHead() view returns (bytes32 r) { + assembly { r := sload(1) } +} + +contract C { + uint padding; + bytes data; + + function f() public returns (uint) { + bytes32 zero; + if (!(readDataSlot() == zero)) return 1; + data = "abc"; + if (!(readDataSlot() == zero)) return 2; + data = "1234567890123456789012345678901234567890123456789012345678901234567890"; + if (!(readDataSlot() != zero)) return 3; + if (!(readDataSlot(1) != zero)) return 4; + if (!(readDataSlot(2) != zero)) return 5; + if (!(readDataSlot(3) == zero)) return 6; + if (!(readDataSlot(4) == zero)) return 7; + data = "abc"; + if (!(readDataSlot() == zero)) return 8; + if (!(readDataSlot(1) == zero)) return 9; + if (!(readDataSlot(2) == zero)) return 10; + if (!(readDataSlot(3) == zero)) return 11; + data = "1234567890123456789012345678901234567890123456789012345678901234567890"; + data = "123456789012345678901234567890123456"; + if (!(readDataSlot() != zero)) return 12; + if (!(readDataSlot(1) != zero)) return 13; + if (!(readDataSlot(2) == zero)) return 14; + if (!(readDataSlot(3) == zero)) return 15; + return 0xff; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0xff diff --git a/test/libsolidity/semanticTests/array/copy_function_storage_array.sol b/test/libsolidity/semanticTests/array/copying/copy_function_storage_array.sol similarity index 92% rename from test/libsolidity/semanticTests/array/copy_function_storage_array.sol rename to test/libsolidity/semanticTests/array/copying/copy_function_storage_array.sol index eba61ad1afa1..7b3022361f3d 100644 --- a/test/libsolidity/semanticTests/array/copy_function_storage_array.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_function_storage_array.sol @@ -14,5 +14,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // test() -> 7 diff --git a/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol similarity index 82% rename from test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol rename to test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol index 076cbbf69a77..f98bcb40d816 100644 --- a/test/libsolidity/semanticTests/array/copy_internal_function_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol @@ -17,6 +17,9 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // one() -> 3 -// two() -> FAILURE +// two() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol new file mode 100644 index 000000000000..ace34e8728a2 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol @@ -0,0 +1,14 @@ + +contract c { + function set() public returns (bool) { data1 = msg.data; return true; } + function reset() public returns (bool) { data1 = data2; return true; } + bytes data1; + bytes data2; +} +// ==== +// compileViaYul: also +// ---- +// set(): 1, 2, 3, 4, 5 -> true +// storage: nonempty +// reset() -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/copying/empty_bytes_copy.sol b/test/libsolidity/semanticTests/array/copying/empty_bytes_copy.sol new file mode 100644 index 000000000000..0f19eaa0ada4 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/empty_bytes_copy.sol @@ -0,0 +1,30 @@ +contract C { + bytes data; + bytes otherData; + function fromMemory() public returns (bytes1) { + bytes memory t; + uint[2] memory x; + x[0] = type(uint).max; + data = t; + data.push(); + return data[0]; + } + function fromCalldata(bytes calldata x) public returns (bytes1) { + data = x; + data.push(); + return data[0]; + } + function fromStorage() public returns (bytes1) { + // zero-length but dirty higher order bits + assembly { sstore(otherData.slot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00) } + data = otherData; + data.push(); + return data[0]; + } +} +// ==== +// compileViaYul: also +// ---- +// fromMemory() -> 0x00 +// fromCalldata(bytes): 0x40, 0x60, 0x00, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// fromStorage() -> 0x00 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested.sol new file mode 100644 index 000000000000..59f5ecffed63 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested.sol @@ -0,0 +1,21 @@ +contract C { + uint72[5][] a; + + function f() public returns (uint72, uint72, uint72, uint72, uint72, uint72, uint72) { + for (uint i = 0; i < 4; i++) + a.push(); + a[0][0] = 1; + a[0][3] = 2; + a[1][1] = 3; + a[1][4] = 4; + a[2][0] = 5; + a[3][2] = 6; + a[3][3] = 7; + uint72[5][] memory m = a; + return (m[0][0], m[0][3], m[1][1], m[1][4], m[2][0], m[3][2], m[3][3]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 3, 4, 5, 6, 7 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol new file mode 100644 index 000000000000..c0dd812ad632 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol @@ -0,0 +1,13 @@ +pragma abicoder v2; +contract C { + bytes[] a; + + function f() public returns (bytes[] memory) { + a.push("abc"); + a.push("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ"); + bytes[] memory m = a; + return m; + } +} +// ---- +// f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_from_pointer.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_from_pointer.sol new file mode 100644 index 000000000000..6a687bdcf9ad --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_from_pointer.sol @@ -0,0 +1,22 @@ +contract C { + uint72[5][] a; + + function f() public returns (uint72, uint72, uint72, uint72, uint72, uint72, uint72) { + for (uint i = 0; i < 4; i++) + a.push(); + a[0][0] = 1; + a[0][3] = 2; + a[1][1] = 3; + a[1][4] = 4; + a[2][0] = 5; + a[3][2] = 6; + a[3][3] = 7; + uint72[5][] storage a_ = a; + uint72[5][] memory m = a_; + return (m[0][0], m[0][3], m[1][1], m[1][4], m[2][0], m[3][2], m[3][3]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2, 3, 4, 5, 6, 7 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_struct.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_struct.sol new file mode 100644 index 000000000000..4da2fa3591d7 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_struct.sol @@ -0,0 +1,26 @@ +contract C { + struct T { uint8 x; uint8 y; uint[] z; } + T[3][] a; + + function f() public returns (uint8, uint8, uint, uint, uint, uint8, uint8, uint, uint, uint) { + a.push(); + a.push(); + a[0][1].x = 11; + a[0][1].y = 12; + a[0][1].z.push(1); + a[0][1].z.push(2); + a[0][1].z.push(3); + a[1][2].x = 21; + a[1][2].y = 22; + a[1][2].z.push(4); + a[1][2].z.push(5); + a[1][2].z.push(6); + T[3][] memory m = a; + return ( + m[0][1].x, m[0][1].y, m[0][1].z[0], m[0][1].z[1], m[0][1].z[2], + m[1][2].x, m[1][2].y, m[1][2].z[0], m[1][2].z[1], m[1][2].z[2] + ); + } +} +// ---- +// f() -> 11, 0x0c, 1, 2, 3, 0x15, 22, 4, 5, 6 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_packed.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_packed.sol new file mode 100644 index 000000000000..8dd7c5b6c078 --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_packed.sol @@ -0,0 +1,16 @@ +contract C { + uint8[33] a; + + function f() public returns (uint8, uint8, uint8) { + a[0] = 2; + a[16] = 3; + a[32] = 4; + uint8[33] memory m = a; + return (m[0], m[16], m[32]); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_packed_dyn.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_packed_dyn.sol new file mode 100644 index 000000000000..d4ef03fbd42e --- /dev/null +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_packed_dyn.sol @@ -0,0 +1,17 @@ +contract C { + uint8[] a; + + function f() public returns (uint8, uint8, uint8) { + for (uint i = 0; i < 33; i++) + a.push(7); + a[0] = 2; + a[16] = 3; + a[32] = 4; + uint8[] memory m = a; + return (m[0], m[16], m[32]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol index a529a767da84..059e3df41cfd 100644 --- a/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol +++ b/test/libsolidity/semanticTests/array/create_dynamic_array_with_zero_length.sol @@ -6,5 +6,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol index 541a037a20e2..9d7d4c6bdb3f 100644 --- a/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol +++ b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol @@ -21,6 +21,7 @@ contract C { }} // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// f() -> FAILURE -// g() -> FAILURE +// f() -> FAILURE, hex"4e487b71", 0x41 +// g() -> FAILURE, hex"4e487b71", 0x41 diff --git a/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol index 9e57f251cf9b..929f935ba5f1 100644 --- a/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol +++ b/test/libsolidity/semanticTests/array/create_multiple_dynamic_arrays.sol @@ -31,5 +31,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/array/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol similarity index 93% rename from test/libsolidity/semanticTests/array/bytes_delete_element.sol rename to test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol index e3a8ec19d196..6cf0da3bb812 100644 --- a/test/libsolidity/semanticTests/array/bytes_delete_element.sol +++ b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol @@ -14,6 +14,7 @@ contract c { uint8(data[97]) == 97; } } - +// ==== +// compileViaYul: also // ---- // test1() -> true diff --git a/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol b/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol new file mode 100644 index 000000000000..2136f7895c5a --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol @@ -0,0 +1,37 @@ +contract C { + bytes data; + + function f() public returns (uint ret) { + data.push("a"); + data.push("b"); + delete data; + assembly { + ret := sload(data.slot) + } + } + + function g() public returns (uint ret) { + assembly { + sstore(data.slot, 67) + } + data.push("a"); + data.push("b"); + assert(data.length == 35); + delete data; + assert(data.length == 0); + + uint size = 999; + assembly { + size := sload(data.slot) + mstore(0, data.slot) + ret := sload(keccak256(0, 32)) + } + assert(size == 0); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0 +// g() -> 0 diff --git a/test/libsolidity/semanticTests/array/delete_memory_array.sol b/test/libsolidity/semanticTests/array/delete/delete_memory_array.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_memory_array.sol rename to test/libsolidity/semanticTests/array/delete/delete_memory_array.sol diff --git a/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol b/test/libsolidity/semanticTests/array/delete/delete_on_array_of_structs.sol similarity index 94% rename from test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol rename to test/libsolidity/semanticTests/array/delete/delete_on_array_of_structs.sol index 316b8d9efd1e..b415ac7f6e44 100644 --- a/test/libsolidity/semanticTests/array/delete_on_array_of_structs.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_on_array_of_structs.sol @@ -16,5 +16,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> true # This code interprets x as an array length and thus will go out of gas. neither of the two should throw due to out-of-bounds access # diff --git a/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol new file mode 100644 index 000000000000..28d6e5d8fd8c --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol @@ -0,0 +1,12 @@ +contract c { + fallback() external { data = msg.data; } + function del() public returns (bool) { delete data; return true; } + bytes data; +} +// ==== +// compileViaYul: also +// ---- +// (): 7 -> +// storage: nonempty +// del(): 7 -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/delete_storage_array.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array.sol similarity index 100% rename from test/libsolidity/semanticTests/array/delete_storage_array.sol rename to test/libsolidity/semanticTests/array/delete/delete_storage_array.sol diff --git a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol new file mode 100644 index 000000000000..281c9a0e5b6e --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol @@ -0,0 +1,18 @@ +contract C { + uint120[] data; + + function f() public returns (uint120, uint120, uint120) { + data.push(123); + data.push(234); + data.push(345); + delete data; + assembly { + sstore(data.slot, 3) + } + return (data[0], data[1], data[2]); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0, 0, 0 diff --git a/test/libsolidity/semanticTests/array/delete/memory_arrays_delete.sol b/test/libsolidity/semanticTests/array/delete/memory_arrays_delete.sol new file mode 100644 index 000000000000..31c86b35393c --- /dev/null +++ b/test/libsolidity/semanticTests/array/delete/memory_arrays_delete.sol @@ -0,0 +1,15 @@ +contract Test { + function del() public returns (uint24[3][4] memory) { + uint24[3][4] memory x; + for (uint24 i = 0; i < x.length; i ++) + for (uint24 j = 0; j < x[i].length; j ++) + x[i][j] = i * 0x10 + j; + delete x[1]; + delete x[3][2]; + return x; + } +} +// ==== +// compileViaYul: also +// ---- +// del() -> 0, 1, 2, 0, 0, 0, 0x20, 0x21, 0x22, 0x30, 0x31, 0 diff --git a/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol new file mode 100644 index 000000000000..07c3f63aa120 --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol @@ -0,0 +1,23 @@ +contract c { + uint[20] spacer; + uint[] dynamic; + function fill() public { + for (uint i = 0; i < 21; ++i) + dynamic.push(i + 1); + } + function halfClear() public { + while (dynamic.length > 5) + dynamic.pop(); + } + function fullClear() public { delete dynamic; } +} +// ==== +// compileViaYul: also +// ---- +// storage: empty +// fill() -> +// storage: nonempty +// halfClear() -> +// storage: nonempty +// fullClear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol index 6680ec5d910a..16f6ba1a6f6f 100644 --- a/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol +++ b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol @@ -39,6 +39,8 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // getLengths() -> 0, 0 // setLengths(uint256,uint256): 48, 49 -> diff --git a/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol b/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol new file mode 100644 index 000000000000..c58abad529fb --- /dev/null +++ b/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol @@ -0,0 +1,23 @@ +contract c { + struct s { uint[][] d; } + s[] data; + function fill() public returns (uint) { + while (data.length < 3) + data.push(); + while (data[2].d.length < 4) + data[2].d.push(); + while (data[2].d[3].length < 5) + data[2].d[3].push(); + data[2].d[3][4] = 8; + return data[2].d[3][4]; + } + function clear() public { delete data; } +} +// ==== +// compileViaYul: also +// ---- +// storage: empty +// fill() -> 8 +// storage: nonempty +// clear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol b/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol index b2c6893a5340..e060ae2d9ff3 100644 --- a/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol +++ b/test/libsolidity/semanticTests/array/dynamic_out_of_bounds_array_access.sol @@ -24,11 +24,11 @@ contract c { // compileViaYul: also // ---- // length() -> 0 -// get(uint256): 3 -> FAILURE +// get(uint256): 3 -> FAILURE, hex"4e487b71", 0x32 // enlarge(uint256): 4 -> 4 // length() -> 4 // set(uint256,uint256): 3, 4 -> true // get(uint256): 3 -> 4 // length() -> 4 -// set(uint256,uint256): 4, 8 -> FAILURE +// set(uint256,uint256): 4, 8 -> FAILURE, hex"4e487b71", 0x32 // length() -> 4 diff --git a/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol index 2ac334c17f5b..02d56710bed3 100644 --- a/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol +++ b/test/libsolidity/semanticTests/array/evm_exceptions_out_of_band_access.sol @@ -16,5 +16,5 @@ contract A { // compileViaYul: also // ---- // test() -> false -// testIt() -> FAILURE +// testIt() -> FAILURE, hex"4e487b71", 0x32 // test() -> false diff --git a/test/libsolidity/semanticTests/array/external_array_args.sol b/test/libsolidity/semanticTests/array/external_array_args.sol new file mode 100644 index 000000000000..532bce0d524e --- /dev/null +++ b/test/libsolidity/semanticTests/array/external_array_args.sol @@ -0,0 +1,13 @@ +contract c { + function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index) + external returns (uint av, uint bv, uint cv) { + av = a[a_index]; + bv = b[b_index]; + cv = c[c_index]; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// test(uint256[8],uint256[],uint256[5],uint256,uint256,uint256): 1, 2, 3, 4, 5, 6, 7, 8, 0x220, 21, 22, 23, 24, 25, 0, 1, 2, 3, 11, 12, 13 -> 1, 12, 23 diff --git a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol new file mode 100644 index 000000000000..074173e9af2f --- /dev/null +++ b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol @@ -0,0 +1,18 @@ +contract c { + uint spacer1; + uint spacer2; + uint[20] data; + function fill() public { + for (uint i = 0; i < data.length; ++i) data[i] = i+1; + } + function clear() public { delete data; } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// storage: empty +// fill() -> +// storage: nonempty +// clear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol index 3f5c0d5dd8dc..38647833534d 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol @@ -35,6 +35,7 @@ contract c { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // setIDStatic(uint256): 0xb -> // getID(uint256): 0x2 -> 0xb diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol index ffeab647f93f..c27de7ea7b71 100644 --- a/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol +++ b/test/libsolidity/semanticTests/array/fixed_bytes_length_access.sol @@ -7,5 +7,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bytes32): "789" -> 32, 16, 8 diff --git a/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol b/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol index 06246cdc6160..6ff519972675 100644 --- a/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol +++ b/test/libsolidity/semanticTests/array/fixed_out_of_bounds_array_access.sol @@ -17,12 +17,13 @@ contract c { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // length() -> 4 // set(uint256,uint256): 3, 4 -> true -// set(uint256,uint256): 4, 5 -> FAILURE -// set(uint256,uint256): 400, 5 -> FAILURE +// set(uint256,uint256): 4, 5 -> FAILURE, hex"4e487b71", 0x32 +// set(uint256,uint256): 400, 5 -> FAILURE, hex"4e487b71", 0x32 // get(uint256): 3 -> 4 -// get(uint256): 4 -> FAILURE -// get(uint256): 400 -> FAILURE +// get(uint256): 4 -> FAILURE, hex"4e487b71", 0x32 +// get(uint256): 400 -> FAILURE, hex"4e487b71", 0x32 // length() -> 4 diff --git a/test/libsolidity/semanticTests/array/function_memory_array.sol b/test/libsolidity/semanticTests/array/function_memory_array.sol index 9ff4cad33e98..2f829382236a 100644 --- a/test/libsolidity/semanticTests/array/function_memory_array.sol +++ b/test/libsolidity/semanticTests/array/function_memory_array.sol @@ -32,10 +32,11 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test(uint256,uint256): 10, 0 -> 11 // test(uint256,uint256): 10, 1 -> 12 // test(uint256,uint256): 10, 2 -> 13 // test(uint256,uint256): 10, 3 -> 15 // test(uint256,uint256): 10, 4 -> 18 -// test(uint256,uint256): 10, 5 -> FAILURE +// test(uint256,uint256): 10, 5 -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/arrays_complex_memory_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/arrays_complex_memory_index_access.sol diff --git a/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access.sol new file mode 100644 index 000000000000..9a0c85ac7c3f --- /dev/null +++ b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access.sol @@ -0,0 +1,27 @@ +contract c { + bytes data; + function direct(bytes calldata arg, uint index) external returns (uint) { + return uint(uint8(arg[index])); + } + function storageCopyRead(bytes calldata arg, uint index) external returns (uint) { + data = arg; + return uint(uint8(data[index])); + } + function storageWrite() external returns (uint) { + data = new bytes(35); + data[31] = 0x77; + data[32] = 0x14; + + data[31] = 0x01; + data[31] |= 0x08; + data[30] = 0x01; + data[32] = 0x03; + return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32])); + } +} +// ==== +// compileViaYul: also +// ---- +// direct(bytes,uint256): 0x40, 33, 34, 0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F, left(0x2021) -> 0x21 +// storageCopyRead(bytes,uint256): 0x40, 33, 34, 0x000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F, left(0x2021) -> 0x21 +// storageWrite() -> 0x193 diff --git a/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access_memory.sol b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access_memory.sol new file mode 100644 index 000000000000..1dcd448b4ef1 --- /dev/null +++ b/test/libsolidity/semanticTests/array/indexAccess/bytes_index_access_memory.sol @@ -0,0 +1,17 @@ +contract Main { + function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (bytes1 c1, bytes1 c2, bytes1 c3) { + c1 = _s1[i1]; + c2 = intern(_s1, i2); + c3 = internIndirect(_s1)[i3]; + } + function intern(bytes memory _s1, uint i) public returns (bytes1 c) { + return _s1[i]; + } + function internIndirect(bytes memory _s1) public returns (bytes memory) { + return _s1; + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes,uint256,uint256,uint256): 0x80, 3, 4, 5, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> "d", "e", "f" diff --git a/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/bytes_memory_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/array/bytes_memory_index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/bytes_memory_index_access.sol diff --git a/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/fixed_bytes_index_access.sol similarity index 92% rename from test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/fixed_bytes_index_access.sol index 9ab74eaf363a..40cc65763366 100644 --- a/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol +++ b/test/libsolidity/semanticTests/array/indexAccess/fixed_bytes_index_access.sol @@ -11,6 +11,8 @@ contract C { return uint256(uint8(data[0][4])); } } +// ==== +// compileViaYul: also // ---- // f(bytes32): "789" -> "9" // g(bytes32): "789" -> 0x35 diff --git a/test/libsolidity/semanticTests/array/index_access.sol b/test/libsolidity/semanticTests/array/indexAccess/index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/array/index_access.sol rename to test/libsolidity/semanticTests/array/indexAccess/index_access.sol diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol b/test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_ints.sol similarity index 86% rename from test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol rename to test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_ints.sol index fc7c85438609..3be84e1847cd 100644 --- a/test/libsolidity/semanticTests/array/inline_array_index_access_ints.sol +++ b/test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_ints.sol @@ -6,5 +6,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3 diff --git a/test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol b/test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_strings.sol similarity index 100% rename from test/libsolidity/semanticTests/array/inline_array_index_access_strings.sol rename to test/libsolidity/semanticTests/array/indexAccess/inline_array_index_access_strings.sol diff --git a/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol b/test/libsolidity/semanticTests/array/indexAccess/memory_arrays_dynamic_index_access_write.sol similarity index 93% rename from test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol rename to test/libsolidity/semanticTests/array/indexAccess/memory_arrays_dynamic_index_access_write.sol index be824b759fc6..046be080c023 100644 --- a/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol +++ b/test/libsolidity/semanticTests/array/indexAccess/memory_arrays_dynamic_index_access_write.sol @@ -15,5 +15,7 @@ contract Test { return set(data)[1]; } } +// ==== +// compileViaYul: also // ---- // f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol b/test/libsolidity/semanticTests/array/indexAccess/memory_arrays_index_access_write.sol similarity index 93% rename from test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol rename to test/libsolidity/semanticTests/array/indexAccess/memory_arrays_index_access_write.sol index 7de7f49be65b..1b17689b4924 100644 --- a/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol +++ b/test/libsolidity/semanticTests/array/indexAccess/memory_arrays_index_access_write.sol @@ -12,5 +12,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/array/inline_array_return.sol b/test/libsolidity/semanticTests/array/inline_array_return.sol index e247d15586c8..d1b862b5a589 100644 --- a/test/libsolidity/semanticTests/array/inline_array_return.sol +++ b/test/libsolidity/semanticTests/array/inline_array_return.sol @@ -11,5 +11,8 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/array/inline_array_singleton.sol b/test/libsolidity/semanticTests/array/inline_array_singleton.sol index e8373854de7f..183b15eb34cc 100644 --- a/test/libsolidity/semanticTests/array/inline_array_singleton.sol +++ b/test/libsolidity/semanticTests/array/inline_array_singleton.sol @@ -6,5 +6,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 4 diff --git a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol index 960b7fdb5d34..528a78b9e23a 100644 --- a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol +++ b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_ints.sol @@ -9,5 +9,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3, 6 diff --git a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol index fa9f24a7a856..12131b4ba7cd 100644 --- a/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol +++ b/test/libsolidity/semanticTests/array/inline_array_storage_to_memory_conversion_strings.sol @@ -8,5 +8,8 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x40, 0x80, 0x3, "ray", 0x2, "mi" diff --git a/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol b/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol index ebcae638a105..7cf95742d509 100644 --- a/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol +++ b/test/libsolidity/semanticTests/array/inline_array_strings_from_document.sol @@ -5,6 +5,9 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0 -> 0x20, 0x4, "This" // f(uint256): 1 -> 0x20, 0x2, "is" diff --git a/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol new file mode 100644 index 000000000000..27d7e241e957 --- /dev/null +++ b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol @@ -0,0 +1,92 @@ +contract C { + bytes public x = "abc"; + bytes public y; + function invalidateXShort() public { + assembly { sstore(x.slot, 64) } + delete y; + } + function invalidateXLong() public { + assembly { sstore(x.slot, 5) } + delete y; + } + function abiEncode() public view returns (bytes memory) { return x; } + function abiEncodePacked() public view returns (bytes memory) { return abi.encodePacked(x); } + function copyToMemory() public view returns (bytes memory m) { m = x; } + function indexAccess() public view returns (bytes1) { return x[0]; } + function assignTo() public { x = "def"; } + function assignToLong() public { x = "1234567890123456789012345678901234567"; } + function copyToStorage() public { y = x; } + function copyFromStorageShort() public { y = "abc"; x = y; } + function copyFromStorageLong() public { y = "1234567890123456789012345678901234567"; x = y; } + function arrayPop() public { x.pop(); } + function arrayPush() public { x.push("t"); } + function arrayPushEmpty() public { x.push(); } + function del() public { delete x; } +} +// ---- +// x() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 +// abiEncode() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 +// abiEncodePacked() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 +// copyToMemory() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 +// indexAccess() -> 0x6100000000000000000000000000000000000000000000000000000000000000 +// arrayPushEmpty() +// arrayPush() +// x() -> 0x20, 5, 0x6162630074000000000000000000000000000000000000000000000000000000 +// arrayPop() +// assignToLong() +// x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 +// assignTo() +// x() -> 0x20, 3, 0x6465660000000000000000000000000000000000000000000000000000000000 +// copyFromStorageShort() +// x() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 +// copyFromStorageLong() +// x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 +// copyToStorage() +// x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 +// y() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 +// del() +// x() -> 0x20, 0x00 +// invalidateXLong() +// x() -> FAILURE, hex"4e487b71", 0x22 +// abiEncode() -> FAILURE, hex"4e487b71", 0x22 +// abiEncodePacked() -> FAILURE, hex"4e487b71", 0x22 +// copyToMemory() -> FAILURE, hex"4e487b71", 0x22 +// indexAccess() -> FAILURE, hex"4e487b71", 0x22 +// arrayPushEmpty() -> FAILURE, hex"4e487b71", 0x22 +// arrayPush() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// arrayPop() -> FAILURE, hex"4e487b71", 0x22 +// assignToLong() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// assignTo() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// copyFromStorageShort() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// copyFromStorageLong() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// copyToStorage() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// y() -> 0x20, 0x00 +// del() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// invalidateXShort() +// x() -> FAILURE, hex"4e487b71", 0x22 +// abiEncode() -> FAILURE, hex"4e487b71", 0x22 +// abiEncodePacked() -> FAILURE, hex"4e487b71", 0x22 +// copyToMemory() -> FAILURE, hex"4e487b71", 0x22 +// indexAccess() -> FAILURE, hex"4e487b71", 0x22 +// arrayPushEmpty() -> FAILURE, hex"4e487b71", 0x22 +// arrayPush() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// arrayPop() -> FAILURE, hex"4e487b71", 0x22 +// assignToLong() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// assignTo() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// copyFromStorageShort() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// copyFromStorageLong() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// copyToStorage() -> FAILURE, hex"4e487b71", 0x22 +// x() -> FAILURE, hex"4e487b71", 0x22 +// y() -> 0x20, 0x00 diff --git a/test/libsolidity/semanticTests/array/array_pop.sol b/test/libsolidity/semanticTests/array/pop/array_pop.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_pop.sol rename to test/libsolidity/semanticTests/array/pop/array_pop.sol diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol new file mode 100644 index 000000000000..ece182803eb9 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol @@ -0,0 +1,28 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + uint16[][] data; + function test() public returns (uint x, uint y, uint z) { + for (uint i = 1; i <= 48; i++) + data.push(inner); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1][0]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1][1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1][2]; + for (uint m = 1; m <= 18; m++) + data.pop(); + delete inner; + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 1, 2, 3 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/pop/array_pop_empty_exception.sol similarity index 71% rename from test/libsolidity/semanticTests/array/array_pop_empty_exception.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_empty_exception.sol index 9f436d2d2b3b..8698cbb26d92 100644 --- a/test/libsolidity/semanticTests/array/array_pop_empty_exception.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_empty_exception.sol @@ -8,5 +8,6 @@ contract c { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// test() -> FAILURE +// test() -> FAILURE, hex"4e487b71", 0x31 diff --git a/test/libsolidity/semanticTests/array/array_pop_isolated.sol b/test/libsolidity/semanticTests/array/pop/array_pop_isolated.sol similarity index 92% rename from test/libsolidity/semanticTests/array/array_pop_isolated.sol rename to test/libsolidity/semanticTests/array/pop/array_pop_isolated.sol index 58c56adc9b87..e211dcb79c66 100644 --- a/test/libsolidity/semanticTests/array/array_pop_isolated.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_isolated.sol @@ -10,5 +10,6 @@ contract c { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 3 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol new file mode 100644 index 000000000000..24f21017715a --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol @@ -0,0 +1,12 @@ +contract c { + uint[] data; + function test() public { + data.push(7); + data.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol new file mode 100644 index 000000000000..8cecac615e84 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol @@ -0,0 +1,23 @@ +contract c { + uint16[] data; + function test() public returns (uint16 x, uint16 y, uint16 z) { + for (uint i = 1; i <= 48; i++) + data.push(uint16(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1]; + for (uint m = 1; m <= 18; m++) + data.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 38, 28, 18 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol new file mode 100644 index 000000000000..467774cc2ebe --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol @@ -0,0 +1,23 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + uint24[] data; + function test() public returns (uint24 x, uint24 y) { + for (uint i = 1; i <= 30; i++) + data.push(uint24(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> 20, 10 +// storage: empty diff --git a/test/libsolidity/semanticTests/array/byte_array_pop.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop.sol similarity index 93% rename from test/libsolidity/semanticTests/array/byte_array_pop.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop.sol index 3f0e05b349f1..6a7c7e2e36d0 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop.sol @@ -14,5 +14,6 @@ contract c { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 2, 1, 1 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol similarity index 91% rename from test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol index 2589f1f558c0..49950aedc0d2 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_copy_long.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol @@ -8,5 +8,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_empty_exception.sol similarity index 75% rename from test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_empty_exception.sol index abe8c737b1a6..9d4a06302bee 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_empty_exception.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_empty_exception.sol @@ -11,5 +11,6 @@ contract c { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// test() -> FAILURE +// test() -> FAILURE, hex"4e487b71", 0x31 diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_isolated.sol similarity index 91% rename from test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_isolated.sol index 1635071c52d4..2e0d514cf1a7 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_isolated.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_isolated.sol @@ -10,5 +10,6 @@ contract c { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 3 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol new file mode 100644 index 000000000000..b4d0adfc08c9 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol @@ -0,0 +1,21 @@ +contract c { + uint256 a; + uint256 b; + uint256 c; + bytes data; + function test() public returns (bool) { + for (uint8 i = 0; i <= 40; i++) + data.push(bytes1(i+1)); + for (int8 j = 40; j >= 0; j--) { + require(data[uint8(j)] == bytes1(uint8(j+1))); + require(data.length == uint8(j+1)); + data.pop(); + } + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol new file mode 100644 index 000000000000..a230eb033f09 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol @@ -0,0 +1,20 @@ +contract c { + uint256 a; + uint256 b; + bytes data; + function test() public { + for (uint8 i = 0; i <= 40; i++) + data.push(0x03); + for (uint8 j = 0; j <= 40; j++) { + assembly { + mstore(0, "garbage") + } + data.pop(); + } + } +} +// ==== +// compileViaYul: also +// ---- +// test() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol similarity index 91% rename from test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol rename to test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol index 1f6d500bd2a0..1d4657073e1e 100644 --- a/test/libsolidity/semanticTests/array/byte_array_pop_masking_long.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol @@ -8,5 +8,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol new file mode 100644 index 000000000000..ce4e8dae9585 --- /dev/null +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol @@ -0,0 +1,17 @@ +contract c { + bytes data; + function test() public { + data.push(0x07); + data.push(0x05); + data.push(0x03); + data.pop(); + data.pop(); + data.pop(); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// test() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/array_push.sol b/test/libsolidity/semanticTests/array/push/array_push.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_push.sol rename to test/libsolidity/semanticTests/array/push/array_push.sol diff --git a/test/libsolidity/semanticTests/array/push/array_push_nested.sol b/test/libsolidity/semanticTests/array/push/array_push_nested.sol new file mode 100644 index 000000000000..5829d54000c0 --- /dev/null +++ b/test/libsolidity/semanticTests/array/push/array_push_nested.sol @@ -0,0 +1,17 @@ +contract C { + uint8 b = 23; + uint120[][] s; + uint8 a = 17; + function f() public { + s.push(); + assert(s.length == 1); + assert(s[0].length == 0); + s[0].push(); + assert(s[0].length == 1); + assert(s[0][0] == 0); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> diff --git a/test/libsolidity/semanticTests/array/array_push_packed_array.sol b/test/libsolidity/semanticTests/array/push/array_push_packed_array.sol similarity index 90% rename from test/libsolidity/semanticTests/array/array_push_packed_array.sol rename to test/libsolidity/semanticTests/array/push/array_push_packed_array.sol index dd5e2e85539a..e99221671a68 100644 --- a/test/libsolidity/semanticTests/array/array_push_packed_array.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_packed_array.sol @@ -12,5 +12,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // test() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/array/array_push_struct.sol b/test/libsolidity/semanticTests/array/push/array_push_struct.sol similarity index 100% rename from test/libsolidity/semanticTests/array/array_push_struct.sol rename to test/libsolidity/semanticTests/array/push/array_push_struct.sol diff --git a/test/libsolidity/semanticTests/array/byte_array_push.sol b/test/libsolidity/semanticTests/array/push/byte_array_push.sol similarity index 94% rename from test/libsolidity/semanticTests/array/byte_array_push.sol rename to test/libsolidity/semanticTests/array/push/byte_array_push.sol index 1a9aa3cdaae7..b846c0a7e3d4 100644 --- a/test/libsolidity/semanticTests/array/byte_array_push.sol +++ b/test/libsolidity/semanticTests/array/push/byte_array_push.sol @@ -15,5 +15,6 @@ contract c { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> false diff --git a/test/libsolidity/semanticTests/array/byte_array_push_transition.sol b/test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol similarity index 100% rename from test/libsolidity/semanticTests/array/byte_array_push_transition.sol rename to test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol diff --git a/test/libsolidity/semanticTests/array/push_no_args_1d.sol b/test/libsolidity/semanticTests/array/push/push_no_args_1d.sol similarity index 100% rename from test/libsolidity/semanticTests/array/push_no_args_1d.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_1d.sol diff --git a/test/libsolidity/semanticTests/array/push_no_args_2d.sol b/test/libsolidity/semanticTests/array/push/push_no_args_2d.sol similarity index 96% rename from test/libsolidity/semanticTests/array/push_no_args_2d.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_2d.sol index 489510244deb..ce2b6f668122 100644 --- a/test/libsolidity/semanticTests/array/push_no_args_2d.sol +++ b/test/libsolidity/semanticTests/array/push/push_no_args_2d.sol @@ -24,6 +24,8 @@ contract C { array2d.push().push() = value; } } +// ==== +// compileViaYul: also // ---- // l() -> 0 // f(uint256,uint256): 42, 64 -> diff --git a/test/libsolidity/semanticTests/array/push_no_args_bytes.sol b/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol similarity index 93% rename from test/libsolidity/semanticTests/array/push_no_args_bytes.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol index dc99dbbd5088..45bd4bfec9b3 100644 --- a/test/libsolidity/semanticTests/array/push_no_args_bytes.sol +++ b/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol @@ -18,6 +18,8 @@ contract C { return array[index]; } } +// ==== +// compileViaYul: also // ---- // l() -> 0 // g(uint256): 70 -> diff --git a/test/libsolidity/semanticTests/array/push_no_args_struct.sol b/test/libsolidity/semanticTests/array/push/push_no_args_struct.sol similarity index 95% rename from test/libsolidity/semanticTests/array/push_no_args_struct.sol rename to test/libsolidity/semanticTests/array/push/push_no_args_struct.sol index 6f626d9c350a..89df694c66bd 100644 --- a/test/libsolidity/semanticTests/array/push_no_args_struct.sol +++ b/test/libsolidity/semanticTests/array/push/push_no_args_struct.sol @@ -31,6 +31,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // l() -> 0 // f(uint256): 42 -> diff --git a/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol new file mode 100644 index 000000000000..2dd2b8f50f96 --- /dev/null +++ b/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol @@ -0,0 +1,18 @@ +contract c { + uint spacer1; + uint spacer2; + uint[3] data; + function fill() public { + for (uint i = 0; i < data.length; ++i) data[i] = i+1; + } + function clear() public { delete data; } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// storage: empty +// fill() -> +// storage: nonempty +// clear() -> +// storage: empty diff --git a/test/libsolidity/semanticTests/array/slices/array_slice_calldata_as_argument_of_external_calls.sol b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_as_argument_of_external_calls.sol new file mode 100644 index 000000000000..1f5e929d4a53 --- /dev/null +++ b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_as_argument_of_external_calls.sol @@ -0,0 +1,39 @@ +contract C { + function f1(bytes calldata c1, uint256 s, uint256 e, bytes calldata c2) public returns (bool) { + return keccak256(c1[s:e]) == keccak256(c2); + } + + function f2(bytes calldata c, uint256 s) public returns (uint256, bytes memory) { + return abi.decode(c[s:], (uint256, bytes)); + } + + function f3(bytes calldata c1, uint256 s, uint256 e, bytes calldata c2) public returns (bool) { + bytes memory a = abi.encode(c1[s:e]); + bytes memory b = abi.encode(c2); + if (a.length != b.length) { return false; } + for (uint256 i = 0; i < a.length; i++) { + if (a[i] != b[i]) { return false; } + } + return true; + } + + function f4(bytes calldata c1, uint256 s, uint256 e, bytes calldata c2) public returns (bool) { + bytes memory a = abi.encodePacked(c1[s:e]); + bytes memory b = abi.encodePacked(c2); + if (a.length != b.length) { return false; } + for (uint256 i = 0; i < a.length; i++) { + if (a[i] != b[i]) { return false; } + } + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// f1(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcde" -> true +// f1(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcdf" -> false +// f2(bytes,uint256): 0x40, 0, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" +// f3(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcde" -> true +// f3(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcdf" -> false +// f4(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcde" -> true +// f4(bytes,uint256,uint256,bytes): 0x80, 1, 5, 0xC0, 8, "abcdefgh", 4, "bcdf" -> false diff --git a/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_calldata.sol b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_calldata.sol new file mode 100644 index 000000000000..2003dc47fbc6 --- /dev/null +++ b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_calldata.sol @@ -0,0 +1,27 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 p1; + uint256[3] a; + uint32 p2; + } + function f(S[] calldata c) internal returns (S[] memory) { + return c; + } + function g(S[] calldata c, uint256 s, uint256 e) public returns (S[] memory) { + return f(c[s:e]); + } + + function f1(uint256[3][] calldata c) internal returns (uint256[3][] memory) { + return c; + } + function g1(uint256[3][] calldata c, uint256 s, uint256 e) public returns (uint256[3][] memory) { + return f1(c[s:e]); + } +} +// ==== +// compileViaYul: also +// ---- +// g((uint128, uint256[3], uint32)[], uint256, uint256): 0x60, 1, 3, 4, 55, 1, 2, 3, 66, 66, 2, 3, 4, 77, 77, 3, 4, 5, 88, 88, 4, 5, 6, 99 -> 0x20, 2, 66, 2, 3, 4, 77, 77, 3, 4, 5, 88 +// g1(uint256[3][], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 -> 0x20, 2, 4, 5, 6, 7, 8, 9 diff --git a/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_memory.sol b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_memory.sol new file mode 100644 index 000000000000..6ec8c7900d5c --- /dev/null +++ b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_memory.sol @@ -0,0 +1,29 @@ +contract C { + function f(int[] calldata b, uint256 start, uint256 end) public returns (int) { + int[] memory m = b[start:end]; + uint len = end - start; + assert(len == m.length); + for (uint i = 0; i < len; i++) { + assert(b[start:end][i] == m[i]); + } + return [b[start:end]][0][0]; + } + + function g(int[] calldata b, uint256 start, uint256 end) public returns (int[] memory) { + return b[start:end]; + } + + function h1(int[] memory b) internal returns (int[] memory) { + return b; + } + + function h(int[] calldata b, uint256 start, uint256 end) public returns (int[] memory) { + return h1(b[start:end]); + } +} +// ==== +// compileViaYul: also +// ---- +// f(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 2 +// g(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 0x20, 2, 2, 3 +// h(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 0x20, 2, 2, 3 diff --git a/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_storage.sol new file mode 100644 index 000000000000..88ad84604aab --- /dev/null +++ b/test/libsolidity/semanticTests/array/slices/array_slice_calldata_to_storage.sol @@ -0,0 +1,16 @@ +contract C { + int[] s; + function f(int[] calldata b, uint256 start, uint256 end) public returns (int) { + s = b[start:end]; + uint len = end - start; + assert(len == s.length); + for (uint i = 0; i < len; i++) { + assert(b[start:end][i] == s[i]); + } + return s[0]; + } +} +// ==== +// compileViaYul: also +// ---- +// f(int256[], uint256, uint256): 0x60, 1, 3, 4, 1, 2, 3, 4 -> 2 diff --git a/test/libsolidity/semanticTests/array/storage_array_ref.sol b/test/libsolidity/semanticTests/array/storage_array_ref.sol index 0b0224bfc490..4249fd2770e7 100644 --- a/test/libsolidity/semanticTests/array/storage_array_ref.sol +++ b/test/libsolidity/semanticTests/array/storage_array_ref.sol @@ -15,7 +15,7 @@ contract BinarySearch { uint256 _value ) private returns (uint256 o_position) { if (_len == 0 || (_len == 1 && _data[_begin] != _value)) - return uint256(-1); // failure + return type(uint256).max; // failure uint256 halfLen = _len / 2; uint256 v = _data[_begin + halfLen]; if (_value < v) return find(_data, _begin, halfLen, _value); diff --git a/test/libsolidity/semanticTests/array/string_bytes_conversion.sol b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol index 9578fc4f758a..5f6e73422d80 100644 --- a/test/libsolidity/semanticTests/array/string_bytes_conversion.sol +++ b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol @@ -12,6 +12,8 @@ contract Test { return bytes(s).length; } } +// ==== +// compileViaYul: also // ---- // f(string,uint256): 0x40, 0x02, 0x06, "abcdef" -> "c" // l() -> 0x06 diff --git a/test/libsolidity/semanticTests/asmForLoop/for_loop_break.sol b/test/libsolidity/semanticTests/asmForLoop/for_loop_break.sol index f1f57aca92da..a9ddaf29073c 100644 --- a/test/libsolidity/semanticTests/asmForLoop/for_loop_break.sol +++ b/test/libsolidity/semanticTests/asmForLoop/for_loop_break.sol @@ -11,5 +11,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 6 diff --git a/test/libsolidity/semanticTests/asmForLoop/for_loop_continue.sol b/test/libsolidity/semanticTests/asmForLoop/for_loop_continue.sol index 96277887ed43..d0f4e052e802 100644 --- a/test/libsolidity/semanticTests/asmForLoop/for_loop_continue.sol +++ b/test/libsolidity/semanticTests/asmForLoop/for_loop_continue.sol @@ -11,5 +11,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 5 diff --git a/test/libsolidity/semanticTests/asmForLoop/for_loop_nested.sol b/test/libsolidity/semanticTests/asmForLoop/for_loop_nested.sol index b8d35bb8f5d4..ee8bd64b78c5 100644 --- a/test/libsolidity/semanticTests/asmForLoop/for_loop_nested.sol +++ b/test/libsolidity/semanticTests/asmForLoop/for_loop_nested.sol @@ -15,6 +15,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0 -> 2 // f(uint256): 1 -> 18 diff --git a/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol index a1bdf323686a..3d9bfb6d690b 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol @@ -7,5 +7,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 diff --git a/test/libsolidity/semanticTests/builtinFunctions/blockhash.sol b/test/libsolidity/semanticTests/builtinFunctions/blockhash.sol index 075e6c395862..4093622efa52 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/blockhash.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/blockhash.sol @@ -11,6 +11,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x3737373737373737373737373737373737373737373737373737373737373738 // g() -> 0x3737373737373737373737373737373737373737373737373737373737373739 diff --git a/test/libsolidity/semanticTests/builtinFunctions/blockhash_shadow_resolution.sol b/test/libsolidity/semanticTests/builtinFunctions/blockhash_shadow_resolution.sol index acfb00c25496..82c593e4593a 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/blockhash_shadow_resolution.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/blockhash_shadow_resolution.sol @@ -4,5 +4,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol index e753cbbe99e7..77184bef697b 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol @@ -21,6 +21,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 // g() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol index 91a26945cd59..9f34a5282641 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol @@ -8,5 +8,7 @@ contract c { return keccak256(abi.encodePacked("b", keccak256(data), "a")); } } +// ==== +// compileViaYul: also // ---- // foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol index 1374538c2c04..3f5be7eedbd4 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_empty.sol @@ -6,5 +6,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 diff --git a/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol index 725d984d86f8..b284b7f014d9 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/keccak256_with_bytes.sol @@ -9,5 +9,7 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // foo() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol index 11b9a5339e79..7985364d2023 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol @@ -5,5 +5,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol index 646f8f6e91a6..07b02e9a364a 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol @@ -9,5 +9,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/c99_scoping_activation.sol b/test/libsolidity/semanticTests/c99_scoping_activation.sol index 1201c2c8359f..7c2a5063b0c3 100644 --- a/test/libsolidity/semanticTests/c99_scoping_activation.sol +++ b/test/libsolidity/semanticTests/c99_scoping_activation.sol @@ -36,6 +36,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3 // g() -> 0 diff --git a/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol b/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol index b59885de88b7..ad1c809365fc 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/calldata/calldata_bytes_external.sol b/test/libsolidity/semanticTests/calldata/calldata_bytes_external.sol index 9be9e8eab650..f17ae4f10ccc 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_bytes_external.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_bytes_external.sol @@ -2,7 +2,7 @@ contract CalldataTest { function test(bytes calldata x) public returns (bytes calldata) { return x; } - function tester(bytes calldata x) public returns (byte) { + function tester(bytes calldata x) public returns (bytes1) { return this.test(x)[2]; } } diff --git a/test/libsolidity/semanticTests/calldata/calldata_bytes_internal.sol b/test/libsolidity/semanticTests/calldata/calldata_bytes_internal.sol index 3b39a51a42af..a0384f887145 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_bytes_internal.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_bytes_internal.sol @@ -1,8 +1,8 @@ contract C { - function f(bytes calldata b, uint i) internal pure returns (byte) { + function f(bytes calldata b, uint i) internal pure returns (bytes1) { return b[i]; } - function f(uint, bytes calldata b, uint) external pure returns (byte) { + function f(uint, bytes calldata b, uint) external pure returns (bytes1) { return f(b, 2); } } diff --git a/test/libsolidity/semanticTests/calldata/calldata_internal_function_pointer.sol b/test/libsolidity/semanticTests/calldata/calldata_internal_function_pointer.sol index 1c900b497d17..d6871d7e4367 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_internal_function_pointer.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_internal_function_pointer.sol @@ -1,17 +1,19 @@ contract C { - function(bytes calldata) returns (byte) x; + function(bytes calldata) returns (bytes1) x; constructor() { x = f; } - function f(bytes calldata b) internal pure returns (byte) { + function f(bytes calldata b) internal pure returns (bytes1) { return b[2]; } - function h(bytes calldata b) external returns (byte) { + function h(bytes calldata b) external returns (bytes1) { return x(b); } - function g() external returns (byte) { + function g() external returns (bytes1) { bytes memory a = new bytes(34); - a[2] = byte(uint8(7)); + a[2] = bytes1(uint8(7)); return this.h(a); } } +// ==== +// compileViaYul: also // ---- // g() -> 0x0700000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/calldata/calldata_internal_library.sol b/test/libsolidity/semanticTests/calldata/calldata_internal_library.sol index 90528fc70272..fa44cc144077 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_internal_library.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_internal_library.sol @@ -1,16 +1,16 @@ library L { - function f(uint, bytes calldata _x, uint) internal returns (byte) { + function f(uint, bytes calldata _x, uint) internal returns (bytes1) { return _x[2]; } } contract C { function f(bytes calldata a) external - returns (byte) + returns (bytes1) { return L.f(3, a, 9); } - function g() public returns (byte) { + function g() public returns (bytes1) { bytes memory x = new bytes(4); x[2] = 0x08; return this.f(x); diff --git a/test/libsolidity/semanticTests/calldata/calldata_internal_multi_array.sol b/test/libsolidity/semanticTests/calldata/calldata_internal_multi_array.sol index 6dea02b29325..33f27a084a9a 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_internal_multi_array.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_internal_multi_array.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function g(uint[][2] calldata s) internal pure returns (uint, uint[] calldata) { diff --git a/test/libsolidity/semanticTests/calldata/calldata_memory_mixed.sol b/test/libsolidity/semanticTests/calldata/calldata_memory_mixed.sol index a87afac117b0..2798a6ab7907 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_memory_mixed.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_memory_mixed.sol @@ -1,11 +1,11 @@ contract C { function f(bytes memory _a, bytes calldata _b, bytes memory _c) public - returns (uint, byte, byte, byte) + returns (uint, bytes1, bytes1, bytes1) { return (_a.length + _b.length + _c.length, _a[1], _b[1], _c[1]); } - function g() public returns (uint, byte, byte, byte) { + function g() public returns (uint, bytes1, bytes1, bytes1) { bytes memory x = new bytes(3); bytes memory y = new bytes(4); bytes memory z = new bytes(7); diff --git a/test/libsolidity/semanticTests/calldata/calldata_string_array.sol b/test/libsolidity/semanticTests/calldata/calldata_string_array.sol index 62459505b97d..1ab1b25a8a17 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_string_array.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_string_array.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol b/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol index 6b4eb2f07ff1..dad6864d1dec 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -18,6 +18,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f((uint8,bytes1)): 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" -> 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" # double check that the valid case goes through # // f((uint8,bytes1)): 0x1234, hex"5678000000000000000000000000000000000000000000000000000000000000" -> FAILURE diff --git a/test/libsolidity/semanticTests/calldata/calldata_struct_internal.sol b/test/libsolidity/semanticTests/calldata/calldata_struct_internal.sol index 810900dd67ff..50351087e82d 100644 --- a/test/libsolidity/semanticTests/calldata/calldata_struct_internal.sol +++ b/test/libsolidity/semanticTests/calldata/calldata_struct_internal.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; struct S { uint x; diff --git a/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol index 69b590828518..0844f42de666 100644 --- a/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol +++ b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -13,6 +13,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bool): 0x0 -> 0x0 // f(bool): 0x1 -> 0x1 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol index 074b3ff6b49b..6cad1debf3ac 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_shortening.sol @@ -18,7 +18,7 @@ contract C { assembly { y := x } - address payable z = address(y); + address payable z = payable(address(y)); assembly { r := z } @@ -28,6 +28,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x1122334455667788990011223344556677889900 // g() -> 0x1122334455667788990011223344556677889900 diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol index ce6ff4fe2899..ebff7898c0c8 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; // Checks that address types are properly cleaned before they are compared. @@ -15,6 +15,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE # We input longer data on purpose.# // g(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol index 073e015b4874..8fcc0a4dcbff 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_shortening.sol @@ -12,5 +12,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> "\xff\xff\xff\xff" diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol index 2ca882c9fa26..d6036ba8a151 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; // Checks that bytesXX types are properly cleaned before they are compared. @@ -12,5 +12,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bytes2,uint16): "abc", 0x40102 -> FAILURE # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol index 7cd496818fe9..161b9ff14743 100644 --- a/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol +++ b/test/libsolidity/semanticTests/cleanup/cleanup_in_compound_assign.sol @@ -10,5 +10,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 0xff, 0xff diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol index e6bd08b8fc44..103de86cc638 100644 --- a/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup.sol @@ -1,7 +1,9 @@ contract C { function f() public pure returns (uint x) { - uint8 y = uint8(2)**uint8(8); - return 0**y; + unchecked { + uint8 y = uint8(2)**uint8(8); + return 0**y; + } } } diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol index a9b5e81b54fb..78221939e2ee 100644 --- a/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup_direct.sol @@ -1,6 +1,8 @@ contract C { function f() public pure returns (uint8 x) { - return uint8(0)**uint8(uint8(2)**uint8(8)); + unchecked { + return uint8(0)**uint8(uint8(2)**uint8(8)); + } } } diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol index 4f817d18622a..5ecc1114efb8 100644 --- a/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup_nonzero_base.sol @@ -1,6 +1,9 @@ contract C { function f() public pure returns (uint8 x) { - return uint8(0x166)**uint8(uint8(2)**uint8(8)); + unchecked { + uint16 x = 0x166; + return uint8(x)**uint8(uint8(2)**uint8(8)); + } } } diff --git a/test/libsolidity/semanticTests/cleanup/exp_cleanup_smaller_base.sol b/test/libsolidity/semanticTests/cleanup/exp_cleanup_smaller_base.sol index 3ef61f219084..7b81d0a1c57e 100644 --- a/test/libsolidity/semanticTests/cleanup/exp_cleanup_smaller_base.sol +++ b/test/libsolidity/semanticTests/cleanup/exp_cleanup_smaller_base.sol @@ -4,7 +4,9 @@ contract C { // right before the exp uint16 e = 0x100; uint8 b = 0x2; - return b**e; + unchecked { + return b**e; + } } } // ---- diff --git a/test/libsolidity/semanticTests/constantEvaluator/negative_fractional_mod.sol b/test/libsolidity/semanticTests/constantEvaluator/negative_fractional_mod.sol new file mode 100644 index 000000000000..21675e51afd3 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/negative_fractional_mod.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure returns (int, int) { + int x = int((-(-5.2 % 3)) * 5); + int t = 5; + return (x, (-(-t % 3)) * 5); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 11, 10 diff --git a/test/libsolidity/semanticTests/constantEvaluator/rounding.sol b/test/libsolidity/semanticTests/constantEvaluator/rounding.sol new file mode 100644 index 000000000000..a640d867631e --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/rounding.sol @@ -0,0 +1,15 @@ +contract C { + int constant a = 7; + int constant b = 3; + int constant c = a / b; + int constant d = (-a) / b; + function f() public pure returns (uint, int, uint, int) { + uint[c] memory x; + uint[-d] memory y; + return (x.length, c, y.length, -d); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 2, 2, 2, 2 diff --git a/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol b/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol index 730b04746250..048d5de3afe2 100644 --- a/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol +++ b/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol @@ -3,10 +3,13 @@ contract C { address constant e = 0x1212121212121212121212121000002134593163; - function f() public returns (byte z) { + function f() public returns (bytes1 z) { assembly { z := e } } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x00 diff --git a/test/libsolidity/semanticTests/constants/asm_constant_file_level.sol b/test/libsolidity/semanticTests/constants/asm_constant_file_level.sol new file mode 100644 index 000000000000..4f907646357c --- /dev/null +++ b/test/libsolidity/semanticTests/constants/asm_constant_file_level.sol @@ -0,0 +1,9 @@ +address constant e = 0x1212121212121212121212121000002134593163; + +contract C { + function f() public returns (address z) { + assembly { z := e } + } +} +// ---- +// f() -> 0x1212121212121212121212121000002134593163 diff --git a/test/libsolidity/semanticTests/constants/constant_string.sol b/test/libsolidity/semanticTests/constants/constant_string.sol index 6b588571d0c3..dfea569b57e5 100644 --- a/test/libsolidity/semanticTests/constants/constant_string.sol +++ b/test/libsolidity/semanticTests/constants/constant_string.sol @@ -18,6 +18,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x20, 3, "\x03\x01\x02" // g() -> 0x20, 3, "\x03\x01\x02" diff --git a/test/libsolidity/semanticTests/constants/constant_string_at_file_level.sol b/test/libsolidity/semanticTests/constants/constant_string_at_file_level.sol new file mode 100644 index 000000000000..558659ecb610 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/constant_string_at_file_level.sol @@ -0,0 +1,34 @@ +bytes constant a = "\x03\x01\x02"; +bytes constant b = hex"030102"; +string constant c = "hello"; +uint256 constant x = 56; +enum ActionChoices {GoLeft, GoRight, GoStraight, Sit} +ActionChoices constant choices = ActionChoices.GoRight; +bytes32 constant st = "abc\x00\xff__"; + +contract C { + function f() public returns (bytes memory) { + return a; + } + + function g() public returns (bytes memory) { + return b; + } + + function h() public returns (bytes memory) { + return bytes(c); + } + + function i() public returns (uint, ActionChoices, bytes32) { + return (x, choices, st); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 0x20, 3, "\x03\x01\x02" +// g() -> 0x20, 3, "\x03\x01\x02" +// h() -> 0x20, 5, "hello" +// i() -> 0x38, 1, 0x61626300ff5f5f00000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/constants/constant_variables.sol b/test/libsolidity/semanticTests/constants/constant_variables.sol index 98b4773c79ba..1bc620419d92 100644 --- a/test/libsolidity/semanticTests/constants/constant_variables.sol +++ b/test/libsolidity/semanticTests/constants/constant_variables.sol @@ -7,5 +7,6 @@ contract Foo { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // constructor() -> diff --git a/test/libsolidity/semanticTests/constants/constants_at_file_level_referencing.sol b/test/libsolidity/semanticTests/constants/constants_at_file_level_referencing.sol new file mode 100644 index 000000000000..a862d9d8e3f8 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/constants_at_file_level_referencing.sol @@ -0,0 +1,42 @@ +==== Source: s1.sol ==== + + +bytes constant a = b; +bytes constant b = hex"030102"; + +function fre() pure returns (bytes memory) { + return a; +} + +==== Source: s2.sol ==== + +import "s1.sol"; + +uint256 constant c = uint8(a[0]) + 2; + +contract C { + function f() public returns (bytes memory) { + return a; + } + + function g() public returns (bytes memory) { + return b; + } + + function h() public returns (uint) { + return c; + } + + function i() public returns (bytes memory) { + return fre(); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 0x20, 3, "\x03\x01\x02" +// g() -> 0x20, 3, "\x03\x01\x02" +// h() -> 5 +// i() -> 0x20, 3, "\x03\x01\x02" diff --git a/test/libsolidity/semanticTests/constants/consteval_array_length.sol b/test/libsolidity/semanticTests/constants/consteval_array_length.sol new file mode 100644 index 000000000000..91e929834df4 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/consteval_array_length.sol @@ -0,0 +1,14 @@ +contract C { + uint constant a = 12; + uint constant b = 10; + + function f() public pure returns (uint, uint) { + uint[(a / b) * b] memory x; + return (x.length, (a / b) * b); + } +} +// ==== +// compileViaYul: true +// ---- +// constructor() -> +// f() -> 0x0a, 0x0a diff --git a/test/libsolidity/semanticTests/constants/same_constants_different_files.sol b/test/libsolidity/semanticTests/constants/same_constants_different_files.sol new file mode 100644 index 000000000000..18d00e331557 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/same_constants_different_files.sol @@ -0,0 +1,27 @@ +==== Source: s1.sol ==== + + +uint constant a = 89; + +function fre() pure returns (uint) { + return a; +} + +==== Source: s2.sol ==== + +import {a as b, fre} from "s1.sol"; +import "s1.sol" as M; + +uint256 constant a = 13; + +contract C { + function f() public returns (uint, uint, uint, uint) { + return (a, fre(), M.a, b); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 0x0d, 0x59, 0x59, 0x59 diff --git a/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol index 613cf62bb7db..7908da6e386f 100644 --- a/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol +++ b/test/libsolidity/semanticTests/constants/simple_constant_variables_test.sol @@ -8,5 +8,6 @@ contract Foo { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getX() -> 56 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol new file mode 100644 index 000000000000..ae380af8e54d --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -0,0 +1,28 @@ +contract Base { + uint public m_x; + address[] m_s; + constructor(uint x, address[] memory s) { + m_x = x; + m_s = s; + } + function part(uint i) public returns (address) { + return m_s[i]; + } +} +contract Main is Base { + constructor(address[] memory s, uint x) Base(x, f(s)) {} + function f(address[] memory s) public returns (address[] memory) { + return s; + } +} +contract Creator { + function f(uint x, address[] memory s) public returns (uint r, address ch) { + Main c = new Main(s, x); + r = c.m_x(); + ch = c.part(x); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol new file mode 100644 index 000000000000..1a9832e88f38 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -0,0 +1,28 @@ +contract Base { + uint public m_x; + bytes m_s; + constructor(uint x, bytes memory s) { + m_x = x; + m_s = s; + } + function part(uint i) public returns (bytes1) { + return m_s[i]; + } +} +contract Main is Base { + constructor(bytes memory s, uint x) Base(x, f(s)) {} + function f(bytes memory s) public returns (bytes memory) { + return s; + } +} +contract Creator { + function f(uint x, bytes memory s) public returns (uint r, bytes1 ch) { + Main c = new Main(s, x); + r = c.m_x(); + ch = c.part(x); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol new file mode 100644 index 000000000000..1d163d82ed73 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -0,0 +1,12 @@ +contract Test { + uint public m_x; + bytes public m_s; + constructor(uint x, bytes memory s) { + m_x = x; + m_s = s; + } +} +// ---- +// constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> +// m_x() -> 7 +// m_s() -> 0x20, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol index d59f2cd190da..099202c956d4 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -8,6 +8,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // constructor(): 1, 2, 3, 4 -> // a() -> 1 diff --git a/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol index 9eb3754ff99d..d511f6f1d93f 100644 --- a/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol +++ b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor.sol @@ -17,5 +17,6 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/functions_called_by_constructor_through_dispatch.sol b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor_through_dispatch.sol index 7549f1387429..93f088f2c080 100644 --- a/test/libsolidity/semanticTests/constructor/functions_called_by_constructor_through_dispatch.sol +++ b/test/libsolidity/semanticTests/constructor/functions_called_by_constructor_through_dispatch.sol @@ -27,5 +27,6 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getName() -> "def\x00\x00\x00" diff --git a/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol index 1e8f9bc78142..46115e9c3ad5 100644 --- a/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol +++ b/test/libsolidity/semanticTests/constructor/inline_member_init_inheritence_without_constructor.sol @@ -16,6 +16,7 @@ contract Derived is Base { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getBMember() -> 5 // getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/constructor/order_of_evaluation.sol b/test/libsolidity/semanticTests/constructor/order_of_evaluation.sol new file mode 100644 index 000000000000..c05c2104f0d4 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/order_of_evaluation.sol @@ -0,0 +1,24 @@ +contract A { + constructor(uint) {} +} +contract B { + constructor(uint) {} +} +contract C { + constructor(uint) {} +} +contract D { + constructor(uint) {} +} +contract X is D, C, B, A { + uint[] x; + function f(uint _x) internal returns (uint) { + x.push(_x); + } + function g() public view returns (uint[] memory) { return x; } + constructor() A(f(1)) C(f(2)) B(f(3)) D(f(4)) {} +} +// ==== +// compileViaYul: also +// ---- +// g() -> 0x20, 4, 1, 3, 2, 4 diff --git a/test/libsolidity/semanticTests/constructor/payable_constructor.sol b/test/libsolidity/semanticTests/constructor/payable_constructor.sol index 143212cba7d8..e32302b70d44 100644 --- a/test/libsolidity/semanticTests/constructor/payable_constructor.sol +++ b/test/libsolidity/semanticTests/constructor/payable_constructor.sol @@ -4,5 +4,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // constructor(), 27 wei -> diff --git a/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol index 72833717d0db..499a53282042 100644 --- a/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol +++ b/test/libsolidity/semanticTests/constructor/store_function_in_constructor.sol @@ -16,6 +16,9 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // use(uint256): 3 -> 6 // result_in_constructor() -> 4 diff --git a/test/libsolidity/semanticTests/constructor/store_function_in_constructor_packed.sol b/test/libsolidity/semanticTests/constructor/store_function_in_constructor_packed.sol new file mode 100644 index 000000000000..7b4fdd5409ed --- /dev/null +++ b/test/libsolidity/semanticTests/constructor/store_function_in_constructor_packed.sol @@ -0,0 +1,26 @@ +contract C { + uint16 public result_in_constructor; + function(uint16) returns (uint16) internal x; + uint16 public other = 0x1fff; + + constructor() { + x = doubleInv; + result_in_constructor = use(2); + } + + function doubleInv(uint16 _arg) public returns (uint16 _ret) { + _ret = ~(_arg * 2); + } + + function use(uint16 _arg) public returns (uint16) { + return x(_arg); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// use(uint16): 3 -> 0xfff9 +// result_in_constructor() -> 0xfffb +// other() -> 0x1fff diff --git a/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol index 04be9f8ed0e5..7d7381d77ef0 100644 --- a/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol +++ b/test/libsolidity/semanticTests/constructor/store_internal_unused_function_in_constructor.sol @@ -14,5 +14,8 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // t() -> 7 diff --git a/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol b/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol index 7b166489fb8c..89795124a271 100644 --- a/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol +++ b/test/libsolidity/semanticTests/constructor/store_internal_unused_library_function_in_constructor.sol @@ -17,5 +17,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // t() -> 7 diff --git a/test/libsolidity/semanticTests/constructor_ihneritance_init_order_2.sol b/test/libsolidity/semanticTests/constructor_ihneritance_init_order_2.sol index db5f4556039e..de3529f77d1f 100644 --- a/test/libsolidity/semanticTests/constructor_ihneritance_init_order_2.sol +++ b/test/libsolidity/semanticTests/constructor_ihneritance_init_order_2.sol @@ -8,7 +8,8 @@ contract B is A { uint public y = f(); } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // constructor() -> // y() -> 42 diff --git a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol index 61d8016318e8..84e6a622c77c 100644 --- a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol +++ b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol @@ -12,6 +12,7 @@ contract B is A { } // ==== // compileViaYul: true +// compileToEwasm: also // ---- // constructor() -> // y() -> 42 diff --git a/test/libsolidity/semanticTests/dirty_calldata_bytes.sol b/test/libsolidity/semanticTests/dirty_calldata_bytes.sol index f3cd7b912466..09c3df576d81 100644 --- a/test/libsolidity/semanticTests/dirty_calldata_bytes.sol +++ b/test/libsolidity/semanticTests/dirty_calldata_bytes.sol @@ -1,6 +1,6 @@ contract C { function f(bytes calldata b) public returns (bool correct) { - byte a = b[3]; + bytes1 a = b[3]; uint r; assembly { r := a @@ -10,5 +10,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bytes): 0x20, 0x04, "dead" -> true diff --git a/test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol b/test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol index df5c672d1040..6b4763ab18e8 100644 --- a/test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol +++ b/test/libsolidity/semanticTests/dirty_calldata_dynamic_array.sol @@ -1,6 +1,6 @@ contract C { function f(int16[] calldata a) external returns (bool correct) { - uint32 x = uint32(a[1]); + uint32 x = uint32(uint16(a[1])); uint r; assembly { r := x diff --git a/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol b/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol index 158a85a5e8dd..265e58bda36c 100644 --- a/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol +++ b/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract test { function a(bytes32 h, uint8 v, bytes32 r, bytes32 s) public returns (address addr) { return ecrecover(h, v, r, s); diff --git a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol index 6560baf6a68c..3ee606d36c46 100644 --- a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol +++ b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol @@ -3,7 +3,7 @@ contract C { // (v should be 27 or 28, not 1) // Note that the precompile does not return zero but returns nothing. function f() public returns (address) { - return ecrecover(bytes32(uint(-1)), 1, bytes32(uint(2)), bytes32(uint(3))); + return ecrecover(bytes32(type(uint256).max), 1, bytes32(uint(2)), bytes32(uint(3))); } } // ==== diff --git a/test/libsolidity/semanticTests/empty_contract.sol b/test/libsolidity/semanticTests/empty_contract.sol index f81e0aa2a4e1..baf722c2d010 100644 --- a/test/libsolidity/semanticTests/empty_contract.sol +++ b/test/libsolidity/semanticTests/empty_contract.sol @@ -2,6 +2,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // allowNonExistingFunctions: true // ---- // i_am_not_there() -> FAILURE diff --git a/test/libsolidity/semanticTests/empty_for_loop.sol b/test/libsolidity/semanticTests/empty_for_loop.sol new file mode 100644 index 000000000000..4aafcaf9dd1d --- /dev/null +++ b/test/libsolidity/semanticTests/empty_for_loop.sol @@ -0,0 +1,14 @@ +contract test { + function f() public returns(uint ret) { + ret = 1; + for (;;) { + ret += 1; + if (ret >= 10) break; + } + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 10 diff --git a/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol b/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol index 9ff75fad6064..9ee71eacb763 100644 --- a/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol +++ b/test/libsolidity/semanticTests/enums/constructing_enums_from_ints.sol @@ -2,11 +2,12 @@ contract c { enum Truth {False, True} function test() public returns (uint256) { - return uint256(Truth(uint8(0x701))); + return uint256(Truth(uint8(0x1))); } } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 1 diff --git a/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol b/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol index 9d5492d1e945..9df30f84adba 100644 --- a/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol +++ b/test/libsolidity/semanticTests/enums/enum_explicit_overflow.sol @@ -13,8 +13,8 @@ contract test { d = uint256(choice); } - function getChoiceFromNegativeLiteral() public returns (uint256 d) { - choice = ActionChoices(-1); + function getChoiceFromMax() public returns (uint256 d) { + choice = ActionChoices(type(uint).max); d = uint256(choice); } @@ -23,9 +23,11 @@ contract test { // ==== // compileViaYul: also +// EVMVersion: >=byzantium // ---- -// getChoiceExp(uint256): 3 -> FAILURE # These should throw # -// getChoiceFromSigned(int256): -1 -> FAILURE -// getChoiceFromNegativeLiteral() -> FAILURE +// getChoiceExp(uint256): 2 -> 2 +// getChoiceExp(uint256): 3 -> FAILURE, hex"4e487b71", 33 # These should throw # +// getChoiceFromSigned(int256): -1 -> FAILURE, hex"4e487b71", 33 +// getChoiceFromMax() -> FAILURE, hex"4e487b71", 33 // getChoiceExp(uint256): 2 -> 2 # These should work # // getChoiceExp(uint256): 0 -> 0 diff --git a/test/libsolidity/semanticTests/enums/enum_explicit_overflow_homestead.sol b/test/libsolidity/semanticTests/enums/enum_explicit_overflow_homestead.sol new file mode 100644 index 000000000000..90e5e1b3e6f5 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/enum_explicit_overflow_homestead.sol @@ -0,0 +1,32 @@ +contract test { + enum ActionChoices {GoLeft, GoRight, GoStraight} + + constructor() {} + + function getChoiceExp(uint256 x) public returns (uint256 d) { + choice = ActionChoices(x); + d = uint256(choice); + } + + function getChoiceFromSigned(int256 x) public returns (uint256 d) { + choice = ActionChoices(x); + d = uint256(choice); + } + + function getChoiceFromMax() public returns (uint256 d) { + choice = ActionChoices(type(uint256).max); + d = uint256(choice); + } + + ActionChoices choice; +} + +// ==== +// EVMVersion: FAILURE # These should throw # +// getChoiceFromSigned(int256): -1 -> FAILURE +// getChoiceFromMax() -> FAILURE +// getChoiceExp(uint256): 2 -> 2 # These should work # +// getChoiceExp(uint256): 0 -> 0 diff --git a/test/libsolidity/semanticTests/enums/enum_with_256_members.sol b/test/libsolidity/semanticTests/enums/enum_with_256_members.sol new file mode 100644 index 000000000000..dbb21c47fc19 --- /dev/null +++ b/test/libsolidity/semanticTests/enums/enum_with_256_members.sol @@ -0,0 +1,61 @@ +pragma experimental ABIEncoderV2; + +enum E { + E000, E001, E002, E003, E004, E005, E006, E007, E008, E009, + E010, E011, E012, E013, E014, E015, E016, E017, E018, E019, + E020, E021, E022, E023, E024, E025, E026, E027, E028, E029, + E030, E031, E032, E033, E034, E035, E036, E037, E038, E039, + E040, E041, E042, E043, E044, E045, E046, E047, E048, E049, + E050, E051, E052, E053, E054, E055, E056, E057, E058, E059, + E060, E061, E062, E063, E064, E065, E066, E067, E068, E069, + E070, E071, E072, E073, E074, E075, E076, E077, E078, E079, + E080, E081, E082, E083, E084, E085, E086, E087, E088, E089, + E090, E091, E092, E093, E094, E095, E096, E097, E098, E099, + E100, E101, E102, E103, E104, E105, E106, E107, E108, E109, + E110, E111, E112, E113, E114, E115, E116, E117, E118, E119, + E120, E121, E122, E123, E124, E125, E126, E127, E128, E129, + E130, E131, E132, E133, E134, E135, E136, E137, E138, E139, + E140, E141, E142, E143, E144, E145, E146, E147, E148, E149, + E150, E151, E152, E153, E154, E155, E156, E157, E158, E159, + E160, E161, E162, E163, E164, E165, E166, E167, E168, E169, + E170, E171, E172, E173, E174, E175, E176, E177, E178, E179, + E180, E181, E182, E183, E184, E185, E186, E187, E188, E189, + E190, E191, E192, E193, E194, E195, E196, E197, E198, E199, + E200, E201, E202, E203, E204, E205, E206, E207, E208, E209, + E210, E211, E212, E213, E214, E215, E216, E217, E218, E219, + E220, E221, E222, E223, E224, E225, E226, E227, E228, E229, + E230, E231, E232, E233, E234, E235, E236, E237, E238, E239, + E240, E241, E242, E243, E244, E245, E246, E247, E248, E249, + E250, E251, E252, E253, E254, E255 +} + +contract C { + function getMinMax() public returns (E, E) { + return (E.E000, E.E255); + } + + function intToEnum(uint8 _i) public returns (E) { + return E(_i); + } + + function enumToInt(E _e) public returns (uint8) { + return uint8(_e); + } + + function decodeEnum(bytes memory data) public returns (E) { + (E e) = abi.decode(data, (E)); + return e; + } +} +// ==== +// compileViaYul: also +// ---- +// getMinMax() -> 0, 255 +// intToEnum(uint8): 0 -> 0 +// intToEnum(uint8): 255 -> 255 +// enumToInt(uint8): 0 -> 0 +// enumToInt(uint8): 255 -> 255 +// enumToInt(uint8): 256 -> FAILURE +// decodeEnum(bytes): 0x20, 32, 0 -> 0 +// decodeEnum(bytes): 0x20, 32, 255 -> 255 +// decodeEnum(bytes): 0x20, 32, 256 -> FAILURE diff --git a/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol b/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol index c6830abf8320..ad0c1a007f07 100644 --- a/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol +++ b/test/libsolidity/semanticTests/enums/using_contract_enums_with_explicit_contract_name.sol @@ -8,5 +8,6 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // answer() -> 1 diff --git a/test/libsolidity/semanticTests/enums/using_enums.sol b/test/libsolidity/semanticTests/enums/using_enums.sol index b903e51827e8..0dd7563d2725 100644 --- a/test/libsolidity/semanticTests/enums/using_enums.sol +++ b/test/libsolidity/semanticTests/enums/using_enums.sol @@ -14,5 +14,6 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getChoice() -> 2 diff --git a/test/libsolidity/semanticTests/enums/using_inherited_enum.sol b/test/libsolidity/semanticTests/enums/using_inherited_enum.sol index 187ec962ecad..a19da62e682e 100644 --- a/test/libsolidity/semanticTests/enums/using_inherited_enum.sol +++ b/test/libsolidity/semanticTests/enums/using_inherited_enum.sol @@ -10,5 +10,6 @@ contract test is base { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // answer() -> 1 diff --git a/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol b/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol index 3bd2b0cea727..053a5298da93 100644 --- a/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol +++ b/test/libsolidity/semanticTests/enums/using_inherited_enum_excplicitly.sol @@ -10,5 +10,6 @@ contract test is base { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // answer() -> 1 diff --git a/test/libsolidity/semanticTests/exponentiation/literal_base.sol b/test/libsolidity/semanticTests/exponentiation/literal_base.sol index 2c11519d0867..3dcba494abc0 100644 --- a/test/libsolidity/semanticTests/exponentiation/literal_base.sol +++ b/test/libsolidity/semanticTests/exponentiation/literal_base.sol @@ -1,8 +1,10 @@ contract test { function f(uint x) public pure returns (uint, int) { - uint a = 2 ** x; - int b = -2 ** x; - return (a, b); + unchecked { + uint a = 2 ** x; + int b = -2 ** x; + return (a, b); + } } } // ---- diff --git a/test/libsolidity/semanticTests/exponentiation/signed_base.sol b/test/libsolidity/semanticTests/exponentiation/signed_base.sol index f58c081293e6..4c189d6b8f17 100644 --- a/test/libsolidity/semanticTests/exponentiation/signed_base.sol +++ b/test/libsolidity/semanticTests/exponentiation/signed_base.sol @@ -10,5 +10,8 @@ contract test { return (x**y1, x**y2); } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 9, -27 diff --git a/test/libsolidity/semanticTests/exponentiation/small_exp.sol b/test/libsolidity/semanticTests/exponentiation/small_exp.sol index 9dfb5599b760..a26675fb7f31 100644 --- a/test/libsolidity/semanticTests/exponentiation/small_exp.sol +++ b/test/libsolidity/semanticTests/exponentiation/small_exp.sol @@ -1,12 +1,13 @@ contract test { - function f() public pure returns (uint) { + function f() public pure returns (uint r) { uint32 x; uint8 y; assembly { x := 0xfffffffffe y := 0x102 } - return x**y; + unchecked { r = x**y; } + return r; } } // ---- diff --git a/test/libsolidity/semanticTests/expressions/bit_operators.sol b/test/libsolidity/semanticTests/expressions/bit_operators.sol index 32ec16bf6bc7..22653f4b3d3e 100644 --- a/test/libsolidity/semanticTests/expressions/bit_operators.sol +++ b/test/libsolidity/semanticTests/expressions/bit_operators.sol @@ -15,5 +15,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3855, 268374015, 268370160 diff --git a/test/libsolidity/semanticTests/expressions/bytes_comparison.sol b/test/libsolidity/semanticTests/expressions/bytes_comparison.sol index a9a0d3a75bdf..552e75b76569 100644 --- a/test/libsolidity/semanticTests/expressions/bytes_comparison.sol +++ b/test/libsolidity/semanticTests/expressions/bytes_comparison.sol @@ -8,5 +8,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol index 4f3828f3a976..3f8e9bb6cf17 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_different_types.sol @@ -7,6 +7,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> 0xcd // f(bool): false -> 0xabab diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol index 456b1902c915..740ae28b8dba 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_false_literal.sol @@ -5,5 +5,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 10 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol index 482849648267..5dae57c74e99 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_functions.sol @@ -9,6 +9,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> 1 // f(bool): false -> 2 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol index c8a335384d12..b66d012a35be 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_multiple.sol @@ -8,6 +8,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 1001 -> 1000 // f(uint256): 500 -> 100 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_1.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_1.sol index 3434cca016e8..8f401452849e 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_1.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_1.sol @@ -22,6 +22,9 @@ contract test { return ret; } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> 1 // f(bool): false -> 2 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol index 8633d2bfab48..f522ca6e56e2 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_storage_memory_2.sol @@ -23,6 +23,8 @@ contract test { return ret; } } +// ==== +// compileViaYul: also // ---- // f(bool): true -> 1 // f(bool): false -> 2 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol index 65145507978a..8e5c69d89194 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_true_literal.sol @@ -5,5 +5,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 5 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol index 53eba3f1d3ef..881292dd4b51 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_tuples.sol @@ -5,6 +5,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> 1, 2 // f(bool): false -> 3, 4 diff --git a/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol b/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol index df6cdb3442b9..f8d407631f98 100644 --- a/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol +++ b/test/libsolidity/semanticTests/expressions/conditional_expression_with_return_values.sol @@ -5,6 +5,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bool,uint256): true, 20 -> 20, 0 // f(bool,uint256): false, 20 -> 0, 20 diff --git a/test/libsolidity/semanticTests/expressions/exp_operator_const.sol b/test/libsolidity/semanticTests/expressions/exp_operator_const.sol index 8d282fb8fbd8..29b025acc67f 100644 --- a/test/libsolidity/semanticTests/expressions/exp_operator_const.sol +++ b/test/libsolidity/semanticTests/expressions/exp_operator_const.sol @@ -3,5 +3,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 8 diff --git a/test/libsolidity/semanticTests/expressions/exp_operator_const_signed.sol b/test/libsolidity/semanticTests/expressions/exp_operator_const_signed.sol index 9e3c3b4c9d95..334a8be29784 100644 --- a/test/libsolidity/semanticTests/expressions/exp_operator_const_signed.sol +++ b/test/libsolidity/semanticTests/expressions/exp_operator_const_signed.sol @@ -3,5 +3,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> -8 diff --git a/test/libsolidity/semanticTests/expressions/exp_zero_literal.sol b/test/libsolidity/semanticTests/expressions/exp_zero_literal.sol index 1dfa0a79642a..a5f9fcff5a54 100644 --- a/test/libsolidity/semanticTests/expressions/exp_zero_literal.sol +++ b/test/libsolidity/semanticTests/expressions/exp_zero_literal.sol @@ -3,5 +3,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/expressions/inc_dec_operators.sol b/test/libsolidity/semanticTests/expressions/inc_dec_operators.sol index 3750ee9bb5e4..7f46fd0ea6f3 100644 --- a/test/libsolidity/semanticTests/expressions/inc_dec_operators.sol +++ b/test/libsolidity/semanticTests/expressions/inc_dec_operators.sol @@ -13,5 +13,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x053866 diff --git a/test/libsolidity/semanticTests/expressions/uncalled_address_transfer_send.sol b/test/libsolidity/semanticTests/expressions/uncalled_address_transfer_send.sol index 0c87b67feb91..b7fe44beb362 100644 --- a/test/libsolidity/semanticTests/expressions/uncalled_address_transfer_send.sol +++ b/test/libsolidity/semanticTests/expressions/uncalled_address_transfer_send.sol @@ -1,12 +1,13 @@ contract TransferTest { fallback() external payable { // This used to cause an ICE - address(this).transfer; + payable(this).transfer; } function f() pure public {} } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol new file mode 100644 index 000000000000..6f380ebeee49 --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -0,0 +1,196 @@ +// â”â”â”â”┓â”â”┓â”â”┓â”â”â”â”â”â”┓â”â”â”â”â”â”┓â”â”â”â”â”â”â”â”┓â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”┓â”â”â”â”â”â”â”â”â”┓â”â”â”â”â”â”â”â”â”â”┓â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”┓┠+// ┃â”â”â”â”›â”┛┗┓┃┃â”â”┃â”â”┓┃â”â”┃â”â”┓┃â”â”â”â”┗┓â”┓┃â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”┛┗┓â”â”â”â”┃â”â”┓┃â”â”â”â”â”â”â”â”â”┛┗┓â”â”â”â”â”â”â”â”â”â”â”â”â”┛┗┓ +// ┃┗â”â”┓┗┓â”┛┃┗â”┓┗┛â”┛┃â”â”┃┃â”┃┃â”â”â”â”â”┃┃┃┃â”â”â”┓â”â”â”┓â”â”â”┓â”â”â”┓â”┓┗┓â”â”›â”â”â”â”┃┃â”â”—â”›â”â”â”┓â”â”┓â”┗┓â”â”›â”â”┓â”â”â”┓â”â”â”â”┓┗┓â”â”› +// ┃â”â”â”â”›â”┃┃â”┃â”┓┃â”â”â”›â”â”›â”â”┃┃â”┃┃â”â”â”â”â”┃┃┃┃┃â”┓┃┃â”┓┃┃â”┓┃┃â”â”┫┣┫â”┃┃â”â”â”â”â”┃┃â”â”┓┃â”┓┃┃â”┓┓â”┃┃â”┃â”┛┗â”┓┃â”┃â”â”â”›â”┃┃┠+// ┃┗â”â”┓â”┃┗┓┃┃┃┃┃┃┗â”┓â”┓┃┗â”┛┃â”â”â”â”â”┛┗┛┃┃┃â”┫┃┗┛┃┃┗┛┃┣â”â”┃┃┃â”┃┗┓â”â”â”â”┃┗â”┛┃┃┗┛┃┃┃┃┃â”┃┗┓┃┃â”┃┗┛┗┓┃┗â”┓â”┃┗┓ +// â”—â”â”â”â”›â”â”—â”┛┗┛┗┛┗â”â”â”┛┗┛┗â”â”â”â”›â”â”â”â”â”—â”â”â”┛┗â”â”┛┃â”â”┛┗â”â”┛┗â”â”┛┗┛â”â”—â”â”›â”â”â”â”â”—â”â”â”┛┗â”â”┛┗┛┗┛â”â”—â”┛┗┛â”â”—â”â”â”┛┗â”â”â”›â”â”—â”â”› +// â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”┃┃â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” +// â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”—â”›â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â” + +// SPDX-License-Identifier: CC0-1.0 + +// This interface is designed to be compatible with the Vyper version. +/// @notice This is the Ethereum 2.0 deposit contract interface. +/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs +interface IDepositContract { + /// @notice A processed deposit event. + event DepositEvent( + bytes pubkey, + bytes withdrawal_credentials, + bytes amount, + bytes signature, + bytes index + ); + + /// @notice Submit a Phase 0 DepositData object. + /// @param pubkey A BLS12-381 public key. + /// @param withdrawal_credentials Commitment to a public key for withdrawals. + /// @param signature A BLS12-381 signature. + /// @param deposit_data_root The SHA-256 hash of the SSZ-encoded DepositData object. + /// Used as a protection against malformed input. + function deposit( + bytes calldata pubkey, + bytes calldata withdrawal_credentials, + bytes calldata signature, + bytes32 deposit_data_root + ) external payable; + + /// @notice Query the current deposit root hash. + /// @return The deposit root hash. + function get_deposit_root() external view returns (bytes32); + + /// @notice Query the current deposit count. + /// @return The deposit count encoded as a little endian 64-bit number. + function get_deposit_count() external view returns (bytes memory); +} + +// Based on official specification in https://eips.ethereum.org/EIPS/eip-165 +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceId The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceId` and + /// `interfaceId` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceId) external pure returns (bool); +} + +// This is a rewrite of the Vyper Eth2.0 deposit contract in Solidity. +// It tries to stay as close as possible to the original source code. +/// @notice This is the Ethereum 2.0 deposit contract interface. +/// For more information see the Phase 0 specification under https://github.com/ethereum/eth2.0-specs +contract DepositContract is IDepositContract, ERC165 { + uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32; + // NOTE: this also ensures `deposit_count` will fit into 64-bits + uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1; + + bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] branch; + uint256 deposit_count; + + bytes32[DEPOSIT_CONTRACT_TREE_DEPTH] zero_hashes; + + constructor() public { + // Compute hashes in empty sparse Merkle tree + for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++) + zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height])); + } + + function get_deposit_root() override external view returns (bytes32) { + bytes32 node; + uint size = deposit_count; + for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) { + if ((size & 1) == 1) + node = sha256(abi.encodePacked(branch[height], node)); + else + node = sha256(abi.encodePacked(node, zero_hashes[height])); + size /= 2; + } + return sha256(abi.encodePacked( + node, + to_little_endian_64(uint64(deposit_count)), + bytes24(0) + )); + } + + function get_deposit_count() override external view returns (bytes memory) { + return to_little_endian_64(uint64(deposit_count)); + } + + function deposit( + bytes calldata pubkey, + bytes calldata withdrawal_credentials, + bytes calldata signature, + bytes32 deposit_data_root + ) override external payable { + // Extended ABI length checks since dynamic types are used. + require(pubkey.length == 48, "DepositContract: invalid pubkey length"); + require(withdrawal_credentials.length == 32, "DepositContract: invalid withdrawal_credentials length"); + require(signature.length == 96, "DepositContract: invalid signature length"); + + // Check deposit amount + require(msg.value >= 1 ether, "DepositContract: deposit value too low"); + require(msg.value % 1 gwei == 0, "DepositContract: deposit value not multiple of gwei"); + uint deposit_amount = msg.value / 1 gwei; + require(deposit_amount <= type(uint64).max, "DepositContract: deposit value too high"); + + // Emit `DepositEvent` log + bytes memory amount = to_little_endian_64(uint64(deposit_amount)); + emit DepositEvent( + pubkey, + withdrawal_credentials, + amount, + signature, + to_little_endian_64(uint64(deposit_count)) + ); + + // Compute deposit data root (`DepositData` hash tree root) + bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0))); + bytes32 signature_root = sha256(abi.encodePacked( + sha256(abi.encodePacked(signature[:64])), + sha256(abi.encodePacked(signature[64:], bytes32(0))) + )); + bytes32 node = sha256(abi.encodePacked( + sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)), + sha256(abi.encodePacked(amount, bytes24(0), signature_root)) + )); + + // Verify computed and expected deposit data roots match + require(node == deposit_data_root, "DepositContract: reconstructed DepositData does not match supplied deposit_data_root"); + + // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) + require(deposit_count < MAX_DEPOSIT_COUNT, "DepositContract: merkle tree full"); + + // Add deposit data root to Merkle tree (update a single `branch` node) + deposit_count += 1; + uint size = deposit_count; + for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH; height++) { + if ((size & 1) == 1) { + branch[height] = node; + return; + } + node = sha256(abi.encodePacked(branch[height], node)); + size /= 2; + } + // As the loop should always end prematurely with the `return` statement, + // this code should be unreachable. We assert `false` just to be safe. + assert(false); + } + + function supportsInterface(bytes4 interfaceId) override external pure returns (bool) { + return interfaceId == type(ERC165).interfaceId || interfaceId == type(IDepositContract).interfaceId; + } + + function to_little_endian_64(uint64 value) internal pure returns (bytes memory ret) { + ret = new bytes(8); + bytes8 bytesValue = bytes8(value); + // Byteswapping during copying to bytes. + ret[0] = bytesValue[7]; + ret[1] = bytesValue[6]; + ret[2] = bytesValue[5]; + ret[3] = bytesValue[4]; + ret[4] = bytesValue[3]; + ret[5] = bytesValue[2]; + ret[6] = bytesValue[1]; + ret[7] = bytesValue[0]; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// supportsInterface(bytes4): 0x0 -> 0 +// supportsInterface(bytes4): 0xffffffff00000000000000000000000000000000000000000000000000000000 -> false # defined to be false by ERC-165 # +// supportsInterface(bytes4): 0x01ffc9a700000000000000000000000000000000000000000000000000000000 -> true # ERC-165 id # +// supportsInterface(bytes4): 0x8564090700000000000000000000000000000000000000000000000000000000 -> true # the deposit interface id # +// get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e +// get_deposit_count() -> 0x20, 8, 0 +// # TODO: check balance and logs after each deposit # +// deposit(bytes,bytes,bytes,bytes32), 32 ether: 0 -> FAILURE # Empty input # +// get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e +// get_deposit_count() -> 0x20, 8, 0 +// deposit(bytes,bytes,bytes,bytes32), 1 ether: 0x80, 0xe0, 0x120, 0xaa4a8d0b7d9077248630f1a4701ae9764e42271d7f22b7838778411857fd349e, 0x30, 0x933ad9491b62059dd065b560d256d8957a8c402cc6e8d8ee7290ae11e8f73292, 0x67a8811c397529dac52ae1342ba58c9500000000000000000000000000000000, 0x20, 0x00f50428677c60f997aadeab24aabf7fceaef491c96a52b463ae91f95611cf71, 0x60, 0xa29d01cc8c6296a8150e515b5995390ef841dc18948aa3e79be6d7c1851b4cbb, 0x5d6ff49fa70b9c782399506a22a85193151b9b691245cebafd2063012443c132, 0x4b6c36debaedefb7b2d71b0503ffdc00150aaffd42e63358238ec888901738b8 -> # txhash: 0x7085c586686d666e8bb6e9477a0f0b09565b2060a11f1c4209d3a52295033832 # +// get_deposit_root() -> 0x2089653123d9c721215120b6db6738ba273bbc5228ac093b1f983badcdc8a438 +// get_deposit_count() -> 0x20, 8, 0x0100000000000000000000000000000000000000000000000000000000000000 +// deposit(bytes,bytes,bytes,bytes32), 32 ether: 0x80, 0xe0, 0x120, 0xdbd986dc85ceb382708cf90a3500f500f0a393c5ece76963ac3ed72eccd2c301, 0x30, 0xb2ce0f79f90e7b3a113ca5783c65756f96c4b4673c2b5c1eb4efc22280259441, 0x06d601211e8866dc5b50dc48a244dd7c00000000000000000000000000000000, 0x20, 0x00344b6c73f71b11c56aba0d01b7d8ad83559f209d0a4101a515f6ad54c89771, 0x60, 0x945caaf82d18e78c033927d51f452ebcd76524497b91d7a11219cb3db6a1d369, 0x7595fc095ce489e46b2ef129591f2f6d079be4faaf345a02c5eb133c072e7c56, 0x0c6c3617eee66b4b878165c502357d49485326bc6b31bc96873f308c8f19c09d -> # txhash: 0x404d8e109822ce448e68f45216c12cb051b784d068fbe98317ab8e50c58304ac # +// get_deposit_root() -> 0x40255975859377d912c53aa853245ebd939bdd2b33a28e084babdcc1ed8238ee +// get_deposit_count() -> 0x20, 8, 0x0200000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/fallback/falback_return.sol b/test/libsolidity/semanticTests/fallback/falback_return.sol new file mode 100644 index 000000000000..2868d85d0096 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/falback_return.sol @@ -0,0 +1,18 @@ +contract A { + uint public x; + fallback () external { + if (x == 2) return; + x++; + } +} +// ==== +// compileViaYul: also +// ---- +// () +// x() -> 1 +// () +// x() -> 2 +// () +// x() -> 2 +// () +// x() -> 2 diff --git a/test/libsolidity/semanticTests/fallback/fallback_argument.sol b/test/libsolidity/semanticTests/fallback/fallback_argument.sol new file mode 100644 index 000000000000..1e3433d93cb2 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_argument.sol @@ -0,0 +1,17 @@ +contract A { + uint public x; + fallback (bytes calldata _input) external returns (bytes memory) { + x = _input.length; + return ""; + } + function f() public returns (bool, bytes memory) { + (bool success, bytes memory retval) = address(this).call("abc"); + return (success, retval); + } +} +// ==== +// compileViaYul: also +// EVMVersion: >=byzantium +// ---- +// f() -> 0x01, 0x40, 0x00 +// x() -> 3 diff --git a/test/libsolidity/semanticTests/fallback/fallback_argument_to_storage.sol b/test/libsolidity/semanticTests/fallback/fallback_argument_to_storage.sol new file mode 100644 index 000000000000..1a995a8cd554 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_argument_to_storage.sol @@ -0,0 +1,16 @@ +contract A { + bytes public x; + fallback (bytes calldata _input) external returns (bytes memory) { + x = _input; + return ""; + } + function f() public returns (bool, bytes memory) { + (bool success, bytes memory retval) = address(this).call("abc"); + return (success, retval); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// f() -> 0x01, 0x40, 0x00 +// x() -> 0x20, 3, "abc" diff --git a/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol b/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol index 90efa8fdae1e..da8a7bc46a19 100644 --- a/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol +++ b/test/libsolidity/semanticTests/fallback/fallback_or_receive.sol @@ -7,6 +7,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0, 0 // () -> diff --git a/test/libsolidity/semanticTests/fallback/fallback_override.sol b/test/libsolidity/semanticTests/fallback/fallback_override.sol new file mode 100644 index 000000000000..c1099e151cfd --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_override.sol @@ -0,0 +1,19 @@ +contract A { + fallback (bytes calldata _input) virtual external returns (bytes memory) { + return _input; + } +} +contract B is A { + fallback (bytes calldata _input) override external returns (bytes memory) { + return "xyz"; + } + function f() public returns (bool, bytes memory) { + (bool success, bytes memory retval) = address(this).call("abc"); + return (success, retval); + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// f() -> 0x01, 0x40, 0x03, 0x78797a0000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/fallback/fallback_override2.sol b/test/libsolidity/semanticTests/fallback/fallback_override2.sol new file mode 100644 index 000000000000..e92163476d43 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_override2.sol @@ -0,0 +1,18 @@ +contract A { + fallback (bytes calldata _input) virtual external returns (bytes memory) { + return _input; + } +} +contract B is A { + fallback () override external { + } + function f() public returns (bool, bytes memory) { + (bool success, bytes memory retval) = address(this).call("abc"); + return (success, retval); + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// f() -> 1, 0x40, 0x00 diff --git a/test/libsolidity/semanticTests/fallback/fallback_override_multi.sol b/test/libsolidity/semanticTests/fallback/fallback_override_multi.sol new file mode 100644 index 000000000000..1e2b3a630b5e --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_override_multi.sol @@ -0,0 +1,22 @@ +contract A { + fallback (bytes calldata _input) virtual external returns (bytes memory) { + return _input; + } +} +contract B { + fallback (bytes calldata _input) virtual external returns (bytes memory) { + return "xyz"; + } +} +contract C is B, A { + fallback () external override (B, A) {} + function f() public returns (bool, bytes memory) { + (bool success, bytes memory retval) = address(this).call("abc"); + return (success, retval); + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// ---- +// f() -> 0x01, 0x40, 0x00 diff --git a/test/libsolidity/semanticTests/fallback/fallback_return_data.sol b/test/libsolidity/semanticTests/fallback/fallback_return_data.sol new file mode 100644 index 000000000000..130212613261 --- /dev/null +++ b/test/libsolidity/semanticTests/fallback/fallback_return_data.sol @@ -0,0 +1,14 @@ +contract A { + fallback (bytes calldata _input) external returns (bytes memory) { + return _input; + } + function f() public returns (bool, bytes memory) { + (bool success, bytes memory retval) = address(this).call("abc"); + return (success, retval); + } +} +// ==== +// compileViaYul: also +// EVMVersion: >=byzantium +// ---- +// f() -> 0x01, 0x40, 0x03, 0x6162630000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol b/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol index 57dbb37bbd1a..fea3c8748f52 100644 --- a/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol +++ b/test/libsolidity/semanticTests/fallback/short_data_calls_fallback.sol @@ -6,6 +6,7 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // (): hex"d88e0b" // x() -> 2 diff --git a/test/libsolidity/semanticTests/freeFunctions/easy.sol b/test/libsolidity/semanticTests/freeFunctions/easy.sol new file mode 100644 index 000000000000..d2fadf08ae83 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/easy.sol @@ -0,0 +1,14 @@ +function add(uint a, uint b) pure returns (uint) { + return a + b; +} + +contract C { + function f(uint x) public pure returns (uint) { + return add(x, 2); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint256): 7 -> 9 diff --git a/test/libsolidity/semanticTests/freeFunctions/free_namesake_contract_function.sol b/test/libsolidity/semanticTests/freeFunctions/free_namesake_contract_function.sol new file mode 100644 index 000000000000..88cd5f4d0f1b --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/free_namesake_contract_function.sol @@ -0,0 +1,11 @@ +function f() pure returns (uint) { return 1337; } +contract C { + function f() public pure returns (uint) { + return f(); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> FAILURE diff --git a/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol new file mode 100644 index 000000000000..5c2e2f909a1b --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol @@ -0,0 +1,17 @@ +contract C { + uint public x = 2; +} + +function test() returns (bool) { + return type(C).runtimeCode.length > 20; +} + +contract D { + function f() public returns (bool) { + return test(); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/freeFunctions/import.sol b/test/libsolidity/semanticTests/freeFunctions/import.sol new file mode 100644 index 000000000000..47625f91d361 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/import.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +struct S { uint x; } +function set(S storage a, uint v) { a.x = v; } + +==== Source: B ==== +import "A"; +import "A" as A; +contract C { + A.S data; + function f(uint v) public returns (uint one, uint two) { + A.set(data, v); + one = data.x; + set(data, v + 1); + two = data.x; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint256): 7 -> 7, 8 diff --git a/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol new file mode 100644 index 000000000000..861313e923b2 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol @@ -0,0 +1,23 @@ +library L { + function pub() public pure returns (uint) { + return 7; + } + function inter() internal pure returns (uint) { + return 8; + } +} + +function fu() pure returns (uint, uint) { + return (L.pub(), L.inter()); +} + +contract C { + function f() public pure returns (uint, uint) { + return fu(); + } +} +// ==== +// compileViaYul: also +// ---- +// library: L +// f() -> 7, 8 diff --git a/test/libsolidity/semanticTests/freeFunctions/new_operator.sol b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol new file mode 100644 index 000000000000..d0eea9f9697a --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol @@ -0,0 +1,17 @@ +contract C { + uint public x = 2; +} + +function test() returns (uint) { + return (new C()).x(); +} + +contract D { + function f() public returns (uint) { + return test(); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/freeFunctions/overloads.sol b/test/libsolidity/semanticTests/freeFunctions/overloads.sol new file mode 100644 index 000000000000..70bb6031eeab --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/overloads.sol @@ -0,0 +1,17 @@ +function f(uint) returns (uint) { + return 2; +} +function f(string memory) returns (uint) { + return 3; +} + +contract C { + function g() public returns (uint, uint) { + return (f(2), f("abc")); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g() -> 2, 3 diff --git a/test/libsolidity/semanticTests/freeFunctions/recursion.sol b/test/libsolidity/semanticTests/freeFunctions/recursion.sol new file mode 100644 index 000000000000..2ce2d46e4493 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/recursion.sol @@ -0,0 +1,24 @@ +function exp(uint base, uint exponent) pure returns (uint power) { + if (exponent == 0) + return 1; + power = exp(base, exponent / 2); + power *= power; + if (exponent & 1 == 1) + power *= base; +} + +contract C { + function g(uint base, uint exponent) public pure returns (uint) { + return exp(base, exponent); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g(uint256,uint256): 0, 0 -> 1 +// g(uint256,uint256): 0, 1 -> 0x00 +// g(uint256,uint256): 1, 0 -> 1 +// g(uint256,uint256): 2, 3 -> 8 +// g(uint256,uint256): 3, 10 -> 59049 +// g(uint256,uint256): 2, 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 diff --git a/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol new file mode 100644 index 000000000000..3b94b06fce77 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol @@ -0,0 +1,17 @@ +contract C { + uint[] data; + function f(uint x, uint[] calldata input) public returns (uint, uint) { + data.push(x); + (uint a, uint[] calldata b) = fun(input, data); + return (a, b[1]); + + } +} + +function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256[]): 7, 0x40, 3, 8, 9, 10 -> 7, 9 diff --git a/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol b/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol index 52ddfff98e78..cd76d2c51333 100644 --- a/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol +++ b/test/libsolidity/semanticTests/functionCall/array_multiple_local_vars.sol @@ -24,6 +24,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256[]): 32, 3, 1000, 1, 2 -> 3 // f(uint256[]): 32, 3, 100, 500, 300 -> 600 diff --git a/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol b/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol index 5d93ba0ee7dd..d8046c43d75a 100644 --- a/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol +++ b/test/libsolidity/semanticTests/functionCall/call_function_returning_function.sol @@ -24,5 +24,6 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/call_function_returning_nothing_via_pointer.sol b/test/libsolidity/semanticTests/functionCall/call_function_returning_nothing_via_pointer.sol index 569d185bdc2a..9876098553a1 100644 --- a/test/libsolidity/semanticTests/functionCall/call_function_returning_nothing_via_pointer.sol +++ b/test/libsolidity/semanticTests/functionCall/call_function_returning_nothing_via_pointer.sol @@ -14,6 +14,7 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true // flag() -> true diff --git a/test/libsolidity/semanticTests/functionCall/call_internal_function_via_expression.sol b/test/libsolidity/semanticTests/functionCall/call_internal_function_via_expression.sol index ebe52b3e578d..53b1e452cb81 100644 --- a/test/libsolidity/semanticTests/functionCall/call_internal_function_via_expression.sol +++ b/test/libsolidity/semanticTests/functionCall/call_internal_function_via_expression.sol @@ -19,6 +19,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // associated() -> 42 // unassociated() -> 42 diff --git a/test/libsolidity/semanticTests/functionCall/call_options_overload.sol b/test/libsolidity/semanticTests/functionCall/call_options_overload.sol index 8938d8f0c318..0881c5bac366 100644 --- a/test/libsolidity/semanticTests/functionCall/call_options_overload.sol +++ b/test/libsolidity/semanticTests/functionCall/call_options_overload.sol @@ -5,8 +5,9 @@ contract C { v = this.f{value: 10}(2); x = this.f{gas: 1000}(2, 3); y = this.f{gas: 1000, value: 10}(2, 3); - z = this.f{gas: 1000}{value: 10}(2, 3); + z = this.f{value: 10, gas: 1000}(2, 3); } + function bal() external returns (uint) { return address(this).balance; } receive() external payable {} } // ==== @@ -14,3 +15,4 @@ contract C { // ---- // (), 1 ether // call() -> 1, 2, 2, 2 +// bal() -> 1000000000000000000 diff --git a/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol b/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol index 1717794448d8..3db423933102 100644 --- a/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol +++ b/test/libsolidity/semanticTests/functionCall/calling_nonexisting_contract_throws.sol @@ -4,7 +4,7 @@ abstract contract D { contract C { - D d = D(0x1212); + D d = D(address(0x1212)); function f() public returns (uint256) { d.g(); @@ -24,6 +24,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> FAILURE // g() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/calling_other_functions.sol b/test/libsolidity/semanticTests/functionCall/calling_other_functions.sol new file mode 100644 index 000000000000..cf2195654547 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/calling_other_functions.sol @@ -0,0 +1,23 @@ +contract collatz { + function run(uint x) public returns(uint y) { + while ((y = x) > 1) { + if (x % 2 == 0) x = evenStep(x); + else x = oddStep(x); + } + } + function evenStep(uint x) public returns(uint y) { + return x / 2; + } + function oddStep(uint x) public returns(uint y) { + return 3 * x + 1; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// run(uint256): 0 -> 0 +// run(uint256): 1 -> 1 +// run(uint256): 2 -> 1 +// run(uint256): 8 -> 1 +// run(uint256): 127 -> 1 diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol index c074d8598689..ce36b2cd07cf 100644 --- a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function.sol @@ -13,6 +13,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// intern() -> FAILURE # This should throw exceptions # +// intern() -> FAILURE, hex"4e487b71", 0x51 # This should throw exceptions # // extern() -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol index 0ae431b24b83..8fb2bfc8c0a3 100644 --- a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_in_detail.sol @@ -16,5 +16,8 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- -// t() -> FAILURE +// t() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol index 44af5be40928..26e2a6e4a8a1 100644 --- a/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol +++ b/test/libsolidity/semanticTests/functionCall/calling_uninitialized_function_through_array.sol @@ -17,5 +17,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// t() -> FAILURE +// t() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/functionCall/conditional_with_arguments.sol b/test/libsolidity/semanticTests/functionCall/conditional_with_arguments.sol index f5e2957c9bc2..db30454f0607 100644 --- a/test/libsolidity/semanticTests/functionCall/conditional_with_arguments.sol +++ b/test/libsolidity/semanticTests/functionCall/conditional_with_arguments.sol @@ -8,5 +8,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol index 0a80a9f5c7bf..9fa72aeaa3d3 100644 --- a/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol +++ b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol @@ -22,17 +22,17 @@ contract C { } } // ==== -// compileViaYul: also // EVMVersion: >=byzantium +// compileViaYul: also // ---- // get() -> 0x00 // assert0_delegated() -> 0x01, 0x40, 0x0 // get_delegated() -> 0x01, 0x40, 0x20, 0x0 // set(uint256): 0x01 -> // get() -> 0x01 -// assert0_delegated() -> 0x00, 0x40, 0x0 +// assert0_delegated() -> 0x00, 0x40, 0x24, 0x4e487b7100000000000000000000000000000000000000000000000000000000, 0x0100000000000000000000000000000000000000000000000000000000 // get_delegated() -> 0x01, 0x40, 0x20, 0x1 // set(uint256): 0x2a -> // get() -> 0x2a -// assert0_delegated() -> 0x00, 0x40, 0x0 +// assert0_delegated() -> 0x00, 0x40, 0x24, 0x4e487b7100000000000000000000000000000000000000000000000000000000, 0x0100000000000000000000000000000000000000000000000000000000 // get_delegated() -> 0x01, 0x40, 0x20, 0x2a diff --git a/test/libsolidity/semanticTests/functionCall/disordered_named_args.sol b/test/libsolidity/semanticTests/functionCall/disordered_named_args.sol index 2d16f515db1b..ced50b905652 100644 --- a/test/libsolidity/semanticTests/functionCall/disordered_named_args.sol +++ b/test/libsolidity/semanticTests/functionCall/disordered_named_args.sol @@ -4,5 +4,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // b() -> 123 diff --git a/test/libsolidity/semanticTests/functionCall/external_function.sol b/test/libsolidity/semanticTests/functionCall/external_function.sol index 3625864477f0..8aea66b0feac 100644 --- a/test/libsolidity/semanticTests/functionCall/external_function.sol +++ b/test/libsolidity/semanticTests/functionCall/external_function.sol @@ -14,5 +14,6 @@ contract c { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test(uint256,uint256): 2, 3 -> 9, 3 diff --git a/test/libsolidity/semanticTests/functionCall/external_public_override.sol b/test/libsolidity/semanticTests/functionCall/external_public_override.sol index 7909f8e324a3..e99d7e381c50 100644 --- a/test/libsolidity/semanticTests/functionCall/external_public_override.sol +++ b/test/libsolidity/semanticTests/functionCall/external_public_override.sol @@ -17,6 +17,7 @@ contract B is A { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2 // g() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/file_level_call_via_module.sol b/test/libsolidity/semanticTests/functionCall/file_level_call_via_module.sol new file mode 100644 index 000000000000..09891abe2180 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/file_level_call_via_module.sol @@ -0,0 +1,16 @@ +==== Source: a.sol ==== +function f(uint) pure returns (uint) { return 7; } +function f(bytes memory x) pure returns (uint) { return x.length; } +==== Source: b.sol ==== +import "a.sol" as M; +contract C { + function f() public pure returns (uint, uint) { + return (M.f(2), M.f("abc")); + + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 7, 3 diff --git a/test/libsolidity/semanticTests/functionCall/base_base_overload.sol b/test/libsolidity/semanticTests/functionCall/inheritance/base_base_overload.sol similarity index 97% rename from test/libsolidity/semanticTests/functionCall/base_base_overload.sol rename to test/libsolidity/semanticTests/functionCall/inheritance/base_base_overload.sol index c1ba1c1ffaed..094b2f413440 100644 --- a/test/libsolidity/semanticTests/functionCall/base_base_overload.sol +++ b/test/libsolidity/semanticTests/functionCall/inheritance/base_base_overload.sol @@ -36,6 +36,7 @@ contract Child is Base { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // x() -> 0 // y() -> 0 diff --git a/test/libsolidity/semanticTests/functionCall/base_overload.sol b/test/libsolidity/semanticTests/functionCall/inheritance/base_overload.sol similarity index 95% rename from test/libsolidity/semanticTests/functionCall/base_overload.sol rename to test/libsolidity/semanticTests/functionCall/inheritance/base_overload.sol index be4a73809121..4413128ccf9c 100644 --- a/test/libsolidity/semanticTests/functionCall/base_overload.sol +++ b/test/libsolidity/semanticTests/functionCall/inheritance/base_overload.sol @@ -20,6 +20,7 @@ contract Child is Base { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // x() -> 0 // y() -> 0 diff --git a/test/libsolidity/semanticTests/functionCall/inheritance/call_base.sol b/test/libsolidity/semanticTests/functionCall/inheritance/call_base.sol new file mode 100644 index 000000000000..b459cf338425 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/inheritance/call_base.sol @@ -0,0 +1,16 @@ +contract Base { + function f(uint n) public returns (uint) { + return 2 * n; + } +} + +contract Child is Base { + function g(uint n) public returns (uint) { + return f(n); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g(uint256): 4 -> 8 diff --git a/test/libsolidity/semanticTests/functionCall/inheritance/call_base_base.sol b/test/libsolidity/semanticTests/functionCall/inheritance/call_base_base.sol new file mode 100644 index 000000000000..aab57ccbc88d --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/inheritance/call_base_base.sol @@ -0,0 +1,30 @@ +contract BaseBase { + function f(uint n) public virtual returns (uint) { + return 2 * n; + } + function s(uint n) public returns (uint) { + return 4 * n; + } +} + +contract Base is BaseBase { + function f(uint n) public virtual override returns (uint) { + return 3 * n; + } +} + +contract Child is Base { + function g(uint n) public returns (uint) { + return f(n); + } + + function h(uint n) public returns (uint) { + return s(n); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g(uint256): 4 -> 12 +// h(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/functionCall/inheritance/call_base_base_explicit.sol b/test/libsolidity/semanticTests/functionCall/inheritance/call_base_base_explicit.sol new file mode 100644 index 000000000000..52cf221395a5 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/inheritance/call_base_base_explicit.sol @@ -0,0 +1,33 @@ +contract BaseBase { + function f(uint n) public virtual returns (uint) { + return 2 * n; + } + + function s(uint n) public returns (uint) { + return 4 * n; + } +} + +contract Base is BaseBase { + function f(uint n) public virtual override returns (uint) { + return 3 * n; + } +} + +contract Child is Base { + function g(uint n) public returns (uint) { + // calling base-bse function of a virtual overridden function. + return BaseBase.f(n); + } + + function k(uint n) public returns (uint) { + // Calling base-base function of a non-virtual function. + return BaseBase.s(n); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g(uint256): 4 -> 8 +// k(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/functionCall/inheritance/call_base_explicit.sol b/test/libsolidity/semanticTests/functionCall/inheritance/call_base_explicit.sol new file mode 100644 index 000000000000..766be624d6b8 --- /dev/null +++ b/test/libsolidity/semanticTests/functionCall/inheritance/call_base_explicit.sol @@ -0,0 +1,16 @@ +contract Base { + function f(uint n) public returns (uint) { + return 2 * n; + } +} + +contract Child is Base { + function g(uint n) public returns (uint) { + return Base.f(n); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g(uint256): 4 -> 8 diff --git a/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol b/test/libsolidity/semanticTests/functionCall/inheritance/call_unimplemented_base.sol similarity index 92% rename from test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol rename to test/libsolidity/semanticTests/functionCall/inheritance/call_unimplemented_base.sol index 862f37f8d02f..f5fd46d94238 100644 --- a/test/libsolidity/semanticTests/functionCall/call_unimplemented_base.sol +++ b/test/libsolidity/semanticTests/functionCall/inheritance/call_unimplemented_base.sol @@ -12,5 +12,6 @@ contract C is V } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // b() -> 42 diff --git a/test/libsolidity/semanticTests/functionCall/mapping_internal_return.sol b/test/libsolidity/semanticTests/functionCall/mapping_internal_return.sol index 7756a92b10c9..b152298b98e9 100644 --- a/test/libsolidity/semanticTests/functionCall/mapping_internal_return.sol +++ b/test/libsolidity/semanticTests/functionCall/mapping_internal_return.sol @@ -17,6 +17,8 @@ contract test { return (a[0], a[1], a[2], b[0], b[1], b[2]); } } +// ==== +// compileViaYul: also // ---- // g() -> 0, 42, 0, 0, 84, 21 // h() -> 0, 42, 0, 0, 84, 17 diff --git a/test/libsolidity/semanticTests/functionCall/member_accessors.sol b/test/libsolidity/semanticTests/functionCall/member_accessors.sol index d7b2b3284160..f155d2c60d9a 100644 --- a/test/libsolidity/semanticTests/functionCall/member_accessors.sol +++ b/test/libsolidity/semanticTests/functionCall/member_accessors.sol @@ -13,8 +13,9 @@ contract test { uint256 super_secret_data; } // ==== -// allowNonExistingFunctions: true // compileViaYul: also +// compileToEwasm: also +// allowNonExistingFunctions: true // ---- // data() -> 8 // name() -> "Celina" diff --git a/test/libsolidity/semanticTests/functionCall/multiple_functions.sol b/test/libsolidity/semanticTests/functionCall/multiple_functions.sol index e72922d7b144..863db5b0b5e2 100644 --- a/test/libsolidity/semanticTests/functionCall/multiple_functions.sol +++ b/test/libsolidity/semanticTests/functionCall/multiple_functions.sol @@ -5,8 +5,9 @@ contract test { function f() public returns(uint n) { return 3; } } // ==== -// allowNonExistingFunctions: true // compileViaYul: also +// compileToEwasm: also +// allowNonExistingFunctions: true // ---- // a() -> 0 // b() -> 1 diff --git a/test/libsolidity/semanticTests/functionCall/multiple_return_values.sol b/test/libsolidity/semanticTests/functionCall/multiple_return_values.sol index d3cab0dac8d8..cbcb4b605e62 100644 --- a/test/libsolidity/semanticTests/functionCall/multiple_return_values.sol +++ b/test/libsolidity/semanticTests/functionCall/multiple_return_values.sol @@ -5,5 +5,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // run(bool,uint256): true, 0xcd -> 0xcd, true, 0 diff --git a/test/libsolidity/semanticTests/functionCall/named_args.sol b/test/libsolidity/semanticTests/functionCall/named_args.sol index e959eba44c95..591159be78ee 100644 --- a/test/libsolidity/semanticTests/functionCall/named_args.sol +++ b/test/libsolidity/semanticTests/functionCall/named_args.sol @@ -1,8 +1,11 @@ contract test { function a(uint a, uint b, uint c) public returns (uint r) { r = a * 100 + b * 10 + c * 1; } function b() public returns (uint r) { r = a({a: 1, b: 2, c: 3}); } + function c() public returns (uint r) { r = a({b: 2, c: 3, a: 1}); } } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // b() -> 123 +// c() -> 123 diff --git a/test/libsolidity/semanticTests/functionCall/named_args_overload.sol b/test/libsolidity/semanticTests/functionCall/named_args_overload.sol index b77e5be6966a..d5505c87b62e 100644 --- a/test/libsolidity/semanticTests/functionCall/named_args_overload.sol +++ b/test/libsolidity/semanticTests/functionCall/named_args_overload.sol @@ -20,15 +20,19 @@ contract C { return f({b: 1, a: 2}); if (num == 3) return f({c: 1, a: 2, b: 3}); + if (num == 4) + return f({b: 5, c: 1, a: 2}); return 500; } } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // call(uint256): 0 -> 0 // call(uint256): 1 -> 1 // call(uint256): 2 -> 3 // call(uint256): 3 -> 6 -// call(uint256): 4 -> 500 +// call(uint256): 4 -> 8 +// call(uint256): 5 -> 500 diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol index bb1b28b3ede1..059d0878c1d8 100644 --- a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -10,12 +10,13 @@ contract Main { function s() public returns (bool) { Receiver r = new Receiver(); - return address(r).send(0); + return payable(r).send(0); } } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // constructor(), 20 wei -> // s() -> true diff --git a/test/libsolidity/semanticTests/functionCall/transaction_status.sol b/test/libsolidity/semanticTests/functionCall/transaction_status.sol index 449ebacbe70f..aa079ba4abbc 100644 --- a/test/libsolidity/semanticTests/functionCall/transaction_status.sol +++ b/test/libsolidity/semanticTests/functionCall/transaction_status.sol @@ -5,7 +5,8 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> // g() -> FAILURE -// h() -> FAILURE +// h() -> FAILURE, hex"4e487b71", 0x01 diff --git a/test/libsolidity/semanticTests/functionSelector/function_selector_via_contract_name.sol b/test/libsolidity/semanticTests/functionSelector/function_selector_via_contract_name.sol index b6a666d5b9a2..ee484b244716 100644 --- a/test/libsolidity/semanticTests/functionSelector/function_selector_via_contract_name.sol +++ b/test/libsolidity/semanticTests/functionSelector/function_selector_via_contract_name.sol @@ -17,6 +17,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test1() -> left(0x26121ff0), left(0xe420264a), left(0x26121ff0), left(0xe420264a) // test2() -> left(0x26121ff0), left(0xe420264a), left(0x26121ff0), left(0xe420264a) diff --git a/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol b/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol index 96a66fce2969..018aa91a99e6 100644 --- a/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol +++ b/test/libsolidity/semanticTests/functionTypes/function_delete_stack.sol @@ -12,5 +12,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// test() -> FAILURE +// test() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol b/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol index 6c7ecf4f693c..501f33cf2398 100644 --- a/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol +++ b/test/libsolidity/semanticTests/functionTypes/function_delete_storage.sol @@ -20,8 +20,11 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // set() -> 7 // ca() -> 7 // d() -> 1 -// ca() -> FAILURE +// ca() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/functionTypes/inline_array_with_value_call_option.sol b/test/libsolidity/semanticTests/functionTypes/inline_array_with_value_call_option.sol index e65da6e1e76f..3b9a70aa14e2 100644 --- a/test/libsolidity/semanticTests/functionTypes/inline_array_with_value_call_option.sol +++ b/test/libsolidity/semanticTests/functionTypes/inline_array_with_value_call_option.sol @@ -6,5 +6,7 @@ contract C { return [this.f, this.g][0]{value: 1}(); } } +// ==== +// compileViaYul: also // ---- // h(), 1 ether -> 1 diff --git a/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol b/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol index 353844723c23..64f55a420485 100644 --- a/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol +++ b/test/libsolidity/semanticTests/functionTypes/mapping_of_functions.sol @@ -25,6 +25,8 @@ contract Flow { } } +// ==== +// compileViaYul: also // ---- // success() -> false // f() -> 7 diff --git a/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol index 0acbcc5858d9..b04025cf962f 100644 --- a/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol +++ b/test/libsolidity/semanticTests/functionTypes/pass_function_types_internally.sol @@ -11,5 +11,8 @@ contract C { return x + 1; } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 7 -> 8 diff --git a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol index dc0731485985..80bd929a0639 100644 --- a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol +++ b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime.sol @@ -15,6 +15,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // runtime(uint256): 3 -> 6 // initial() -> 4 diff --git a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol index a778589abff0..de02c07c0d40 100644 --- a/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol +++ b/test/libsolidity/semanticTests/functionTypes/same_function_in_construction_and_runtime_equality_check.sol @@ -14,5 +14,8 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // test() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index a09404fce004..d7b285802377 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -24,5 +24,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // t() -> 9 diff --git a/test/libsolidity/semanticTests/functionTypes/struct_with_external_function.sol b/test/libsolidity/semanticTests/functionTypes/struct_with_external_function.sol new file mode 100644 index 000000000000..19086df7a2af --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/struct_with_external_function.sol @@ -0,0 +1,34 @@ +struct S { + uint16 a; + function() external returns (uint) x; + uint16 b; +} +contract Flow { + S[2] t; + + function X() public pure returns (uint) { + return 1; + } + + function Y() public pure returns (uint) { + return 2; + } + + constructor() { + t[0].a = 0xff07; + t[0].b = 0xff07; + t[1].x = this.Y; + t[1].a = 0xff07; + t[1].b = 0xff07; + t[0].x = this.X; + } + + function f() public returns (uint, uint) { + return (t[0].x(), t[1].x()); + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 2 diff --git a/test/libsolidity/semanticTests/functionTypes/struct_with_functions.sol b/test/libsolidity/semanticTests/functionTypes/struct_with_functions.sol new file mode 100644 index 000000000000..14a9ff190a7a --- /dev/null +++ b/test/libsolidity/semanticTests/functionTypes/struct_with_functions.sol @@ -0,0 +1,35 @@ +struct S { + uint16 a; + function() returns (uint) x; + uint16 b; +} +contract Flow { + S[2] t; + + function X() internal pure returns (uint) { + return 1; + } + + function Y() internal pure returns (uint) { + return 2; + } + + constructor() { + t[0].a = 0xff07; + t[0].b = 0xff07; + t[1].x = Y; + t[1].a = 0xff07; + t[1].b = 0xff07; + t[0].x = X; + } + + function f() public returns (uint, uint) { + return (t[0].x(), t[1].x()); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 1, 2 diff --git a/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol index ca1055fd6e2b..7d77355eb7eb 100644 --- a/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol +++ b/test/libsolidity/semanticTests/functionTypes/uninitialized_internal_storage_function_call.sol @@ -7,5 +7,8 @@ contract Test { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- -// f() -> FAILURE +// f() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/getters/array_mapping_struct.sol b/test/libsolidity/semanticTests/getters/array_mapping_struct.sol index d824897e8326..4aba178566b2 100644 --- a/test/libsolidity/semanticTests/getters/array_mapping_struct.sol +++ b/test/libsolidity/semanticTests/getters/array_mapping_struct.sol @@ -18,6 +18,8 @@ contract C { n[1][1].b = 10; } } +// ==== +// compileViaYul: also // ---- // m(uint256,uint256): 0, 0 -> 0x00, 0x00 // m(uint256,uint256): 1, 0 -> 1, 2 diff --git a/test/libsolidity/semanticTests/getters/mapping_array_struct.sol b/test/libsolidity/semanticTests/getters/mapping_array_struct.sol index 60683dfc0a0f..01c15320a70e 100644 --- a/test/libsolidity/semanticTests/getters/mapping_array_struct.sol +++ b/test/libsolidity/semanticTests/getters/mapping_array_struct.sol @@ -16,6 +16,8 @@ contract C { n[1][1].b = 10; } } +// ==== +// compileViaYul: also // ---- // m(uint256,uint256): 0, 0 -> FAILURE // m(uint256,uint256): 1, 0 -> 1, 2 diff --git a/test/libsolidity/semanticTests/getters/small_types.sol b/test/libsolidity/semanticTests/getters/small_types.sol deleted file mode 100644 index a26eba785ba3..000000000000 --- a/test/libsolidity/semanticTests/getters/small_types.sol +++ /dev/null @@ -1,19 +0,0 @@ -contract C { - uint8 public a; - uint16 public b; - uint128 public c; - uint public d; - constructor() { - a = 3; - b = 4; - c = 5; - d = 6; - } -} -// ==== -// compileViaYul: also -// ---- -// a() -> 3 -// b() -> 4 -// c() -> 5 -// d() -> 6 diff --git a/test/libsolidity/semanticTests/getters/string_and_bytes.sol b/test/libsolidity/semanticTests/getters/string_and_bytes.sol new file mode 100644 index 000000000000..731d5678c89e --- /dev/null +++ b/test/libsolidity/semanticTests/getters/string_and_bytes.sol @@ -0,0 +1,14 @@ +contract C { + string public a; + string public b; + bytes public c; + constructor() { + a = "hello world"; + b = hex"41424344"; + c = hex"ff077fff"; + } +} +// ---- +// a() -> 0x20, 11, "hello world" +// b() -> 0x20, 4, "ABCD" +// c() -> 0x20, 4, -439061522557375173052089223601630338202760422010735733633791622124826263552 diff --git a/test/libsolidity/semanticTests/getters/value_types.sol b/test/libsolidity/semanticTests/getters/value_types.sol new file mode 100644 index 000000000000..3b0cbaa934ed --- /dev/null +++ b/test/libsolidity/semanticTests/getters/value_types.sol @@ -0,0 +1,35 @@ +contract C { + uint8 public a; + uint16 public b; + uint128 public c; + uint public d; + bytes1 public e; + bytes20 public f; + bytes32 public g; + bool public h; + address public i; + constructor() { + a = 3; + b = 4; + c = 5; + d = 6; + e = bytes1(uint8(0x7f)); + f = bytes20(uint160(0x6465616462656566313564656164000000000010)); + g = bytes32(uint256(0x6465616462656566313564656164000000000000000000000000000000000010)); + h = true; + i = address(type(uint160).max / 3); + } +} +// ==== +// compileToEwasm: also +// compileViaYul: also +// ---- +// a() -> 3 +// b() -> 4 +// c() -> 5 +// d() -> 6 +// e() -> 0x7f00000000000000000000000000000000000000000000000000000000000000 +// f() -> 0x6465616462656566313564656164000000000010000000000000000000000000 +// g() -> 0x6465616462656566313564656164000000000000000000000000000000000010 +// h() -> true +// i() -> 0x5555555555555555555555555555555555555555 diff --git a/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol index 2ef12c04aa95..fd2300d43cb5 100644 --- a/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol +++ b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol @@ -12,6 +12,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >=tangerineWhistle // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol new file mode 100644 index 000000000000..91eff04e0915 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint[2][] calldata x) public returns (uint[2][] memory r) { + assembly { x.offset := 0x44 x.length := 2 } + r = x; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[2][]): 0x0, 1, 8, 7, 6, 5 -> 0x20, 2, 8, 7, 6, 5 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_array_read.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_read.sol new file mode 100644 index 000000000000..8c415b429881 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_read.sol @@ -0,0 +1,13 @@ +contract C { + function f(uint[2][] calldata x) public returns (uint o, uint l, uint s) { + assembly { l := x.length o := x.offset } + uint[2] calldata t = x[1]; + // statically-sized arrays only use one slot, so we read directly. + assembly { s := t } + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint256[2][]): 0x20, 2, 1, 2, 3, 4 -> 0x44, 2, 0x84 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_assign.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign.sol new file mode 100644 index 000000000000..f17dd975caa9 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign.sol @@ -0,0 +1,10 @@ +contract C { + function f(bytes calldata x) public returns (bytes memory) { + assembly { x.offset := 1 x.length := 3 } + return x; + } +} +// ==== +// compileViaYul: also +// ---- +// f(bytes): 0x20, 0, 0 -> 0x20, 3, 0x5754f80000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_assign_from_nowhere.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign_from_nowhere.sol new file mode 100644 index 000000000000..1b559f27b45f --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_assign_from_nowhere.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (bytes calldata x) { + assembly { x.offset := 0 x.length := 4 } + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x20, 4, 0x26121ff000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_length_read.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_length_read.sol new file mode 100644 index 000000000000..5cac714edee7 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_length_read.sol @@ -0,0 +1,19 @@ +contract C { + function lenBytesRead(bytes calldata x) public returns (uint l) { + assembly { l := x.length } + } + + function lenStringRead(string calldata x) public returns (uint l) { + assembly { l := x.length } + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// lenBytesRead(bytes): 0x20, 4, "abcd" -> 4 +// lenBytesRead(bytes): 0x20, 0, "abcd" -> 0x00 +// lenBytesRead(bytes): 0x20, 0x21, "abcd", "ef" -> 33 +// lenStringRead(string): 0x20, 4, "abcd" -> 4 +// lenStringRead(string): 0x20, 0, "abcd" -> 0x00 +// lenStringRead(string): 0x20, 0x21, "abcd", "ef" -> 33 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read.sol new file mode 100644 index 000000000000..10ce32ee043a --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read.sol @@ -0,0 +1,20 @@ +contract C { + function f(bytes calldata x) public returns (uint r) { + assembly { r := x.offset } + } + + function f(uint, bytes calldata x, uint) public returns (uint r, uint v) { + assembly { + r := x.offset + v := x.length + } + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(bytes): 0x20, 0, 0 -> 0x44 +// f(bytes): 0x22, 0, 0, 0 -> 0x46 +// f(uint256,bytes,uint256): 7, 0x60, 8, 2, 0 -> 0x84, 2 +// f(uint256,bytes,uint256): 0, 0, 0 -> 0x24, 0x00 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read_write.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read_write.sol new file mode 100644 index 000000000000..1ceed9c5e20f --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_offset_read_write.sol @@ -0,0 +1,19 @@ +contract C { + function f(uint, bytes calldata x, uint) public returns (uint r, uint v) { + assembly { + x.offset := 8 + x.length := 20 + } + assembly { + r := x.offset + v := x.length + } + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint256,bytes,uint256): 7, 0x60, 8, 2, 0 -> 8, 0x14 +// f(uint256,bytes,uint256): 0, 0, 0 -> 8, 0x14 diff --git a/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol b/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol index 835048b85eb9..317e92284172 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/constant_access.sol @@ -3,7 +3,7 @@ contract C { bytes2 constant b = 0xabcd; bytes3 constant c = "abc"; bool constant d = true; - address payable constant e = 0x1212121212121212121212121212121212121212; + address constant e = 0x1212121212121212121212121212121212121212; function f() public pure returns (uint w, bytes2 x, bytes3 y, bool z, address t) { assembly { w := a @@ -14,5 +14,8 @@ contract C { } } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2, left(0xabcd), left(0x616263), true, 0x1212121212121212121212121212121212121212 diff --git a/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol index d2e24a70eccd..5b1120211bfb 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/constant_access_referencing.sol @@ -10,8 +10,8 @@ contract C { bytes3 constant cccc = ccc; bool constant d = true; bool constant dd = d; - address payable constant e = 0x1212121212121212121212121212121212121212; - address payable constant ee = e; + address constant e = 0x1212121212121212121212121212121212121212; + address constant ee = e; function f() public pure returns (uint w, bytes2 x, bytes3 y, bool z, address t) { assembly { w := aaa @@ -22,5 +22,8 @@ contract C { } } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2, left(0xabcd), left(0x616263), true, 0x1212121212121212121212121212121212121212 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol index 3d31c1addfa3..93d63faf61f9 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_embedded_function_call.sol @@ -3,9 +3,9 @@ contract C { assembly { let d:= 0x10 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := g(a) - function g(r) - > s { + function g(r) -> s { s := mul(r, r) } y := g(b) @@ -23,5 +23,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x1, 0x4, 0x7, 0x10 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol index 451ecbbe50b8..3c5bb39d1dce 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for.sol @@ -18,6 +18,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0 -> 1 // f(uint256): 1 -> 1 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol index 2c05f1556086..54bad5007058 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_for2.sol @@ -23,6 +23,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0 -> 0, 2, 0 // f(uint256): 1 -> 1, 4, 3 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol index 17a271d5b333..44a89dae3549 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call.sol @@ -1,7 +1,7 @@ contract C { function f() public { assembly { - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 @@ -17,5 +17,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1, 2, 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol index 26d3d43b7def..985ae011c469 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call2.sol @@ -3,7 +3,7 @@ contract C { assembly { let d := 0x10 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 @@ -20,5 +20,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x1, 0x2, 0x7, 0x10 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol index 2dec9761bbfb..315fec9ba5ab 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_function_call_assignment.sol @@ -3,7 +3,7 @@ contract C { assembly { let a1, b1, c1 - function asmfun(a, b, c) - > x, y, z { + function asmfun(a, b, c) -> x, y, z { x := a y := b z := 7 @@ -19,5 +19,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1, 2, 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol index 9f94f3c1991b..e7ed170afff8 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_if.sol @@ -10,6 +10,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0 -> 0 // f(uint256): 1 -> 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol index 3c9f3a2f371f..70f19479af45 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_in_modifiers.sol @@ -11,9 +11,23 @@ contract C { function f() public m returns (bool) { return true; } + + modifier n { + uint256 a = 1; + assembly { + a := 2 + } + if (a != 2) + _; + revert(); + } + + function g() public n returns (bool) { + // This statement should never execute. + return true; + } } -// ==== -// compileViaYul: also // ---- // f() -> true +// g() -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol index 857af4243b6c..b3225413dd4b 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_memory_access.sol @@ -11,5 +11,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 0x20, 0x5, "12345" diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol index c48d749676cb..de77a82884b6 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_read_and_write_stack.sol @@ -9,5 +9,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 45 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol index a5e3c4675661..3ddfde121c35 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_recursion.sol @@ -20,6 +20,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0 -> 1 // f(uint256): 1 -> 1 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol index 3dfdcbc66e9d..9da111f4be74 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access.sol @@ -18,6 +18,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true // z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol index c7707aebe1dc..80affa2ef425 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_inside_function.sol @@ -19,6 +19,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true // z() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol new file mode 100644 index 000000000000..9f655309121f --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_local_var.sol @@ -0,0 +1,19 @@ +contract C { + uint256[] public a; + + function f() public returns (uint256) { + uint256[] storage x = a; + uint256 off; + assembly { + sstore(x.slot, 7) + off := x.offset + } + assert(off == 0); + return a.length; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol index 1837098350b9..81c6f5bb6148 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_storage_access_via_pointer.sol @@ -18,6 +18,9 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> true // a() -> 7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol index 9b9a76109c7a..3b98c93f6b88 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_switch.sol @@ -17,6 +17,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0 -> 2 // f(uint256): 1 -> 8 diff --git a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol index fa981fbd3352..c0d91f24e441 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inline_assembly_write_to_stack.sol @@ -9,5 +9,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 7, "abcdef" diff --git a/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol b/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol index 250e0ce8a0c6..abf9916c9c90 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/inlineasm_empty_let.sol @@ -11,5 +11,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/leave.sol b/test/libsolidity/semanticTests/inlineAssembly/leave.sol index b8c0ccda2a1f..d884b9c678ca 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/leave.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/leave.sol @@ -12,5 +12,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol b/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol index abfc6d09be32..a1f9874eb7bb 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/slot_access.sol @@ -25,6 +25,8 @@ contract C { return data().a; } } +// ==== +// compileViaYul: also // ---- // get() -> 0 // mappingAccess(uint256): 1 -> 0, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/truefalse.sol b/test/libsolidity/semanticTests/inlineAssembly/truefalse.sol index c10cdd65aa32..d46770cf3e0c 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/truefalse.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/truefalse.sol @@ -8,5 +8,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1, 0 diff --git a/test/libsolidity/semanticTests/integer/basic.sol b/test/libsolidity/semanticTests/integer/basic.sol index 7c31e3888594..a6bca57edbe5 100644 --- a/test/libsolidity/semanticTests/integer/basic.sol +++ b/test/libsolidity/semanticTests/integer/basic.sol @@ -20,5 +20,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // basic() -> true diff --git a/test/libsolidity/semanticTests/integer/int.sol b/test/libsolidity/semanticTests/integer/int.sol index 79d685645fb2..9c34d9560d1f 100644 --- a/test/libsolidity/semanticTests/integer/int.sol +++ b/test/libsolidity/semanticTests/integer/int.sol @@ -234,6 +234,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // intMinA() -> true // intMinB() -> true diff --git a/test/libsolidity/semanticTests/integer/many_local_variables.sol b/test/libsolidity/semanticTests/integer/many_local_variables.sol new file mode 100644 index 000000000000..cfa122e77ee4 --- /dev/null +++ b/test/libsolidity/semanticTests/integer/many_local_variables.sol @@ -0,0 +1,12 @@ +contract test { + function run(uint x1, uint x2, uint x3) public returns(uint y) { + uint8 a = 0x1; uint8 b = 0x10; uint16 c = 0x100; + y = a + b + c + x1 + x2 + x3; + y += b + x2; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// run(uint256,uint256,uint256): 0x1000, 0x10000, 0x100000 -> 0x121121 diff --git a/test/libsolidity/semanticTests/integer/small_signed_types.sol b/test/libsolidity/semanticTests/integer/small_signed_types.sol new file mode 100644 index 000000000000..41a15f733961 --- /dev/null +++ b/test/libsolidity/semanticTests/integer/small_signed_types.sol @@ -0,0 +1,10 @@ +contract test { + function run() public returns(int256 y) { + return -int32(10) * -int64(20); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// run() -> 200 diff --git a/test/libsolidity/semanticTests/integer/uint.sol b/test/libsolidity/semanticTests/integer/uint.sol index a2f3746c26a7..4f76f838c861 100644 --- a/test/libsolidity/semanticTests/integer/uint.sol +++ b/test/libsolidity/semanticTests/integer/uint.sol @@ -233,6 +233,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // uintMinA() -> true // uintMinB() -> true diff --git a/test/libsolidity/semanticTests/interfaceID/homer.sol b/test/libsolidity/semanticTests/interfaceID/homer.sol index 243cba0b4edd..8e8bf956f0bf 100644 --- a/test/libsolidity/semanticTests/interfaceID/homer.sol +++ b/test/libsolidity/semanticTests/interfaceID/homer.sol @@ -29,6 +29,9 @@ contract Homer is ERC165, Simpson { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // supportsInterface(bytes4): left(0x01ffc9a0) -> false // supportsInterface(bytes4): left(0x01ffc9a7) -> true diff --git a/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol b/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol index 855ca4d40e9d..6f612bb95c2a 100644 --- a/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol +++ b/test/libsolidity/semanticTests/interfaceID/homer_interfaceId.sol @@ -31,6 +31,7 @@ contract Homer is ERC165, Simpson { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // supportsInterface(bytes4): left(0x01ffc9a0) -> false // supportsInterface(bytes4): left(0x01ffc9a7) -> true diff --git a/test/libsolidity/semanticTests/interfaceID/interfaceId_events.sol b/test/libsolidity/semanticTests/interfaceID/interfaceId_events.sol index 09cb6c0d4f76..ef6db06d1cfa 100644 --- a/test/libsolidity/semanticTests/interfaceID/interfaceId_events.sol +++ b/test/libsolidity/semanticTests/interfaceID/interfaceId_events.sol @@ -16,6 +16,7 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// hello_world() -> left(0xc6be8b58) -// hello_world_with_event() -> left(0xc6be8b58) +// hello_world() -> left(0xc6be8b58) +// hello_world_with_event() -> left(0xc6be8b58) diff --git a/test/libsolidity/semanticTests/interfaceID/interfaces.sol b/test/libsolidity/semanticTests/interfaceID/interfaces.sol index ba63cfb12483..633bd7a947a6 100644 --- a/test/libsolidity/semanticTests/interfaceID/interfaces.sol +++ b/test/libsolidity/semanticTests/interfaceID/interfaces.sol @@ -53,15 +53,13 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // hello() -> left(0x19ff1d21) // world() -> left(0xdf419679) -// // ERC165_interfaceId() -> left(0x01ffc9a7) -// -// hello_world() -> left(0xc6be8b58) -// hello_world_interfaceId() -> left(0xc6be8b58) +// hello_world() -> left(0xc6be8b58) +// hello_world_interfaceId() -> left(0xc6be8b58) // ghello_world_interfaceId() -> left(0xc6be8b58) -// // other() -> left(0x85295877) // hello_world_derived_interfaceId() -> left(0x85295877) diff --git a/test/libsolidity/semanticTests/interfaceID/lisa.sol b/test/libsolidity/semanticTests/interfaceID/lisa.sol index 885cf9b2463c..7c50b5f161c9 100644 --- a/test/libsolidity/semanticTests/interfaceID/lisa.sol +++ b/test/libsolidity/semanticTests/interfaceID/lisa.sol @@ -40,6 +40,8 @@ contract Lisa is ERC165MappingImplementation, Simpson { } } +// ==== +// compileViaYul: also // ---- // supportsInterface(bytes4): left(0x01ffc9a0) -> false // supportsInterface(bytes4): left(0x01ffc9a7) -> true diff --git a/test/libsolidity/semanticTests/intheritance/access_base_storage.sol b/test/libsolidity/semanticTests/intheritance/access_base_storage.sol index a083ccd985b5..71e41df791f9 100644 --- a/test/libsolidity/semanticTests/intheritance/access_base_storage.sol +++ b/test/libsolidity/semanticTests/intheritance/access_base_storage.sol @@ -24,6 +24,7 @@ contract Derived is Base { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // setData(uint256,uint256): 1, 2 -> true // getViaBase() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol b/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol index 753e9bd1e520..615494e761f4 100644 --- a/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol +++ b/test/libsolidity/semanticTests/intheritance/base_access_to_function_type_variables.sol @@ -14,8 +14,11 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // g() -> 2 -// h() -> FAILURE +// h() -> FAILURE, hex"4e487b71", 0x51 // set() -> // h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol index 5628501d9a2d..b749a6d5cb88 100644 --- a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol +++ b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_direct.sol @@ -17,5 +17,6 @@ contract C is B { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol index 949024f43e8f..d424de842965 100644 --- a/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol +++ b/test/libsolidity/semanticTests/intheritance/derived_overload_base_function_indirect.sol @@ -24,6 +24,7 @@ contract C is A, B { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 10 // h() -> 2 diff --git a/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol b/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol index 7481a0441e11..117cf94e85a6 100644 --- a/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol +++ b/test/libsolidity/semanticTests/intheritance/explicit_base_class.sol @@ -23,6 +23,7 @@ contract Derived is Base { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 3 // f() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol index ab7a097ccde6..96dce72cff10 100644 --- a/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol +++ b/test/libsolidity/semanticTests/intheritance/inherited_constant_state_var.sol @@ -11,5 +11,6 @@ contract B is A { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function.sol b/test/libsolidity/semanticTests/intheritance/inherited_function.sol index fc01100de029..9908e3934375 100644 --- a/test/libsolidity/semanticTests/intheritance/inherited_function.sol +++ b/test/libsolidity/semanticTests/intheritance/inherited_function.sol @@ -16,5 +16,6 @@ contract B is A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/inherited_function_through_dispatch.sol b/test/libsolidity/semanticTests/intheritance/inherited_function_through_dispatch.sol index a7aa1fcb8598..9204db4e5c0a 100644 --- a/test/libsolidity/semanticTests/intheritance/inherited_function_through_dispatch.sol +++ b/test/libsolidity/semanticTests/intheritance/inherited_function_through_dispatch.sol @@ -17,5 +17,6 @@ contract B is A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 1 diff --git a/test/libsolidity/semanticTests/intheritance/member_notation_ctor.sol b/test/libsolidity/semanticTests/intheritance/member_notation_ctor.sol new file mode 100644 index 000000000000..898c979fa058 --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/member_notation_ctor.sol @@ -0,0 +1,25 @@ +==== Source: A ==== +contract C { + int private x; + constructor (int p) public { x = p; } + function getX() public returns (int) { return x; } +} +==== Source: B ==== +import "A" as M; + +contract D is M.C { + constructor (int p) M.C(p) public {} +} + +contract A { + function g(int p) public returns (int) { + D d = new D(p); + return d.getX(); + } +} + +// ==== +// compileViaYul: also +// ---- +// g(int256): -1 -> -1 +// g(int256): 10 -> 10 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol index 7f65eb4adb7d..52bacb98a171 100644 --- a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_first.sol @@ -14,5 +14,6 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 3 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol index bee0e8fa5723..15b706ca9d30 100644 --- a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_resolve_to_second.sol @@ -14,5 +14,6 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 10 diff --git a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol index df437a32c8ce..b282f5a51eef 100644 --- a/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol +++ b/test/libsolidity/semanticTests/intheritance/overloaded_function_call_with_if_else.sol @@ -15,6 +15,7 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g(bool): true -> 3 // g(bool): false -> 10 diff --git a/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol index 5be2158a258a..a140a3841ad2 100644 --- a/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol +++ b/test/libsolidity/semanticTests/intheritance/super_in_constructor.sol @@ -30,6 +30,8 @@ contract D is B, C { return data; } } - +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 15 diff --git a/test/libsolidity/semanticTests/intheritance/super_in_constructor_assignment.sol b/test/libsolidity/semanticTests/intheritance/super_in_constructor_assignment.sol new file mode 100644 index 000000000000..042bfc8332ce --- /dev/null +++ b/test/libsolidity/semanticTests/intheritance/super_in_constructor_assignment.sol @@ -0,0 +1,40 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + function() internal returns (uint) x = super.f; + return x() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + function() internal returns (uint) x = super.f; + return x() | 4; + } +} + + +contract D is B, C { + uint256 data; + + constructor() { + function() internal returns (uint) x = super.f; + data = x() | 8; + } + + function f() public override (B, C) returns (uint256 r) { + return data; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 15 diff --git a/test/libsolidity/semanticTests/intheritance/super_overload.sol b/test/libsolidity/semanticTests/intheritance/super_overload.sol index 6a1f6dc3d1d3..bbb6fab6566e 100644 --- a/test/libsolidity/semanticTests/intheritance/super_overload.sol +++ b/test/libsolidity/semanticTests/intheritance/super_overload.sol @@ -22,6 +22,9 @@ contract C is A, B { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // g() -> 10 // h() -> 2 diff --git a/test/libsolidity/semanticTests/isoltestFormatting.sol b/test/libsolidity/semanticTests/isoltestFormatting.sol index a6e25172e9d1..d4c6e1c57dba 100644 --- a/test/libsolidity/semanticTests/isoltestFormatting.sol +++ b/test/libsolidity/semanticTests/isoltestFormatting.sol @@ -10,6 +10,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 4, 11, 0x0111, 0x333333, 2222222222222222222 // g() -> 0x10, 0x0100, 0x0101, 0x333333, 2222222222222222222 diff --git a/test/libsolidity/semanticTests/libraries/bound_returning_calldata.sol b/test/libsolidity/semanticTests/libraries/bound_returning_calldata.sol index 867fb7e8c5ed..6cd9045b48c2 100644 --- a/test/libsolidity/semanticTests/libraries/bound_returning_calldata.sol +++ b/test/libsolidity/semanticTests/libraries/bound_returning_calldata.sol @@ -9,7 +9,7 @@ library D { contract C { using D for bytes; - function f(bytes calldata _x) public pure returns (byte, byte) { + function f(bytes calldata _x) public pure returns (bytes1, bytes1) { return (_x.f()[0], _x.g()[0]); } } diff --git a/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol b/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol index 3114f851071a..ab953c310b5a 100644 --- a/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol +++ b/test/libsolidity/semanticTests/libraries/bound_returning_calldata_external.sol @@ -9,11 +9,12 @@ library D { contract C { using D for bytes; - function f(bytes calldata _x) public pure returns (byte, byte) { + function f(bytes calldata _x) public pure returns (bytes1, bytes1) { return (_x.f()[0], _x.g()[0]); } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // library: D diff --git a/test/libsolidity/semanticTests/libraries/bound_to_calldata.sol b/test/libsolidity/semanticTests/libraries/bound_to_calldata.sol index 300fa881246f..b80d2fdc7889 100644 --- a/test/libsolidity/semanticTests/libraries/bound_to_calldata.sol +++ b/test/libsolidity/semanticTests/libraries/bound_to_calldata.sol @@ -1,15 +1,15 @@ library D { - function f(bytes calldata _x) internal pure returns (byte) { + function f(bytes calldata _x) internal pure returns (bytes1) { return _x[0]; } - function g(bytes memory _x) internal pure returns (byte) { + function g(bytes memory _x) internal pure returns (bytes1) { return _x[0]; } } contract C { using D for bytes; - function f(bytes calldata _x) public pure returns (byte, byte) { + function f(bytes calldata _x) public pure returns (bytes1, bytes1) { return (_x.f(), _x.g()); } } diff --git a/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol b/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol index 2f8c7c7f3937..bd3cb3865972 100644 --- a/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol +++ b/test/libsolidity/semanticTests/libraries/bound_to_calldata_external.sol @@ -1,19 +1,20 @@ library D { - function f(bytes calldata _x) public pure returns (byte) { + function f(bytes calldata _x) public pure returns (bytes1) { return _x[0]; } - function g(bytes memory _x) public pure returns (byte) { + function g(bytes memory _x) public pure returns (bytes1) { return _x[0]; } } contract C { using D for bytes; - function f(bytes calldata _x) public pure returns (byte, byte) { + function f(bytes calldata _x) public pure returns (bytes1, bytes1) { return (_x.f(), _x.g()); } } // ==== +// compileViaYul: also // EVMVersion: >homestead // ---- // library: D diff --git a/test/libsolidity/semanticTests/libraries/external_call_with_function_pointer_parameter.sol b/test/libsolidity/semanticTests/libraries/external_call_with_function_pointer_parameter.sol new file mode 100644 index 000000000000..a0843bfcb944 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/external_call_with_function_pointer_parameter.sol @@ -0,0 +1,26 @@ +library L { + function run( + function(uint256) external returns (uint256) _operation, + uint256 _a + ) + external + returns (uint256) + { + return _operation(_a); + } +} + +contract C { + function double(uint256 _a) external returns (uint256) { + return _a * _a; + } + + function g(uint256 _value) external returns (uint256) { + return L.run(this.double, _value); + } +} +// ==== +// compileViaYul: also +// ---- +// library: L +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/external_call_with_storage_array_parameter.sol b/test/libsolidity/semanticTests/libraries/external_call_with_storage_array_parameter.sol new file mode 100644 index 000000000000..81268af7a52f --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/external_call_with_storage_array_parameter.sol @@ -0,0 +1,19 @@ +library L { + function f(uint256[2] storage _a) external returns (uint256) { + return _a[0] * _a[1]; + } +} + +contract C { + uint256[2] x; + + function g(uint256 _value) external returns (uint256) { + x[0] = x[1] = _value; + return L.f(x); + } +} +// ==== +// compileViaYul: also +// ---- +// library: L +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/external_call_with_storage_mapping_parameter.sol b/test/libsolidity/semanticTests/libraries/external_call_with_storage_mapping_parameter.sol new file mode 100644 index 000000000000..d009bed5cb7d --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/external_call_with_storage_mapping_parameter.sol @@ -0,0 +1,17 @@ +library L { + function f(mapping(uint256 => uint256) storage _a) external returns (uint256) { + return _a[0] * _a[1]; + } +} + +contract C { + mapping(uint256 => uint256) x; + + function g(uint256 _value) external returns (uint256) { + x[0] = x[1] = _value; + return L.f(x); + } +} +// ---- +// library: L +// g(uint256): 4 -> 16 diff --git a/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses.sol b/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses.sol new file mode 100644 index 000000000000..6d073c627cf8 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses.sol @@ -0,0 +1,27 @@ +library L { + struct S { + uint256[] data; + } + + function f(S memory _s) internal { + _s.data[3] += 2; + } +} + + +contract C { + using L for L.S; + + function f() public returns (uint256) { + L.S memory x; + x.data = new uint256[](7); + x.data[3] = 8; + (x.f)(); + return x.data[3]; + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x0a diff --git a/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses1.sol b/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses1.sol new file mode 100644 index 000000000000..514cea6ba2db --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_call_bound_with_parentheses1.sol @@ -0,0 +1,16 @@ +library L { + function f() internal returns (uint) { + return 3; + } +} + +contract C { + function foo() public returns (uint) { + return (L.f)(); + } +} + +// ==== +// compileViaYul: also +// ---- +// foo() -> 3 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_array_named_pop_push.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_array_named_pop_push.sol index c684cc77f399..cd19d66f4be6 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_array_named_pop_push.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_array_named_pop_push.sol @@ -15,5 +15,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_bool.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_bool.sol index 836f976bb7d6..a21b8550c037 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_bool.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_bool.sol @@ -13,8 +13,9 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// foo(bool, bool): true, true -> false -// foo(bool, bool): true, false -> true -// foo(bool, bool): false, true -> true -// foo(bool, bool): false, false -> false +// foo(bool,bool): true, true -> false +// foo(bool,bool): true, false -> true +// foo(bool,bool): false, true -> true +// foo(bool,bool): false, false -> false diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_contract.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_contract.sol index a9566ca59ff2..b7aaacb9d696 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_contract.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_contract.sol @@ -17,5 +17,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 42 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_array.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_array.sol index 842911079503..79c8826a6aa4 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_array.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_array.sol @@ -17,5 +17,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // secondItem() -> 0x22 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_bytes.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_bytes.sol index 62bd48beb2c9..ea8ae12a1d59 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_bytes.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_fixed_bytes.sol @@ -13,5 +13,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// sum(bytes2, bytes2): left(0x1100), left(0x0022) -> left(0x1122) +// sum(bytes2,bytes2): left(0x1100), left(0x0022) -> left(0x1122) diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_function_named_selector.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_function_named_selector.sol index 75c4e4cb86cd..18dadbc873bb 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_function_named_selector.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_function_named_selector.sol @@ -18,5 +18,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test(uint256): 5 -> 10 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_integer.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_integer.sol index b6793c3d7744..47f8af110241 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_integer.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_integer.sol @@ -13,5 +13,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// foo(uint256, uint256): 8, 42 -> 50 +// foo(uint256,uint256): 8, 42 -> 50 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_interface.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_interface.sol index 47e58bb32420..f7939b83e743 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_interface.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_interface.sol @@ -18,5 +18,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 42 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_internal_function.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_internal_function.sol index 519eafdddabd..dec984601f4c 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_internal_function.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_internal_function.sol @@ -18,5 +18,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test(uint256): 5 -> 10 diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_storage_string.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_storage_string.sol new file mode 100644 index 000000000000..6c15fa7351f0 --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_storage_string.sol @@ -0,0 +1,22 @@ +library L { + function f(string memory a) internal pure returns (string memory) { + return a; + } + function g(string storage a) internal pure returns (string memory) { + return a; + } +} + +contract C { + using L for string; + string s; + + function test(string calldata x) public returns (string memory, string memory) { + s = x; + return (s.f(), s.g()); + } +} +// ==== +// compileViaYul: also +// ---- +// test(string): 0x20, 3, "def" -> 0x40, 0x80, 3, "def", 3, "def" diff --git a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_string.sol b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_string.sol index d8d4205128fd..90e41e956f1e 100644 --- a/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_string.sol +++ b/test/libsolidity/semanticTests/libraries/internal_library_function_bound_to_string.sol @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // secondChar() -> 98 diff --git a/test/libsolidity/semanticTests/libraries/library_address.sol b/test/libsolidity/semanticTests/libraries/library_address.sol index 3854a39bf223..6ca064445012 100644 --- a/test/libsolidity/semanticTests/libraries/library_address.sol +++ b/test/libsolidity/semanticTests/libraries/library_address.sol @@ -51,6 +51,6 @@ contract C { // j(uint256): 1 -> 1 // j(uint256): 2 -> 4 // j(uint256): 4 -> 16 -// k(uint256): 1 -> FAILURE -// k(uint256): 2 -> FAILURE -// k(uint256): 4 -> FAILURE +// k(uint256): 1 -> FAILURE, hex"4e487b71", 0x01 +// k(uint256): 2 -> FAILURE, hex"4e487b71", 0x01 +// k(uint256): 4 -> FAILURE, hex"4e487b71", 0x01 diff --git a/test/libsolidity/semanticTests/libraries/library_address_homestead.sol b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol index ce7344f4adc4..256b43d3ae76 100644 --- a/test/libsolidity/semanticTests/libraries/library_address_homestead.sol +++ b/test/libsolidity/semanticTests/libraries/library_address_homestead.sol @@ -12,6 +12,8 @@ contract C { return success; } } +// ==== +// compileViaYul: also // ---- // library: L // g(uint256,uint256): 1, 1 -> true diff --git a/test/libsolidity/semanticTests/libraries/library_address_via_module.sol b/test/libsolidity/semanticTests/libraries/library_address_via_module.sol new file mode 100644 index 000000000000..e6f0ed4815fb --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_address_via_module.sol @@ -0,0 +1,60 @@ +==== Source: a.sol ==== + +import "a.sol" as M; + +library L { + function f(uint256 v) external pure returns (uint) { + return v * v; + } + function g(uint256 v) external returns (uint) { + return v * v; + } +} +contract C { + function addr() public view returns (bool) { + return address(M.L) == address(0); + } + function g(uint256 v) public view returns (uint256) { + return M.L.f(v); + } + function h(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).delegatecall(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function i(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).call(abi.encodeWithSignature("f(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function j(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).delegatecall(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } + function k(uint256 v) public returns (uint256) { + (bool success, bytes memory result) = address(M.L).call(abi.encodeWithSignature("g(uint256)", v)); + assert(success); + return abi.decode(result, (uint256)); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// library: L +// addr() -> false +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 4 +// g(uint256): 4 -> 16 +// h(uint256): 1 -> 1 +// h(uint256): 2 -> 4 +// h(uint256): 4 -> 16 +// i(uint256): 1 -> 1 +// i(uint256): 2 -> 4 +// i(uint256): 4 -> 16 +// j(uint256): 1 -> 1 +// j(uint256): 2 -> 4 +// j(uint256): 4 -> 16 +// k(uint256): 1 -> FAILURE, hex"4e487b71", 0x01 +// k(uint256): 2 -> FAILURE, hex"4e487b71", 0x01 +// k(uint256): 4 -> FAILURE, hex"4e487b71", 0x01 diff --git a/test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol b/test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol index 8081b0e24715..186fad7dbc1a 100644 --- a/test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol +++ b/test/libsolidity/semanticTests/libraries/library_function_selectors_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; library L { struct S { uint256 a; } function f(S storage s) external returns (uint) { return s.a; } diff --git a/test/libsolidity/semanticTests/libraries/library_return_struct_with_mapping.sol b/test/libsolidity/semanticTests/libraries/library_return_struct_with_mapping.sol new file mode 100644 index 000000000000..00001a2f830f --- /dev/null +++ b/test/libsolidity/semanticTests/libraries/library_return_struct_with_mapping.sol @@ -0,0 +1,21 @@ +pragma abicoder v2; + +library Lib { + struct Items { + mapping (uint => uint) a; + } + + function get() public returns (Items storage x) { + assembly { x.slot := 123 } + } +} + +contract C { + function f() public returns(uint256 slot) { + Lib.Items storage ptr = Lib.get(); + assembly { slot := ptr.slot } + } +} +// ---- +// library: Lib +// f() -> 123 diff --git a/test/libsolidity/semanticTests/libraries/stub.sol b/test/libsolidity/semanticTests/libraries/stub.sol index 8bae3df27296..4ed1f2673446 100644 --- a/test/libsolidity/semanticTests/libraries/stub.sol +++ b/test/libsolidity/semanticTests/libraries/stub.sol @@ -6,6 +6,8 @@ contract C { return L.f(v); } } +// ==== +// compileViaYul: also // ---- // library: L // g(uint256): 1 -> 1 diff --git a/test/libsolidity/semanticTests/libraries/using_for_storage_structs.sol b/test/libsolidity/semanticTests/libraries/using_for_storage_structs.sol index cd45726b676b..086ab8a84ae2 100644 --- a/test/libsolidity/semanticTests/libraries/using_for_storage_structs.sol +++ b/test/libsolidity/semanticTests/libraries/using_for_storage_structs.sol @@ -21,5 +21,8 @@ contract C { return (s.f(), h(s)); } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // g() -> 7, 7 diff --git a/test/libsolidity/semanticTests/literals/denominations.sol b/test/libsolidity/semanticTests/literals/denominations.sol index 256525cb932d..b4d98f8d191a 100644 --- a/test/libsolidity/semanticTests/literals/denominations.sol +++ b/test/libsolidity/semanticTests/literals/denominations.sol @@ -5,5 +5,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1000000001000000001 diff --git a/test/libsolidity/semanticTests/literals/escape.sol b/test/libsolidity/semanticTests/literals/escape.sol index 8f015297eb8f..846dcc793c33 100644 --- a/test/libsolidity/semanticTests/literals/escape.sol +++ b/test/libsolidity/semanticTests/literals/escape.sol @@ -1,13 +1,14 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { - function f() public pure returns (uint, byte, byte) { + function f() public pure returns (uint, bytes1, bytes1) { bytes memory encoded = abi.encodePacked("\\\\"); return (encoded.length, encoded[0], encoded[1]); } } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2, 0x5c00000000000000000000000000000000000000000000000000000000000000, 0x5c00000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/literals/ether.sol b/test/libsolidity/semanticTests/literals/ether.sol index f03171796461..dc7b6eff46cb 100644 --- a/test/libsolidity/semanticTests/literals/ether.sol +++ b/test/libsolidity/semanticTests/literals/ether.sol @@ -5,5 +5,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1000000000000000000 diff --git a/test/libsolidity/semanticTests/literals/gwei.sol b/test/libsolidity/semanticTests/literals/gwei.sol index 54870040ff44..76ef2fba5dca 100644 --- a/test/libsolidity/semanticTests/literals/gwei.sol +++ b/test/libsolidity/semanticTests/literals/gwei.sol @@ -5,6 +5,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1000000000 - diff --git a/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol b/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol index 3194d7ee93ec..0ebe52f7fef8 100644 --- a/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol +++ b/test/libsolidity/semanticTests/literals/hex_string_with_underscore.sol @@ -5,5 +5,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 32, 5, left(0x123456789A) diff --git a/test/libsolidity/semanticTests/literals/scientific_notation.sol b/test/libsolidity/semanticTests/literals/scientific_notation.sol index e79fca70b80c..d96540e98a80 100644 --- a/test/libsolidity/semanticTests/literals/scientific_notation.sol +++ b/test/libsolidity/semanticTests/literals/scientific_notation.sol @@ -26,6 +26,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 20000000000 // g() -> 2 @@ -33,4 +34,3 @@ contract C { // i() -> -20000000000 // j() -> -2 // k() -> -25 - diff --git a/test/libsolidity/semanticTests/literals/wei.sol b/test/libsolidity/semanticTests/literals/wei.sol index 59af2a3d731b..87d7a4fb9bff 100644 --- a/test/libsolidity/semanticTests/literals/wei.sol +++ b/test/libsolidity/semanticTests/literals/wei.sol @@ -5,5 +5,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/memoryManagement/assembly_access.sol b/test/libsolidity/semanticTests/memoryManagement/assembly_access.sol new file mode 100644 index 000000000000..15e171dfcf6e --- /dev/null +++ b/test/libsolidity/semanticTests/memoryManagement/assembly_access.sol @@ -0,0 +1,16 @@ +contract C { + function f() public pure { + uint[] memory x; + uint y; + assembly { + y := x + } + // The value of an uninitialized dynamic array is not zero but rather + // an address of a location in memory that has the value of zero. + assert(y != 0); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> diff --git a/test/libsolidity/semanticTests/memoryManagement/return_variable.sol b/test/libsolidity/semanticTests/memoryManagement/return_variable.sol new file mode 100644 index 000000000000..501fea0f1355 --- /dev/null +++ b/test/libsolidity/semanticTests/memoryManagement/return_variable.sol @@ -0,0 +1,30 @@ +contract C { + function memorySize() internal pure returns (uint s) { + assembly { s := mload(0x40) } + } + function f() public returns (uint, uint, uint) { + uint a = memorySize(); + g(); + uint b = memorySize(); + h(); + uint c = memorySize(); + i(); + uint d = memorySize(); + return (b - a, c - b, d - c); + } + // In these functions, we do allocate memory in both cases. + // In `i()`, this could be avoided but we would have to check + // that all code paths return explicitly and provide a value. + function g() internal returns (uint[40] memory) { + } + function h() internal returns (uint[40] memory t) { + } + function i() internal returns (uint[40] memory) { + uint[40] memory x; + return x; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x0500, 0x0500, 0x0a00 diff --git a/test/libsolidity/semanticTests/memoryManagement/static_memory_array_allocation.sol b/test/libsolidity/semanticTests/memoryManagement/static_memory_array_allocation.sol new file mode 100644 index 000000000000..868d532b84cf --- /dev/null +++ b/test/libsolidity/semanticTests/memoryManagement/static_memory_array_allocation.sol @@ -0,0 +1,24 @@ +contract C { + function memorySize() internal pure returns (uint s) { + assembly { s := mload(0x40) } + } + function withValue() public pure returns (uint) { + uint[20] memory x; + uint memorySizeBefore = memorySize(); + uint[20] memory t = x; + uint memorySizeAfter = memorySize(); + return memorySizeAfter - memorySizeBefore; + } + function withoutValue() public pure returns (uint) { + uint[20] memory x; + uint memorySizeBefore = memorySize(); + uint[20] memory t; + uint memorySizeAfter = memorySize(); + return memorySizeAfter - memorySizeBefore; + } +} +// ==== +// compileViaYul: also +// ---- +// withValue() -> 0x00 +// withoutValue() -> 0x0280 diff --git a/test/libsolidity/semanticTests/memoryManagement/struct_allocation.sol b/test/libsolidity/semanticTests/memoryManagement/struct_allocation.sol new file mode 100644 index 000000000000..68a52182b10d --- /dev/null +++ b/test/libsolidity/semanticTests/memoryManagement/struct_allocation.sol @@ -0,0 +1,24 @@ +contract C { + struct S { uint x; uint y; uint z; } + function memorySize() internal pure returns (uint s) { + assembly { s := mload(0x40) } + } + function withValue() public pure returns (uint) { + S memory x = S(1, 2, 3); + uint memorySizeBefore = memorySize(); + S memory t = x; + uint memorySizeAfter = memorySize(); + return memorySizeAfter - memorySizeBefore; + } + function withoutValue() public pure returns (uint) { + uint memorySizeBefore = memorySize(); + S memory t; + uint memorySizeAfter = memorySize(); + return memorySizeAfter - memorySizeBefore; + } +} +// ==== +// compileViaYul: also +// ---- +// withValue() -> 0x00 +// withoutValue() -> 0x60 diff --git a/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol b/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol new file mode 100644 index 000000000000..f4b05fa13d95 --- /dev/null +++ b/test/libsolidity/semanticTests/metaTypes/name_other_contract.sol @@ -0,0 +1,29 @@ +abstract contract A { + function f() virtual public pure; +} + +interface I { + function f() external pure; +} + +contract C { + function f() pure public { + } +} + +contract Test is C { + function c() public pure returns (string memory) { + return type(C).name; + } + function a() public pure returns (string memory) { + return type(A).name; + } + function i() public pure returns (string memory) { + return type(I).name; + } +} + +// ---- +// c() -> 0x20, 1, "C" +// a() -> 0x20, 1, "A" +// i() -> 0x20, 1, "I" diff --git a/test/libsolidity/semanticTests/modifiers/access_through_contract_name.sol b/test/libsolidity/semanticTests/modifiers/access_through_contract_name.sol new file mode 100644 index 000000000000..4d1538d7cfb7 --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/access_through_contract_name.sol @@ -0,0 +1,22 @@ +contract A { + uint public x = 7; + modifier m virtual { x = 2; _; } +} +contract C is A { + modifier m override { x = 1; _; } + + function f() public A.m returns (uint) { + return 9; + } + function g() public m returns (uint) { + return 10; + } +} +// ---- +// x() -> 7 +// f() -> 9 +// x() -> 2 +// g() -> 0x0a +// x() -> 1 +// f() -> 9 +// x() -> 2 diff --git a/test/libsolidity/semanticTests/modifiers/access_through_module_name.sol b/test/libsolidity/semanticTests/modifiers/access_through_module_name.sol new file mode 100644 index 000000000000..58c60014311f --- /dev/null +++ b/test/libsolidity/semanticTests/modifiers/access_through_module_name.sol @@ -0,0 +1,25 @@ +==== Source: a ==== +import "a" as M; +contract C { + uint public x; + modifier m { x = 1; _; } + + function f() public M.M.C.m returns (uint t, uint r) { + t = x; + x = 3; + r = 9; + } + function g() public m returns (uint t, uint r) { + t = x; + x = 4; + r = 10; + } +} +// ---- +// x() -> 0x00 +// f() -> 1, 9 +// x() -> 3 +// g() -> 1, 0x0a +// x() -> 4 +// f() -> 1, 9 +// x() -> 3 diff --git a/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol index 27a9550cf1f8..67fc0e6be6c0 100644 --- a/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol +++ b/test/libsolidity/semanticTests/modifiers/break_in_modifier.sol @@ -3,7 +3,8 @@ contract C { modifier run() { for (uint256 i = 0; i < 10; i++) { _; - break; + if (i == 1) + break; } } @@ -13,9 +14,7 @@ contract C { x = t; } } -// ==== -// compileViaYul: also // ---- // x() -> 0 // f() -> -// x() -> 1 +// x() -> 2 diff --git a/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol index 27a9550cf1f8..616a2183c02d 100644 --- a/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol +++ b/test/libsolidity/semanticTests/modifiers/stacked_return_with_modifiers.sol @@ -1,21 +1,21 @@ contract C { uint256 public x; - modifier run() { + modifier m() { for (uint256 i = 0; i < 10; i++) { _; - break; + ++x; + return; } } - function f() public run { - uint256 k = x; - uint256 t = k + 1; - x = t; + function f() public m m m returns (uint) { + for (uint256 i = 0; i < 10; i++) { + ++x; + return 42; + } } } -// ==== -// compileViaYul: also // ---- // x() -> 0 -// f() -> -// x() -> 1 +// f() -> 42 +// x() -> 4 diff --git a/test/libsolidity/semanticTests/multiSource/circular_import.sol b/test/libsolidity/semanticTests/multiSource/circular_import.sol new file mode 100644 index 000000000000..c5131124d0ad --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/circular_import.sol @@ -0,0 +1,16 @@ +==== Source: s1.sol ==== +import {f as g} from "s2.sol"; +function f() pure returns (uint) { return 1; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +function f() pure returns (uint) { return 2; } +contract C { + function foo() public pure returns (uint) { + return f() - g(); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// foo() -> 1 diff --git a/test/libsolidity/semanticTests/multiSource/circular_import_2.sol b/test/libsolidity/semanticTests/multiSource/circular_import_2.sol new file mode 100644 index 000000000000..920890b3773a --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/circular_import_2.sol @@ -0,0 +1,16 @@ +==== Source: s1.sol ==== +import {f as g, g as h} from "s2.sol"; +function f() pure returns (uint) { return 1000 + g() - h(); } +==== Source: s2.sol ==== +import {f as h} from "s1.sol"; +function f() pure returns (uint) { return 2; } +function g() pure returns (uint) { return 4; } +contract C { + function foo() public pure returns (uint) { + return h() - f() - g(); + } +} +// ==== +// compileViaYul: also +// ---- +// foo() -> 992 diff --git a/test/libsolidity/semanticTests/multiSource/circular_reimport.sol b/test/libsolidity/semanticTests/multiSource/circular_reimport.sol new file mode 100644 index 000000000000..4514b24b0a19 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/circular_reimport.sol @@ -0,0 +1,18 @@ +==== Source: s1.sol ==== +import {f as g, g as h} from "s2.sol"; +function f() pure returns (uint) { return 100 + h() - g(); } +==== Source: s2.sol ==== +import {f as h} from "s1.sol"; +function f() pure returns (uint) { return 2; } +function g() pure returns (uint) { return 4; } +==== Source: s3.sol ==== +import "s1.sol"; +contract C { + function foo() public pure returns (uint) { + return f() - g() - h(); + } +} +// ==== +// compileViaYul: also +// ---- +// foo() -> 0x60 diff --git a/test/libsolidity/semanticTests/multiSource/circular_reimport_2.sol b/test/libsolidity/semanticTests/multiSource/circular_reimport_2.sol new file mode 100644 index 000000000000..07faf9356acc --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/circular_reimport_2.sol @@ -0,0 +1,18 @@ +==== Source: s1.sol ==== +import {f as g, g as h} from "s2.sol"; +function f() pure returns (uint) { return 1000 + h() - g(); } +==== Source: s2.sol ==== +import {f as h} from "s1.sol"; +function f() pure returns (uint) { return 2; } +function g() pure returns (uint) { return 4; } +==== Source: s3.sol ==== +import "s2.sol"; +contract C { + function foo() public pure returns (uint) { + return 10000 + f() - g() - h(); + } +} +// ==== +// compileViaYul: also +// ---- +// foo() -> 0x2324 diff --git a/test/libsolidity/semanticTests/multiSource/free_different_interger_types.sol b/test/libsolidity/semanticTests/multiSource/free_different_interger_types.sol new file mode 100644 index 000000000000..ecb66c23a883 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/free_different_interger_types.sol @@ -0,0 +1,15 @@ +==== Source: s1.sol ==== +function f(uint24) pure returns (uint) { return 24; } +function g(bool) pure returns (bool) { return true; } +==== Source: s2.sol ==== +import {f as g, g as g} from "s1.sol"; +contract C { + function foo() public pure returns (uint, bool) { + return (g(2), g(false)); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// foo() -> 24, true diff --git a/test/libsolidity/semanticTests/multiSource/free_function_resolution_base_contract.sol b/test/libsolidity/semanticTests/multiSource/free_function_resolution_base_contract.sol new file mode 100644 index 000000000000..15bdec6040d4 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/free_function_resolution_base_contract.sol @@ -0,0 +1,19 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C { + function g() public pure returns (uint) { + return f(); + } +} +==== Source: s2.sol ==== +import "s1.sol"; +contract D is C { + function h() public pure returns (uint) { + return g(); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// h() -> 1337 diff --git a/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual.sol b/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual.sol new file mode 100644 index 000000000000..fe6a994c1297 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual.sol @@ -0,0 +1,19 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C { + function g() public pure virtual returns (uint) { + return f() + 1; + } +} +==== Source: s2.sol ==== +import "s1.sol"; +contract D is C { + function g() public pure override returns (uint) { + return f(); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g() -> 1337 diff --git a/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual_super.sol b/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual_super.sol new file mode 100644 index 000000000000..1ba9b8c3861c --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual_super.sol @@ -0,0 +1,19 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C { + function g() public pure virtual returns (uint) { + return f(); + } +} +==== Source: s2.sol ==== +import "s1.sol"; +contract D is C { + function g() public pure override returns (uint) { + return super.g(); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g() -> 1337 diff --git a/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual_transitive.sol b/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual_transitive.sol new file mode 100644 index 000000000000..e8c71492bbf5 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/free_function_resolution_override_virtual_transitive.sol @@ -0,0 +1,26 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C { + function g() public pure virtual returns (uint) { + return f(); + } +} +==== Source: s2.sol ==== +import "s1.sol"; +contract D is C { + function g() public pure virtual override returns (uint) { + return super.g() + 1; + } +} +==== Source: s3.sol ==== +import "s2.sol"; +contract E is D { + function g() public pure override returns (uint) { + return super.g() + 1; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g() -> 1339 diff --git a/test/libsolidity/semanticTests/multiSource/free_function_transitive_import.sol b/test/libsolidity/semanticTests/multiSource/free_function_transitive_import.sol new file mode 100644 index 000000000000..6bcfc500144f --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/free_function_transitive_import.sol @@ -0,0 +1,28 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C { + function g() public pure returns (uint) { + return f(); + } +} +==== Source: s2.sol ==== +import "s1.sol"; +contract D is C { + function h() public pure returns (uint) { + return g(); + } +} +==== Source: s3.sol ==== +import "s2.sol"; +import {f as f} from "s2.sol"; +contract E is D { + function i() public pure returns (uint) { + return f(); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// i() -> 1337 diff --git a/test/libsolidity/semanticTests/multiSource/import.sol b/test/libsolidity/semanticTests/multiSource/import.sol index d3f5ed301997..74f96c4498db 100644 --- a/test/libsolidity/semanticTests/multiSource/import.sol +++ b/test/libsolidity/semanticTests/multiSource/import.sol @@ -9,6 +9,7 @@ contract B is A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 1337 -> 1337 // g(uint256): 1337 -> 1338 diff --git a/test/libsolidity/semanticTests/multiSource/imported_free_function_via_alias.sol b/test/libsolidity/semanticTests/multiSource/imported_free_function_via_alias.sol new file mode 100644 index 000000000000..5ea279c4e666 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/imported_free_function_via_alias.sol @@ -0,0 +1,20 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C { + function g() public pure virtual returns (uint) { + return f(); + } +} +==== Source: s2.sol ==== +import "s1.sol" as M; +function f() pure returns (uint) { return 6; } +contract D is M.C { + function g() public pure override returns (uint) { + return super.g() + f() * 10000; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// g() -> 61337 diff --git a/test/libsolidity/semanticTests/multiSource/imported_free_function_via_alias_direct_call.sol b/test/libsolidity/semanticTests/multiSource/imported_free_function_via_alias_direct_call.sol new file mode 100644 index 000000000000..409bc42eff9e --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/imported_free_function_via_alias_direct_call.sol @@ -0,0 +1,15 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +function f() pure returns (uint) { return 6; } +contract D { + function h() public pure returns (uint) { + return g() + f() * 10000; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// h() -> 61337 diff --git a/test/libsolidity/semanticTests/multiSource/reimport_imported_function.sol b/test/libsolidity/semanticTests/multiSource/reimport_imported_function.sol new file mode 100644 index 000000000000..0a5efe236194 --- /dev/null +++ b/test/libsolidity/semanticTests/multiSource/reimport_imported_function.sol @@ -0,0 +1,16 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +==== Source: s3.sol ==== +import {g as h} from "s2.sol"; +contract C { + function foo() public pure returns (uint) { + return h(); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// foo() -> 1337 diff --git a/test/libsolidity/semanticTests/operators/compound_assign.sol b/test/libsolidity/semanticTests/operators/compound_assign.sol new file mode 100644 index 000000000000..d58761d99d2a --- /dev/null +++ b/test/libsolidity/semanticTests/operators/compound_assign.sol @@ -0,0 +1,23 @@ +contract test { + uint value1; + uint value2; + function f(uint x, uint y) public returns (uint w) { + uint value3 = y; + value1 += x; + value3 *= x; + value2 *= value3 + value1; + return value2 += 7; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint256,uint256): 0, 6 -> 7 +// f(uint256,uint256): 1, 3 -> 0x23 +// f(uint256,uint256): 2, 25 -> 0x0746 +// f(uint256,uint256): 3, 69 -> 396613 +// f(uint256,uint256): 4, 84 -> 137228105 +// f(uint256,uint256): 5, 2 -> 0xcc7c5e28 +// f(uint256,uint256): 6, 51 -> 1121839760671 +// f(uint256,uint256): 7, 48 -> 408349672884251 diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol b/test/libsolidity/semanticTests/operators/shifts/bitwise_shifting_constantinople.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol rename to test/libsolidity/semanticTests/operators/shifts/bitwise_shifting_constantinople.sol diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol b/test/libsolidity/semanticTests/operators/shifts/bitwise_shifting_constantinople_combined.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol rename to test/libsolidity/semanticTests/operators/shifts/bitwise_shifting_constantinople_combined.sol diff --git a/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol b/test/libsolidity/semanticTests/operators/shifts/bitwise_shifting_constants_constantinople.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol rename to test/libsolidity/semanticTests/operators/shifts/bitwise_shifting_constants_constantinople.sol diff --git a/test/libsolidity/semanticTests/operators/shifts/shift_cleanup.sol b/test/libsolidity/semanticTests/operators/shifts/shift_cleanup.sol new file mode 100644 index 000000000000..722fdae184d5 --- /dev/null +++ b/test/libsolidity/semanticTests/operators/shifts/shift_cleanup.sol @@ -0,0 +1,15 @@ +contract C { + function f() public returns (uint16 x) { + unchecked { + x = 0xffff; + x += 32; + x <<= 8; + x >>= 16; + } + } +} + +// ==== +// compileViaYul: also +// ---- +// f() -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol b/test/libsolidity/semanticTests/operators/shifts/shift_cleanup_garbled.sol similarity index 88% rename from test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_cleanup_garbled.sol index 6789bc350261..1e9cf1e82b95 100644 --- a/test/libsolidity/semanticTests/shifts/shift_cleanup_garbled.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_cleanup_garbled.sol @@ -9,5 +9,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left.sol b/test/libsolidity/semanticTests/operators/shifts/shift_constant_left.sol similarity index 81% rename from test/libsolidity/semanticTests/shifts/shift_constant_left.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_constant_left.sol index 4e43cae3787e..32558f96bbb6 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_left.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_constant_left.sol @@ -3,5 +3,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // a() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol b/test/libsolidity/semanticTests/operators/shifts/shift_constant_left_assignment.sol similarity index 86% rename from test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_constant_left_assignment.sol index 38a776b30739..abf85972959e 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_left_assignment.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_constant_left_assignment.sol @@ -7,5 +7,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x4200 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right.sol b/test/libsolidity/semanticTests/operators/shifts/shift_constant_right.sol similarity index 81% rename from test/libsolidity/semanticTests/shifts/shift_constant_right.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_constant_right.sol index 8278e045ce01..14b8e0b1b26f 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_right.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_constant_right.sol @@ -3,5 +3,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // a() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol b/test/libsolidity/semanticTests/operators/shifts/shift_constant_right_assignment.sol similarity index 86% rename from test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_constant_right_assignment.sol index 1853814c6c53..9c27e16408b1 100644 --- a/test/libsolidity/semanticTests/shifts/shift_constant_right_assignment.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_constant_right_assignment.sol @@ -7,5 +7,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_left.sol b/test/libsolidity/semanticTests/operators/shifts/shift_left.sol similarity index 95% rename from test/libsolidity/semanticTests/shifts/shift_left.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_left.sol index e72671cebd76..ca94ccc53291 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_left.sol @@ -6,6 +6,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol b/test/libsolidity/semanticTests/operators/shifts/shift_left_assignment.sol similarity index 95% rename from test/libsolidity/semanticTests/shifts/shift_left_assignment.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_left_assignment.sol index fd5981996828..c746163a7b8a 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_assignment.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_left_assignment.sol @@ -7,6 +7,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol b/test/libsolidity/semanticTests/operators/shifts/shift_left_assignment_different_type.sol similarity index 94% rename from test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_left_assignment_different_type.sol index 2f470d50017e..ae2ddf94f66a 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_assignment_different_type.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_left_assignment_different_type.sol @@ -7,6 +7,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint8): 0x4266, 0x0 -> 0x4266 // f(uint256,uint8): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol b/test/libsolidity/semanticTests/operators/shifts/shift_left_larger_type.sol similarity index 91% rename from test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_left_larger_type.sol index de7b4ec3e0b2..f96b69d4ba1b 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_larger_type.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_left_larger_type.sol @@ -8,5 +8,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol b/test/libsolidity/semanticTests/operators/shifts/shift_left_uint32.sol similarity index 93% rename from test/libsolidity/semanticTests/shifts/shift_left_uint32.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_left_uint32.sol index a08f13aef8b9..f0402dccf1ac 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_uint32.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_left_uint32.sol @@ -6,6 +6,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint32,uint32): 0x4266, 0x0 -> 0x4266 // f(uint32,uint32): 0x4266, 0x8 -> 0x426600 diff --git a/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol b/test/libsolidity/semanticTests/operators/shifts/shift_left_uint8.sol similarity index 89% rename from test/libsolidity/semanticTests/shifts/shift_left_uint8.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_left_uint8.sol index af214a244d86..21f7d313b3e0 100644 --- a/test/libsolidity/semanticTests/shifts/shift_left_uint8.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_left_uint8.sol @@ -6,6 +6,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint8,uint8): 0x66, 0x0 -> 0x66 // f(uint8,uint8): 0x66, 0x8 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol b/test/libsolidity/semanticTests/operators/shifts/shift_negative_constant_left.sol similarity index 81% rename from test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_negative_constant_left.sol index 964b6543bfcb..8fd2dcf7022a 100644 --- a/test/libsolidity/semanticTests/shifts/shift_negative_constant_left.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_negative_constant_left.sol @@ -3,5 +3,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // a() -> -16896 diff --git a/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol b/test/libsolidity/semanticTests/operators/shifts/shift_negative_constant_right.sol similarity index 81% rename from test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_negative_constant_right.sol index 993fae441a13..24a33c71f8be 100644 --- a/test/libsolidity/semanticTests/shifts/shift_negative_constant_right.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_negative_constant_right.sol @@ -3,5 +3,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // a() -> -66 diff --git a/test/libsolidity/semanticTests/shifts/shift_overflow.sol b/test/libsolidity/semanticTests/operators/shifts/shift_overflow.sol similarity index 95% rename from test/libsolidity/semanticTests/shifts/shift_overflow.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_overflow.sol index c303d449e1ff..604350f78606 100644 --- a/test/libsolidity/semanticTests/shifts/shift_overflow.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_overflow.sol @@ -10,6 +10,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // leftU(uint8,uint8): 255, 8 -> 0 // leftU(uint8,uint8): 255, 1 -> 254 diff --git a/test/libsolidity/semanticTests/shifts/shift_right.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right.sol similarity index 95% rename from test/libsolidity/semanticTests/shifts/shift_right.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right.sol index bfdb665d1be4..997ea73934ca 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right.sol @@ -6,6 +6,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_assignment.sol similarity index 93% rename from test/libsolidity/semanticTests/shifts/shift_right_assignment.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_assignment.sol index 80f25238c6cc..4d6b215ea5b7 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_assignment.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_assignment.sol @@ -7,6 +7,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint256): 0x4266, 0x0 -> 0x4266 // f(uint256,uint256): 0x4266, 0x8 -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_garbled.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_garbled.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_garbled.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_signed.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_signed.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_signed_v2.sol similarity index 95% rename from test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_signed_v2.sol index 4c705337c88d..8843a00b5e44 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_signed_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -20,6 +20,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe // f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_v2.sol similarity index 85% rename from test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_v2.sol index 18ea9972c528..6397c7aa0f2f 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_garbled_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -12,6 +12,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint8,uint8): 0x00, 0x04 -> 0x0f // f(uint8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_literal.sol similarity index 98% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_literal.sol index 2ae9647e262e..c14b5ae33e1e 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_literal.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_literal.sol @@ -50,6 +50,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f1() -> true // f2() -> true diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_assignment.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_assignment.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_assignment.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_int16.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int16.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_int16.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_int32.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int32.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_int32.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_int8.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_int8.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_int8.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int16.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int16.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol similarity index 86% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol index 5ffb50c23e6e..cfc73bf49ef8 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -8,6 +8,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(int16,uint16): 0xff99, 0x00 -> FAILURE // f(int16,uint16): 0xff99, 0x01 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int32.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int32.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol similarity index 86% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol index a4cb461a1ea2..ea034781275d 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -8,6 +8,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(int32,uint32): 0xffffff99, 0x00 -> FAILURE // f(int32,uint32): 0xffffff99, 0x01 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int8.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int8.sol diff --git a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol similarity index 85% rename from test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol index f42f0677348e..3fb9e714c1ee 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -8,6 +8,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(int8,uint8): 0x99, 0x00 -> FAILURE // f(int8,uint8): 0x99, 0x01 -> FAILURE diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_uint32.sol similarity index 92% rename from test/libsolidity/semanticTests/shifts/shift_right_uint32.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_uint32.sol index 8cc6c4a9871c..5d1378582640 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_uint32.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_uint32.sol @@ -6,6 +6,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint32,uint32): 0x4266, 0x0 -> 0x4266 // f(uint32,uint32): 0x4266, 0x8 -> 0x42 diff --git a/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol b/test/libsolidity/semanticTests/operators/shifts/shift_right_uint8.sol similarity index 90% rename from test/libsolidity/semanticTests/shifts/shift_right_uint8.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_right_uint8.sol index acfd99b86953..1caf967648ae 100644 --- a/test/libsolidity/semanticTests/shifts/shift_right_uint8.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_right_uint8.sol @@ -6,6 +6,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint8,uint8): 0x66, 0x0 -> 0x66 // f(uint8,uint8): 0x66, 0x8 -> 0x0 diff --git a/test/libsolidity/semanticTests/shifts/shift_underflow_negative_rvalue.sol b/test/libsolidity/semanticTests/operators/shifts/shift_underflow_negative_rvalue.sol similarity index 92% rename from test/libsolidity/semanticTests/shifts/shift_underflow_negative_rvalue.sol rename to test/libsolidity/semanticTests/operators/shifts/shift_underflow_negative_rvalue.sol index ad14558beb4a..9014c8759c54 100644 --- a/test/libsolidity/semanticTests/shifts/shift_underflow_negative_rvalue.sol +++ b/test/libsolidity/semanticTests/operators/shifts/shift_underflow_negative_rvalue.sol @@ -10,6 +10,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(int256,uint256): 1, -1 -> 0 // g(int256,uint256): 1, -1 -> 0 diff --git a/test/libsolidity/semanticTests/shifts/shifts.sol b/test/libsolidity/semanticTests/operators/shifts/shifts.sol similarity index 100% rename from test/libsolidity/semanticTests/shifts/shifts.sol rename to test/libsolidity/semanticTests/operators/shifts/shifts.sol diff --git a/test/libsolidity/semanticTests/payable/no_nonpayable_circumvention_by_modifier.sol b/test/libsolidity/semanticTests/payable/no_nonpayable_circumvention_by_modifier.sol new file mode 100644 index 000000000000..cefb8f3b7f10 --- /dev/null +++ b/test/libsolidity/semanticTests/payable/no_nonpayable_circumvention_by_modifier.sol @@ -0,0 +1,18 @@ +contract C { + modifier tryCircumvent { + if (false) _; // avoid the function, we should still not accept ether + } + function f() tryCircumvent public returns (uint) { + return msgvalue(); + } + function msgvalue() internal returns (uint) { + return msg.value; + } + // TODO: remove this helper function once isoltest supports balance checking + function balance() external returns (uint) { + return address(this).balance; + } +} +// ---- +// f(), 27 wei -> FAILURE +// balance() -> 0 diff --git a/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol b/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol index b6e9416a7fff..bfaa52894228 100644 --- a/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol +++ b/test/libsolidity/semanticTests/receive/empty_calldata_calls_receive.sol @@ -4,6 +4,7 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // x() -> 0 // () diff --git a/test/libsolidity/semanticTests/receive/ether_and_data.sol b/test/libsolidity/semanticTests/receive/ether_and_data.sol index 44af7ab912b2..ea139e549e26 100644 --- a/test/libsolidity/semanticTests/receive/ether_and_data.sol +++ b/test/libsolidity/semanticTests/receive/ether_and_data.sol @@ -3,6 +3,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // (), 1 ether // (), 1 ether: 1 -> FAILURE diff --git a/test/libsolidity/semanticTests/receive/inherited.sol b/test/libsolidity/semanticTests/receive/inherited.sol index a713229837d3..cadfb44a86c6 100644 --- a/test/libsolidity/semanticTests/receive/inherited.sol +++ b/test/libsolidity/semanticTests/receive/inherited.sol @@ -6,6 +6,7 @@ contract A { contract B is A {} // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getData() -> 0 // () -> diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol index e91f08a03af2..37f9da6c2b98 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_invalid.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][] calldata a) external returns (uint) { return 42; diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol index d39b3f4fbb9e..4e857cea90d2 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_decode.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][2][] calldata x) external returns (uint256) { x[0]; diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol index 0e2ba94b10da..48e97fa9cd3a 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_dynamic_static_short_reencode.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][2][] calldata x) external returns (uint256) { return 42; diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol b/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol index f54a47d731c8..55beec091193 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_array_invalid_length.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][] calldata x) external returns (uint256) { return x[0].length; diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol b/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol index c19538d0a98e..ca953af4eacc 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_arrays_too_large.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint a, uint[] calldata b, uint c) external pure returns (uint) { return 7; diff --git a/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol b/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol index c3fcece1b749..545f30474264 100644 --- a/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol +++ b/test/libsolidity/semanticTests/revertStrings/calldata_tail_short.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][] calldata x) external { x[0]; } } diff --git a/test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol b/test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol index b21f85cc8d86..110b1e50c724 100644 --- a/test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol +++ b/test/libsolidity/semanticTests/revertStrings/called_contract_has_code.sol @@ -1,7 +1,7 @@ contract C { function f() external {} function g() external { - C c = C(0x0000000000000000000000000000000000000000000000000000000000000000); + C c = C(address(0x0000000000000000000000000000000000000000000000000000000000000000)); c.f(); } } diff --git a/test/libsolidity/semanticTests/revertStrings/empty_v1.sol b/test/libsolidity/semanticTests/revertStrings/empty_v1.sol new file mode 100644 index 000000000000..4e6fffc79b43 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/empty_v1.sol @@ -0,0 +1,18 @@ +pragma abicoder v1; +contract C { + function f() public { + revert(""); + } + function g(string calldata msg) public { + revert(msg); + } +} +// ==== +// ABIEncoderV1Only: true +// EVMVersion: >=byzantium +// compileViaYul: false +// revertStrings: debug +// ---- +// f() -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): 0x20, 0, "" -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): 0x20, 0 -> FAILURE, hex"08c379a0", 0x20, 0 diff --git a/test/libsolidity/semanticTests/revertStrings/empty_v1_yul.sol b/test/libsolidity/semanticTests/revertStrings/empty_v1_yul.sol new file mode 100644 index 000000000000..ad9543c88014 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/empty_v1_yul.sol @@ -0,0 +1,18 @@ +contract C { + function f() public { + revert(""); + } + function g(string calldata msg) public { + revert(msg); + } +} +// ==== +// ABIEncoderV1Only: true +// EVMVersion: >=byzantium +// compileViaYul: true +// revertStrings: debug +// ---- +// f() -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): "" -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): 0x20, 0, "" -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): 0x20, 0 -> FAILURE, hex"08c379a0", 0x20, 0 diff --git a/test/libsolidity/semanticTests/revertStrings/empty_v2.sol b/test/libsolidity/semanticTests/revertStrings/empty_v2.sol new file mode 100644 index 000000000000..ea81afbe8e35 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/empty_v2.sol @@ -0,0 +1,18 @@ +pragma abicoder v2; +contract C { + function f() public { + revert(""); + } + function g(string calldata msg) public { + revert(msg); + } +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: also +// revertStrings: debug +// ---- +// f() -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): "" -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): 0x20, 0, "" -> FAILURE, hex"08c379a0", 0x20, 0 +// g(string): 0x20, 0 -> FAILURE, hex"08c379a0", 0x20, 0 diff --git a/test/libsolidity/semanticTests/revertStrings/function_entry_checks_v1.sol b/test/libsolidity/semanticTests/revertStrings/function_entry_checks_v1.sol new file mode 100644 index 000000000000..e10cb267ce29 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/function_entry_checks_v1.sol @@ -0,0 +1,10 @@ +pragma abicoder v1; +contract C { + function t(uint) public pure {} +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: false +// revertStrings: debug +// ---- +// t(uint256) -> FAILURE, hex"08c379a0", 0x20, 0x12, "Calldata too short" diff --git a/test/libsolidity/semanticTests/revertStrings/function_entry_checks_v1_abiv2.sol b/test/libsolidity/semanticTests/revertStrings/function_entry_checks_v1_abiv2.sol new file mode 100644 index 000000000000..dc7c169b9099 --- /dev/null +++ b/test/libsolidity/semanticTests/revertStrings/function_entry_checks_v1_abiv2.sol @@ -0,0 +1,10 @@ +pragma abicoder v2; +contract C { + function t(uint) public pure {} +} +// ==== +// EVMVersion: >=byzantium +// compileViaYul: false +// revertStrings: debug +// ---- +// t(uint256) -> FAILURE, hex"08c379a0", 0x20, 34, "ABI decoding: tuple data too sho", "rt" diff --git a/test/libsolidity/semanticTests/revertStrings/function_entry_checks.sol b/test/libsolidity/semanticTests/revertStrings/function_entry_checks_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/revertStrings/function_entry_checks.sol rename to test/libsolidity/semanticTests/revertStrings/function_entry_checks_v2.sol diff --git a/test/libsolidity/semanticTests/revertStrings/short_input_array.sol b/test/libsolidity/semanticTests/revertStrings/short_input_array.sol index 4c83b79ffa36..77e5ef5ac0d1 100644 --- a/test/libsolidity/semanticTests/revertStrings/short_input_array.sol +++ b/test/libsolidity/semanticTests/revertStrings/short_input_array.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint[] memory a) public pure returns (uint) { return 7; } } diff --git a/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol b/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol index b8bb0493a358..de6876ac7bec 100644 --- a/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol +++ b/test/libsolidity/semanticTests/revertStrings/short_input_bytes.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function e(bytes memory a) public pure returns (uint) { return 7; } } diff --git a/test/libsolidity/semanticTests/revertStrings/transfer.sol b/test/libsolidity/semanticTests/revertStrings/transfer.sol index 5d13f7d72237..8ee6fd2e8885 100644 --- a/test/libsolidity/semanticTests/revertStrings/transfer.sol +++ b/test/libsolidity/semanticTests/revertStrings/transfer.sol @@ -8,13 +8,13 @@ contract C { A a = new A(); receive() external payable {} function f() public { - address(a).transfer(1 wei); + payable(a).transfer(1 wei); } function h() public { - address(a).transfer(100 ether); + payable(a).transfer(100 ether); } function g() public view returns (uint) { - return address(this).balance; + return payable(this).balance; } } // ==== diff --git a/test/libsolidity/semanticTests/reverts/assert_require.sol b/test/libsolidity/semanticTests/reverts/assert_require.sol index 6bd146a7a3aa..9a856f0142f2 100644 --- a/test/libsolidity/semanticTests/reverts/assert_require.sol +++ b/test/libsolidity/semanticTests/reverts/assert_require.sol @@ -16,9 +16,10 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// f() -> FAILURE -// g(bool): false -> FAILURE +// f() -> FAILURE, hex"4e487b71", 0x01 +// g(bool): false -> FAILURE, hex"4e487b71", 0x01 // g(bool): true -> true // h(bool): false -> FAILURE // h(bool): true -> true diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol index 1d240813ae3c..e393d81e20b0 100644 --- a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_arg.sol @@ -16,6 +16,7 @@ contract C { } } // ==== +// EVMVersion: >=byzantium // compileViaYul: also // ---- -// test() -> FAILURE # should throw # +// test() -> FAILURE, hex"4e487b71", 33 # should throw # diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol index 6bdf14298809..89fe7490f923 100644 --- a/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_as_external_ret.sol @@ -26,7 +26,9 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also +// EVMVersion: >=byzantium // ---- -// test_return() -> FAILURE # both should throw # -// test_inline_assignment() -> FAILURE -// test_assignment() -> FAILURE +// test_return() -> FAILURE, hex"4e487b71", 33 # both should throw # +// test_inline_assignment() -> FAILURE, hex"4e487b71", 33 +// test_assignment() -> FAILURE, hex"4e487b71", 33 diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol index 3c082a363560..3125fcf0a870 100644 --- a/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_compared.sol @@ -24,7 +24,9 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also +// EVMVersion: >=byzantium // ---- // test_eq_ok() -> 1 -// test_eq() -> FAILURE # both should throw # -// test_neq() -> FAILURE +// test_eq() -> FAILURE, hex"4e487b71", 33 # both should throw # +// test_neq() -> FAILURE, hex"4e487b71", 33 diff --git a/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol b/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol index 2b90451ec639..57f6af6f2f98 100644 --- a/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol +++ b/test/libsolidity/semanticTests/reverts/invalid_enum_stored.sol @@ -16,8 +16,11 @@ contract C { return 1; } } - +// ==== +// compileViaYul: also +// compileToEwasm: also +// EVMVersion: >=byzantium // ---- // test_store_ok() -> 1 // x() -> 0 -// test_store() -> FAILURE # should throw # +// test_store() -> FAILURE, hex"4e487b71", 33 # should throw # diff --git a/test/libsolidity/semanticTests/reverts/invalid_instruction.sol b/test/libsolidity/semanticTests/reverts/invalid_instruction.sol index 839296ca193a..4ce7527f8c4f 100644 --- a/test/libsolidity/semanticTests/reverts/invalid_instruction.sol +++ b/test/libsolidity/semanticTests/reverts/invalid_instruction.sol @@ -8,5 +8,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> FAILURE diff --git a/test/libsolidity/semanticTests/reverts/revert.sol b/test/libsolidity/semanticTests/reverts/revert.sol index 02496ef9402c..e51b8056ff03 100644 --- a/test/libsolidity/semanticTests/reverts/revert.sol +++ b/test/libsolidity/semanticTests/reverts/revert.sol @@ -16,6 +16,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> FAILURE // a() -> 42 diff --git a/test/libsolidity/semanticTests/reverts/revert_return_area.sol b/test/libsolidity/semanticTests/reverts/revert_return_area.sol new file mode 100644 index 000000000000..360e5fe9f40c --- /dev/null +++ b/test/libsolidity/semanticTests/reverts/revert_return_area.sol @@ -0,0 +1,20 @@ +contract C { + fallback() external { + revert("abc"); + } + + function f() public returns (uint s, uint r) { + address x = address(this); + assembly { + mstore(0, 7) + s := call(sub(0, 1), x, 0, 0, 0, 0, 32) + r := mload(0) + } + } +} + +// ==== +// compileViaYul: also +// EVMVersion: >=byzantium +// ---- +// f() -> 0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/reverts/simple_throw.sol b/test/libsolidity/semanticTests/reverts/simple_throw.sol index bf9df114c1d3..973146e394fd 100644 --- a/test/libsolidity/semanticTests/reverts/simple_throw.sol +++ b/test/libsolidity/semanticTests/reverts/simple_throw.sol @@ -8,6 +8,7 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 11 -> 21 // f(uint256): 1 -> FAILURE diff --git a/test/libsolidity/semanticTests/salted_create/salted_create.sol b/test/libsolidity/semanticTests/salted_create/salted_create.sol index b20adf74b4f5..6a08fb18a7c2 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create.sol @@ -17,6 +17,7 @@ contract A { } } // ==== +// compileViaYul: also // EVMVersion: >=constantinople // ---- // different_salt() -> true diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index 16383f8b953e..277ef14ff9f9 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -12,8 +12,8 @@ contract B contract A { function f() public payable returns (uint, uint, uint) { B x = new B{salt: "abc", value: 3}(7); - B y = new B{value: 3}{salt: "abc"}(8); - B z = new B{value: 3, salt: "abc"}(9); + B y = new B{value: 3, salt: "abc"}(8); + B z = new B{salt: "abc", value: 3}(9); return (x.getBalance(), y.getBalance(), z.getBalance()); } } diff --git a/test/libsolidity/semanticTests/shifts/shift_cleanup.sol b/test/libsolidity/semanticTests/shifts/shift_cleanup.sol deleted file mode 100644 index 81296ba9520f..000000000000 --- a/test/libsolidity/semanticTests/shifts/shift_cleanup.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract C { - function f() public returns (uint16 x) { - x = 0xffff; - x += 32; - x <<= 8; - x >>= 16; - } -} - -// ---- -// f() -> 0x0 diff --git a/test/libsolidity/semanticTests/smoke/alignment.sol b/test/libsolidity/semanticTests/smoke/alignment.sol index 6215814685de..bb4ba9317fd0 100644 --- a/test/libsolidity/semanticTests/smoke/alignment.sol +++ b/test/libsolidity/semanticTests/smoke/alignment.sol @@ -18,6 +18,8 @@ contract D { return (stateBool, stateDecimal, stateBytes); } } +// ==== +// compileViaYul: also // ---- // stateBool() -> true // stateBool() -> right(true) diff --git a/test/libsolidity/semanticTests/smoke/arrays.sol b/test/libsolidity/semanticTests/smoke/arrays.sol index 34ec984bb834..85d721d14767 100644 --- a/test/libsolidity/semanticTests/smoke/arrays.sol +++ b/test/libsolidity/semanticTests/smoke/arrays.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct T { @@ -32,6 +32,8 @@ contract C { return (["any", "any"], ["any", "any", "any"]); } } +// ==== +// compileViaYul: also // ---- // r() -> true, false, true // s() -> 123, 456, 789 @@ -41,4 +43,3 @@ contract C { // w2() -> 0x20, 0x40, 0x80, 3, "any", 3, "any" // w3() -> 0x20, 0x60, 0xa0, 0xe0, 3, "any", 3, "any", 3, "any" // x() -> 0x40, 0x0100, 0x40, 0x80, 3, "any", 3, "any", 0x60, 0xa0, 0xe0, 3, "any", 3, "any", 3, "any" - diff --git a/test/libsolidity/semanticTests/smoke/basic.sol b/test/libsolidity/semanticTests/smoke/basic.sol index 892ee8702d58..206e59d9df51 100644 --- a/test/libsolidity/semanticTests/smoke/basic.sol +++ b/test/libsolidity/semanticTests/smoke/basic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function d() public { @@ -13,7 +13,7 @@ contract C { return (2, 3); } function h(uint x, uint y) public pure returns (uint) { - return x - y; + unchecked { return x - y; } } function i(bool b) public pure returns (bool) { return !b; @@ -28,6 +28,8 @@ contract C { return a * 7; } } +// ==== +// compileViaYul: also // ---- // d() -> // e(), 1 wei -> 1 diff --git a/test/libsolidity/semanticTests/smoke/failure.sol b/test/libsolidity/semanticTests/smoke/failure.sol index 95b8c9109e25..1ec82c003ce0 100644 --- a/test/libsolidity/semanticTests/smoke/failure.sol +++ b/test/libsolidity/semanticTests/smoke/failure.sol @@ -14,12 +14,13 @@ contract C { } } // ==== -// compileViaYul: also +// compileToEwasm: also // EVMVersion: >homestead // allowNonExistingFunctions: true +// compileViaYul: also // ---- // _() -> FAILURE -// e() -> FAILURE, hex"08c379a0", 0x20, 19, "Transaction failed." -// f(bool): false -> FAILURE, hex"08c379a0", 0x20, 0 -// g(bool): false -> FAILURE, hex"08c379a0", 0x20, 15, "Value is false." -// h() -> FAILURE +// e() -> FAILURE, hex"08c379a0", 0x20, 0x13, "Transaction failed." +// f(bool): false -> FAILURE, hex"08c379a0", 0x20, 0x00 +// g(bool): false -> FAILURE, hex"08c379a0", 0x20, 0x0f, "Value is false." +// h() -> FAILURE, hex"4e487b71", 0x01 diff --git a/test/libsolidity/semanticTests/smoke/multiline.sol b/test/libsolidity/semanticTests/smoke/multiline.sol index ff8ee813e117..2639a6c6d610 100644 --- a/test/libsolidity/semanticTests/smoke/multiline.sol +++ b/test/libsolidity/semanticTests/smoke/multiline.sol @@ -4,12 +4,12 @@ contract C { } } // ==== -// allowNonExistingFunctions: true // compileViaYul: also +// compileToEwasm: also +// allowNonExistingFunctions: true // ---- // f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1 // -> 5 // g() // # g() does not exist # // -> FAILURE - diff --git a/test/libsolidity/semanticTests/smoke/multiline_comments.sol b/test/libsolidity/semanticTests/smoke/multiline_comments.sol index 3e1d59a6ddd1..ed332e245349 100644 --- a/test/libsolidity/semanticTests/smoke/multiline_comments.sol +++ b/test/libsolidity/semanticTests/smoke/multiline_comments.sol @@ -5,6 +5,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1 // # A comment on the function parameters. # @@ -17,4 +18,3 @@ contract C { // 1 // -> 5 // # Should return sum of all parameters. # - diff --git a/test/libsolidity/semanticTests/smoke/structs.sol b/test/libsolidity/semanticTests/smoke/structs.sol index 6a7286e57459..a6d9fbfc6c75 100644 --- a/test/libsolidity/semanticTests/smoke/structs.sol +++ b/test/libsolidity/semanticTests/smoke/structs.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { @@ -19,6 +19,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // s() -> 23, 42 // t() -> 0x20, 23, 42, 0x60, 3, "any" diff --git a/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol b/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol index 5d18cf32d7c3..79143aa9a3c6 100644 --- a/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol +++ b/test/libsolidity/semanticTests/specialFunctions/abi_functions_member_access.sol @@ -9,5 +9,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> diff --git a/test/libsolidity/semanticTests/state/block_chainid.sol b/test/libsolidity/semanticTests/state/block_chainid.sol new file mode 100644 index 000000000000..473629d8d938 --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_chainid.sol @@ -0,0 +1,12 @@ +contract C { + function f() public returns (uint) { + return block.chainid; + } +} +// ==== +// EVMVersion: >=istanbul +// compileViaYul: also +// ---- +// f() -> 1 +// f() -> 1 +// f() -> 1 diff --git a/test/libsolidity/semanticTests/state/block_coinbase.sol b/test/libsolidity/semanticTests/state/block_coinbase.sol new file mode 100644 index 000000000000..45cb64c5de9a --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_coinbase.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (address payable) { + return block.coinbase; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x7878787878787878787878787878787878787878 +// f() -> 0x7878787878787878787878787878787878787878 +// f() -> 0x7878787878787878787878787878787878787878 diff --git a/test/libsolidity/semanticTests/state/block_difficulty.sol b/test/libsolidity/semanticTests/state/block_difficulty.sol new file mode 100644 index 000000000000..1b5966fb067e --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_difficulty.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint) { + return block.difficulty; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 200000000 +// f() -> 200000000 +// f() -> 200000000 diff --git a/test/libsolidity/semanticTests/state/block_gaslimit.sol b/test/libsolidity/semanticTests/state/block_gaslimit.sol new file mode 100644 index 000000000000..7fb313825947 --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_gaslimit.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint) { + return block.gaslimit; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 20000000 +// f() -> 20000000 +// f() -> 20000000 diff --git a/test/libsolidity/semanticTests/state/block_number.sol b/test/libsolidity/semanticTests/state/block_number.sol new file mode 100644 index 000000000000..a08261b920ce --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_number.sol @@ -0,0 +1,12 @@ +contract C { + constructor() {} + function f() public returns (uint) { + return block.number; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// f() -> 2 +// f() -> 3 diff --git a/test/libsolidity/semanticTests/state/block_timestamp.sol b/test/libsolidity/semanticTests/state/block_timestamp.sol new file mode 100644 index 000000000000..4b4bba38dc0e --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_timestamp.sol @@ -0,0 +1,12 @@ +contract C { + constructor() {} + function f() public returns (uint) { + return block.timestamp; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() # This is the 1st block # +// f() -> 0x1e # This is the 2nd block (each block is "15 seconds") # +// f() -> 0x2d # This is the 3rd block # diff --git a/test/libsolidity/semanticTests/state/blockhash_basic.sol b/test/libsolidity/semanticTests/state/blockhash_basic.sol new file mode 100644 index 000000000000..528c8aea64b5 --- /dev/null +++ b/test/libsolidity/semanticTests/state/blockhash_basic.sol @@ -0,0 +1,23 @@ +contract C { + bytes32 public genesisHash; + bytes32 public currentHash; + constructor() { + require(block.number == 1); + genesisHash = blockhash(0); + currentHash = blockhash(1); + } + function f(uint blockNumber) public returns (bytes32) { + return blockhash(blockNumber); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737 +// currentHash() -> 0 +// f(uint256): 0 -> 0x3737373737373737373737373737373737373737373737373737373737373737 +// f(uint256): 1 -> 0x3737373737373737373737373737373737373737373737373737373737373738 +// f(uint256): 255 -> 0x00 +// f(uint256): 256 -> 0x00 +// f(uint256): 257 -> 0x00 diff --git a/test/libsolidity/semanticTests/state/gasleft.sol b/test/libsolidity/semanticTests/state/gasleft.sol new file mode 100644 index 000000000000..0339ff17a229 --- /dev/null +++ b/test/libsolidity/semanticTests/state/gasleft.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (bool) { + return gasleft() > 0; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> true +// f() -> true +// f() -> true diff --git a/test/libsolidity/semanticTests/state/msg_data.sol b/test/libsolidity/semanticTests/state/msg_data.sol new file mode 100644 index 000000000000..6217da3b6699 --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_data.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (bytes calldata) { + return msg.data; + } + function g(uint,bool) public returns (bytes calldata) { + return msg.data; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x20, 4, 17219911917854084299749778639755835327755045716242581057573779540915269926912 +// g(uint256,bool): 1234, true -> 0x20, 0x44, 35691323728519381642872894128098848782337736632589179916067422734266033766400, 33268574187263889506619096617382224251268236217415066441681855047532544, 26959946667150639794667015087019630673637144422540572481103610249216 diff --git a/test/libsolidity/semanticTests/state/msg_sender.sol b/test/libsolidity/semanticTests/state/msg_sender.sol new file mode 100644 index 000000000000..57c7fd5f3b9f --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_sender.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (address) { + return msg.sender; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x1212121212121212121212121212120000000012 diff --git a/test/libsolidity/semanticTests/state/msg_sig.sol b/test/libsolidity/semanticTests/state/msg_sig.sol new file mode 100644 index 000000000000..e7c7b1b75c0c --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_sig.sol @@ -0,0 +1,13 @@ +contract C { + function f() public returns (bytes4) { + return msg.sig; + } + function g() public returns (bytes4) { + return msg.sig; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// g() -> 0xe2179b8e00000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/state/msg_value.sol b/test/libsolidity/semanticTests/state/msg_value.sol new file mode 100644 index 000000000000..fa7ca3db6feb --- /dev/null +++ b/test/libsolidity/semanticTests/state/msg_value.sol @@ -0,0 +1,10 @@ +contract C { + function f() public payable returns (uint) { + return msg.value; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0 +// f(), 12 ether -> 12000000000000000000 diff --git a/test/libsolidity/semanticTests/state/tx_gasprice.sol b/test/libsolidity/semanticTests/state/tx_gasprice.sol new file mode 100644 index 000000000000..321e2f55e723 --- /dev/null +++ b/test/libsolidity/semanticTests/state/tx_gasprice.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (uint) { + return tx.gasprice; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 3000000000 +// f() -> 3000000000 +// f() -> 3000000000 diff --git a/test/libsolidity/semanticTests/state/tx_origin.sol b/test/libsolidity/semanticTests/state/tx_origin.sol new file mode 100644 index 000000000000..34f19f6161ba --- /dev/null +++ b/test/libsolidity/semanticTests/state/tx_origin.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (address) { + return tx.origin; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x9292929292929292929292929292929292929292 +// f() -> 0x9292929292929292929292929292929292929292 +// f() -> 0x9292929292929292929292929292929292929292 diff --git a/test/libsolidity/semanticTests/state/uncalled_blockhash.sol b/test/libsolidity/semanticTests/state/uncalled_blockhash.sol new file mode 100644 index 000000000000..9a964335ddbe --- /dev/null +++ b/test/libsolidity/semanticTests/state/uncalled_blockhash.sol @@ -0,0 +1,9 @@ +contract C { + function f() public returns (bytes32) { + return (blockhash)(block.number - 1); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x3737373737373737373737373737373737373737373737373737373737373738 diff --git a/test/libsolidity/semanticTests/state_var_initialization.sol b/test/libsolidity/semanticTests/state_var_initialization.sol index 5da329fa41fe..93b77fbcfdec 100644 --- a/test/libsolidity/semanticTests/state_var_initialization.sol +++ b/test/libsolidity/semanticTests/state_var_initialization.sol @@ -9,6 +9,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // i() -> 2 // k() -> 0 diff --git a/test/libsolidity/semanticTests/state_variables_init_order.sol b/test/libsolidity/semanticTests/state_variables_init_order.sol index ef63a38f964e..41169d05401f 100644 --- a/test/libsolidity/semanticTests/state_variables_init_order.sol +++ b/test/libsolidity/semanticTests/state_variables_init_order.sol @@ -10,5 +10,6 @@ contract B is A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// x() -> 1 \ No newline at end of file +// x() -> 1 diff --git a/test/libsolidity/semanticTests/state_variables_init_order_2.sol b/test/libsolidity/semanticTests/state_variables_init_order_2.sol index 67e38da6ba15..f0ac9013fd64 100644 --- a/test/libsolidity/semanticTests/state_variables_init_order_2.sol +++ b/test/libsolidity/semanticTests/state_variables_init_order_2.sol @@ -14,5 +14,6 @@ contract B is A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// z() -> 1 \ No newline at end of file +// z() -> 1 diff --git a/test/libsolidity/semanticTests/statements/do_while_loop_continue.sol b/test/libsolidity/semanticTests/statements/do_while_loop_continue.sol index 19b4f38277cd..6274640d121e 100644 --- a/test/libsolidity/semanticTests/statements/do_while_loop_continue.sol +++ b/test/libsolidity/semanticTests/statements/do_while_loop_continue.sol @@ -12,5 +12,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 42 diff --git a/test/libsolidity/semanticTests/storage/array_accessor.sol b/test/libsolidity/semanticTests/storage/array_accessor.sol index 599931a372c7..f9efae437cdd 100644 --- a/test/libsolidity/semanticTests/storage/array_accessor.sol +++ b/test/libsolidity/semanticTests/storage/array_accessor.sol @@ -22,6 +22,8 @@ contract test { multiple_map[2][1][2].finalArray[3] = 5; } } +// ==== +// compileViaYul: also // ---- // data(uint256): 0 -> 8 // data(uint256): 8 -> FAILURE diff --git a/test/libsolidity/semanticTests/storage/chop_sign_bits.sol b/test/libsolidity/semanticTests/storage/chop_sign_bits.sol index 51cba0261264..4993ecb35ca0 100644 --- a/test/libsolidity/semanticTests/storage/chop_sign_bits.sol +++ b/test/libsolidity/semanticTests/storage/chop_sign_bits.sol @@ -19,6 +19,8 @@ contract Test { return z; } } +// ==== +// compileViaYul: also // ---- // x(uint256): 0 -> -1 // x(uint256): 1 -> -2 diff --git a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol new file mode 100644 index 000000000000..234f0191f0c5 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol @@ -0,0 +1,31 @@ +contract Test { + bytes x; + function set(bytes memory _a) public { x = _a; } +} +// ==== +// compileViaYul: also +// ---- +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty +// set(bytes): 0x20, 31, "1234567890123456789012345678901" +// storage: nonempty +// set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX" +// storage: nonempty +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty +// set(bytes): 0x20, 66, "12345678901234567890123456789012", "12345678901234567890123456789012", "12" +// storage: nonempty +// set(bytes): 0x20, 3, "abc" +// storage: nonempty +// set(bytes): 0x20, 0 +// storage: empty diff --git a/test/libsolidity/semanticTests/storage/mapping_state.sol b/test/libsolidity/semanticTests/storage/mapping_state.sol new file mode 100644 index 000000000000..28178bca2083 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/mapping_state.sol @@ -0,0 +1,46 @@ +contract Ballot { + mapping(address => bool) canVote; + mapping(address => uint) voteCount; + mapping(address => bool) voted; + function getVoteCount(address addr) public returns (uint retVoteCount) { + return voteCount[addr]; + } + function grantVoteRight(address addr) public { + canVote[addr] = true; + } + function vote(address voter, address vote) public returns (bool success) { + if (!canVote[voter] || voted[voter]) return false; + voted[voter] = true; + voteCount[vote] = voteCount[vote] + 1; + return true; + } +} +// ==== +// compileViaYul: also +// ---- +// getVoteCount(address): 0 -> 0 +// getVoteCount(address): 1 -> 0 +// getVoteCount(address): 2 -> 0 +// vote(address,address): 0, 2 -> false +// getVoteCount(address): 0 -> 0 +// getVoteCount(address): 1 -> 0 +// getVoteCount(address): 2 -> 0 +// grantVoteRight(address): 0 -> +// grantVoteRight(address): 1 -> +// vote(address,address): 0, 2 -> true +// getVoteCount(address): 0 -> 0 +// getVoteCount(address): 1 -> 0 +// getVoteCount(address): 2 -> 1 +// vote(address,address): 0, 1 -> false +// getVoteCount(address): 0 -> 0 +// getVoteCount(address): 1 -> 0 +// getVoteCount(address): 2 -> 1 +// vote(address,address): 2, 1 -> false +// getVoteCount(address): 0 -> 0 +// getVoteCount(address): 1 -> 0 +// getVoteCount(address): 2 -> 1 +// grantVoteRight(address): 2 -> +// vote(address,address): 2, 1 -> true +// getVoteCount(address): 0 -> 0 +// getVoteCount(address): 1 -> 1 +// getVoteCount(address): 2 -> 1 diff --git a/test/libsolidity/semanticTests/storage/packed_functions.sol b/test/libsolidity/semanticTests/storage/packed_functions.sol index 4a49a614f3b5..32597923b84b 100644 --- a/test/libsolidity/semanticTests/storage/packed_functions.sol +++ b/test/libsolidity/semanticTests/storage/packed_functions.sol @@ -39,6 +39,8 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // set() -> // t1() -> 7 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol b/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol index 9b20dbfd2c55..4a84544caa2f 100644 --- a/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol +++ b/test/libsolidity/semanticTests/storage/packed_storage_overflow.sol @@ -4,10 +4,10 @@ contract C { uint16 b; function f() public returns (uint256, uint256, uint256, uint256) { - a++; + unchecked { a++; } uint256 c = b; delete b; - a -= 2; + unchecked { a -= 2; } return (x, c, b, a); } } diff --git a/test/libsolidity/semanticTests/storage/packed_storage_signed.sol b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol index 8db0c757ed3b..2f789a41b6d8 100644 --- a/test/libsolidity/semanticTests/storage/packed_storage_signed.sol +++ b/test/libsolidity/semanticTests/storage/packed_storage_signed.sol @@ -9,14 +9,18 @@ contract C { returns (uint256 x1, uint256 x2, uint256 x3, uint256 x4) { a = -2; - b = -uint8(a) * 2; - c = a * int8(120) * int8(121); - x1 = uint256(a); + unchecked { + b = (0 - uint8(a)) * 2; + c = a * int8(120) * int8(121); + } + x1 = uint256(int256(a)); x2 = b; - x3 = uint256(c); + x3 = uint256(int256(c)); x4 = d; } } +// ==== +// compileViaYul: also // ---- // test() -> -2, 4, -112, 0 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol index 50a0d68f7725..fe1738265f0a 100644 --- a/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_bytes.sol @@ -43,5 +43,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> true diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol index 3f2983f46236..7c3fb564d502 100644 --- a/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_enum.sol @@ -31,5 +31,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 1 diff --git a/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol b/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol index 611fda3ccf57..1da275113d8a 100644 --- a/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol +++ b/test/libsolidity/semanticTests/storage/packed_storage_structs_uint.sol @@ -28,5 +28,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 1 diff --git a/test/libsolidity/semanticTests/storage/simple_accessor.sol b/test/libsolidity/semanticTests/storage/simple_accessor.sol index 70e8a331f3f8..600ffd2af2ad 100644 --- a/test/libsolidity/semanticTests/storage/simple_accessor.sol +++ b/test/libsolidity/semanticTests/storage/simple_accessor.sol @@ -6,5 +6,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // data() -> 8 diff --git a/test/libsolidity/semanticTests/storage/state_smoke_test.sol b/test/libsolidity/semanticTests/storage/state_smoke_test.sol index dc82e9129b58..4f5a4080d6f3 100644 --- a/test/libsolidity/semanticTests/storage/state_smoke_test.sol +++ b/test/libsolidity/semanticTests/storage/state_smoke_test.sol @@ -12,6 +12,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // get(uint8): 0x00 -> 0 // get(uint8): 0x01 -> 0 diff --git a/test/libsolidity/semanticTests/strings/empty_string.sol b/test/libsolidity/semanticTests/strings/empty_string.sol new file mode 100644 index 000000000000..19a54497a6f5 --- /dev/null +++ b/test/libsolidity/semanticTests/strings/empty_string.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure returns (string memory) { + return ""; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/strings/empty_string_input.sol b/test/libsolidity/semanticTests/strings/empty_string_input.sol new file mode 100644 index 000000000000..c1f0bca22522 --- /dev/null +++ b/test/libsolidity/semanticTests/strings/empty_string_input.sol @@ -0,0 +1,30 @@ +contract C { + function f() public pure returns (string memory) { + return ""; + } + function g(string calldata msg) public pure returns (string memory) { + return msg; + } + function h(string calldata msg, uint256 v) public pure returns (string memory, uint256) { + return (msg, v); + } + // Adjusting order of input/output intentionally. + function i(string calldata msg1, uint256 v, string calldata msg2) public pure returns (string memory, string memory, uint256) { + return (msg1, msg2, v); + } + function j(string calldata msg1, uint256 v) public pure returns (string memory, string memory, uint256) { + return (msg1, "", v); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x20, 0 +// g(string): 0x20, 0, "" -> 0x20, 0 +// g(string): 0x20, 0 -> 0x20, 0 +// h(string,uint256): 0x40, 0x888, 0, "" -> 0x40, 0x0888, 0 +// h(string,uint256): 0x40, 0x888, 0 -> 0x40, 0x0888, 0 +// i(string,uint256,string): 0x60, 0x888, 0x60, 0, "" -> 0x60, 0x80, 0x0888, 0, 0 +// i(string,uint256,string): 0x60, 0x888, 0x60, 0 -> 0x60, 0x80, 0x0888, 0, 0 +// j(string,uint256): 0x40, 0x888, 0, "" -> 0x60, 0x80, 0x0888, 0, 0 +// j(string,uint256): 0x40, 0x888, 0 -> 0x60, 0x80, 0x0888, 0, 0 diff --git a/test/libsolidity/semanticTests/strings/string_escapes.sol b/test/libsolidity/semanticTests/strings/string_escapes.sol new file mode 100644 index 000000000000..66a8b1ea3f93 --- /dev/null +++ b/test/libsolidity/semanticTests/strings/string_escapes.sol @@ -0,0 +1,10 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "\t\n\r\'\"\\"; + return escapeCharacters; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x090a0d27225c0000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/strings/unicode_escapes.sol b/test/libsolidity/semanticTests/strings/unicode_escapes.sol index fc57aa48ed74..c16e306a7332 100644 --- a/test/libsolidity/semanticTests/strings/unicode_escapes.sol +++ b/test/libsolidity/semanticTests/strings/unicode_escapes.sol @@ -17,6 +17,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // oneByteUTF8() -> 0x20, 7, "aaa$aaa" // twoBytesUTF8() -> 0x20, 8, "aaa\xc2\xa2aaa" diff --git a/test/libsolidity/semanticTests/strings/unicode_string.sol b/test/libsolidity/semanticTests/strings/unicode_string.sol index ceea9702ab64..a8c98e4a81d1 100644 --- a/test/libsolidity/semanticTests/strings/unicode_string.sol +++ b/test/libsolidity/semanticTests/strings/unicode_string.sol @@ -9,6 +9,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x20, 0x14, "\xf0\x9f\x98\x83, \xf0\x9f\x98\xad, and \xf0\x9f\x98\x88" // g() -> 0x20, 0x14, "\xf0\x9f\x98\x83, \xf0\x9f\x98\xad, and \xf0\x9f\x98\x88" diff --git a/test/libsolidity/semanticTests/structs/array_of_recursive_struct.sol b/test/libsolidity/semanticTests/structs/array_of_recursive_struct.sol index 16ee9d59b4c1..0aaa6ea93e6b 100644 --- a/test/libsolidity/semanticTests/structs/array_of_recursive_struct.sol +++ b/test/libsolidity/semanticTests/structs/array_of_recursive_struct.sol @@ -8,5 +8,8 @@ contract Test { assert(val[0].vals.length == 42); } } -// ----- +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- // func() -> diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_nested_structs.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_nested_structs.sol new file mode 100644 index 000000000000..72e65b9ef487 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_nested_structs.sol @@ -0,0 +1,49 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + + struct S1 { + uint128 u; + S s; + } + + struct S2 { + S[2] array; + } + + function f1(S1 calldata c) internal returns(S1 calldata) { + return c; + } + + function f(S1 calldata c, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + S1 memory m = f1(c); + assert(m.s.a[0][0] == c.s.a[0][0]); + assert(m.s.a[1][1] == c.s.a[1][1]); + return (p, m.s.p1, m.s.a[0][0], m.s.a[1][1], m.s.p2); + } + + function g(S2 calldata c) external returns(uint128, uint256, uint256, uint32) { + S2 memory m = c; + assert(m.array[0].a[0][0] == c.array[0].a[0][0]); + assert(m.array[0].a[1][1] == c.array[0].a[1][1]); + return (m.array[1].p1, m.array[1].a[0][0], m.array[1].a[1][1], m.array[1].p2); + } + + function h(S1 calldata c, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + S memory m = c.s; + assert(m.a[0][0] == c.s.a[0][0]); + assert(m.a[1][1] == c.s.a[1][1]); + return (p, m.p1, m.a[0][0], m.a[1][1], m.p2); + } +} +// ==== +// compileViaYul: also +// ---- +// f((uint128, (uint128, uint256[][2], uint32)), uint32): 0x40, 44, 11, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 44, 22, 1, 2, 33 +// g(((uint128, uint256[][2], uint32)[2])): 0x20, 0x20, 0x40, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 22, 1, 2, 33 +// h((uint128, (uint128, uint256[][2], uint32)), uint32): 0x40, 44, 11, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 44, 22, 1, 2, 33 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol index d3a79a181e23..37a4baab4c20 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -15,5 +15,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol index 21fada9c232c..7d4d124bb84a 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_and_ints.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol index 3e8613fad9b5..0439aeff584b 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -19,6 +19,7 @@ contract C { c = s.c; } } - +// ==== +// compileViaYul: also // ---- // f((uint256,uint256[2],uint256)): 42, 1, 2, 23 -> 42, 1, 2, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member_dynamic.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member_dynamic.sol new file mode 100644 index 000000000000..bac400295297 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_array_member_dynamic.sol @@ -0,0 +1,24 @@ +pragma abicoder v2; + +contract C { + struct S { + uint32 a; + uint256[] b; + uint64 c; + } + + function f(S calldata s) + external + pure + returns (uint32 a, uint256 b0, uint256 b1, uint64 c) + { + a = s.a; + b0 = s.b[0]; + b1 = s.b[1]; + c = s.c; + } +} +// ==== +// compileViaYul: also +// ---- +// f((uint32,uint256[],uint64)): 0x20, 42, 0x60, 23, 2, 1, 2 -> 42, 1, 2, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_argument_of_lib_function.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_argument_of_lib_function.sol new file mode 100644 index 000000000000..ea405da907a4 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_argument_of_lib_function.sol @@ -0,0 +1,29 @@ +pragma abicoder v2; + +struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; +} +struct S1 { + uint128 u; + S s; +} + +library L { + function f(S1 memory m, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + return (p, m.s.p1, m.s.a[0][0], m.s.a[1][1], m.s.p2); + } +} + +contract C { + + function f(S1 calldata c, uint32 p) external returns(uint32, uint128, uint256, uint256, uint32) { + return L.f(c, p); + } +} +// ==== +// compileViaYul: also +// ---- +// library: L +// f((uint128, (uint128, uint256[][2], uint32)), uint32): 0x40, 44, 11, 0x40, 22, 0x60, 33, 0x40, 0x40, 2, 1, 2 -> 44, 22, 1, 2, 33 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_memory_argument.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_memory_argument.sol new file mode 100644 index 000000000000..ae7bea1527d7 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_as_memory_argument.sol @@ -0,0 +1,23 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + + function g(uint32 p1, S memory s) internal returns(uint32, uint128, uint256, uint256, uint32) { + s.p1++; + s.a[0][1]++; + return (p1, s.p1, s.a[0][0], s.a[1][1], s.p2); + } + + function f(uint32 p1, S calldata c) external returns(uint32, uint128, uint256, uint256, uint32) { + return g(p1, c); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 78, 1, 2, 88 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_struct_member.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_struct_member.sol new file mode 100644 index 000000000000..fede46d6a320 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_struct_member.sol @@ -0,0 +1,28 @@ +pragma abicoder v2; + +contract C { + struct S { + uint64 a; + uint64 b; + } + struct S1 { + uint256 a; + S s; + uint256 c; + } + + function f(S1 calldata s1) + external + pure + returns (uint256 a, uint64 b0, uint64 b1, uint256 c) + { + a = s1.a; + b0 = s1.s.a; + b1 = s1.s.b; + c = s1.c; + } +} +// ==== +// compileViaYul: also +// ---- +// f((uint256,(uint64, uint64),uint256)): 42, 1, 2, 23 -> 42, 1, 2, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_struct_member_dynamic.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_struct_member_dynamic.sol new file mode 100644 index 000000000000..b2a651504f09 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_struct_member_dynamic.sol @@ -0,0 +1,28 @@ +pragma abicoder v2; + +contract C { + struct S { + uint64 a; + bytes b; + } + struct S1 { + uint256 a; + S s; + uint256 c; + } + + function f(S1 calldata s1) + external + pure + returns (uint256 a, uint64 b0, bytes1 b1, uint256 c) + { + a = s1.a; + b0 = s1.s.a; + b1 = s1.s.b[0]; + c = s1.c; + } +} +// ==== +// compileViaYul: also +// ---- +// f((uint256,(uint64, bytes),uint256)): 0x20, 42, 0x60, 23, 1, 0x40, 2, "ab" -> 42, 1, "a", 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol index db8171a202d0..fd9c570d0408 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory.sol @@ -1,17 +1,20 @@ -pragma experimental ABIEncoderV2; - +pragma abicoder v2; contract C { struct S { uint256 a; uint256 b; + bytes2 c; } - function f(S calldata s) external pure returns (uint256, uint256) { + function f(S calldata s) external pure returns (uint256, uint256, bytes1) { S memory m = s; - return (m.a, m.b); + return (m.a, m.b, m.c[1]); } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- -// f((uint256,uint256)): 42, 23 -> 42, 23 +// f((uint256,uint256,bytes2)): 42, 23, "ab" -> 42, 23, "b" diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory_tuple_assignment.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory_tuple_assignment.sol new file mode 100644 index 000000000000..9d928fb6aee9 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_memory_tuple_assignment.sol @@ -0,0 +1,22 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + + function f(uint32 p1, S calldata c) external returns(uint32, uint128, uint256, uint256, uint32) { + S memory m; + uint32 p2; + (p2, m) = (p1, c); + m.p1++; + m.a[0][1]++; + return (p2, m.p1, m.a[0][0], m.a[1][1], m.p2); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 78, 1, 2, 88 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol new file mode 100644 index 000000000000..64332470533a --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_to_storage.sol @@ -0,0 +1,23 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256 a; + uint64 b; + bytes2 c; + } + + uint[153] r; + S s; + + function f(uint32 a, S calldata c, uint256 b) external returns (uint256, uint256, bytes1) { + s = c; + return (s.a, s.b, s.c[1]); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint32,(uint256,uint64,bytes2),uint256): 1, 42, 23, "ab", 1 -> 42, 23, "b" diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_array_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_array_to_memory.sol new file mode 100644 index 000000000000..25530a181529 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_array_to_memory.sol @@ -0,0 +1,23 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256 a; + uint256[2] b; + uint256 c; + } + + function f(S calldata c) + external + pure + returns (uint256, uint256, uint256, uint256) + { + S memory m = c; + return (m.a, m.b[0], m.b[1], m.c); + } +} + +// ==== +// compileViaYul: also +// ---- +// f((uint256,uint256[2],uint256)): 42, 1, 2, 23 -> 42, 1, 2, 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_bytes_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_bytes_to_memory.sol new file mode 100644 index 000000000000..f62742fa765b --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_bytes_to_memory.sol @@ -0,0 +1,23 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256 a; + bytes b; + uint256 c; + } + + function f(S calldata c) + external + pure + returns (uint256, bytes1, bytes1, uint256) + { + S memory m = c; + return (m.a, m.b[0], m.b[1], m.c); + } +} + +// ==== +// compileViaYul: also +// ---- +// f((uint256,bytes,uint256)): 0x20, 42, 0x60, 23, 2, "ab" -> 42, "a", "b", 23 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_memory.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_memory.sol new file mode 100644 index 000000000000..a84c88851c7a --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_memory.sol @@ -0,0 +1,23 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + function f(uint32 p1, S calldata c) external returns(uint32, uint128, uint256, uint256, uint32) { + S memory s = c; + assert(s.a[0][0] == c.a[0][0]); + assert(s.a[1][1] == c.a[1][1]); + s.p1++; + assert(s.p1 != c.p1); + s.a[0][1]++; + assert(s.a[0][1] != c.a[0][1]); + return (p1, s.p1, s.a[0][0], s.a[1][1], s.p2); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 78, 1, 2, 88 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol new file mode 100644 index 000000000000..efcb2a639b1f --- /dev/null +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol @@ -0,0 +1,20 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 p1; + uint256[][2] a; + uint32 p2; + } + S s; + function f(uint32 p1, S calldata c) external returns(uint32, uint128, uint256, uint256, uint32) { + s = c; + assert(s.a[0][0] == c.a[0][0]); + assert(s.a[1][1] == c.a[1][1]); + return (p1, s.p1, s.a[0][0], s.a[1][1], s.p2); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint32, (uint128, uint256[][2], uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 77, 1, 2, 88 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol index 0e62ee4ef319..cfcb43458b1d 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_structs.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -25,5 +25,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f((uint256,uint256),(uint256),(uint256,uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/structs/calldata/dynamic_nested.sol b/test/libsolidity/semanticTests/structs/calldata/dynamic_nested.sol index 8d6b7310300b..a22a11bf501a 100644 --- a/test/libsolidity/semanticTests/structs/calldata/dynamic_nested.sol +++ b/test/libsolidity/semanticTests/structs/calldata/dynamic_nested.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S2 { uint256 b; } @@ -9,5 +9,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f((uint256,(uint256)[])): 32, 17, 64, 2, 23, 42 -> 2, 17, 23, 42 diff --git a/test/libsolidity/semanticTests/structs/calldata/dynamically_encoded.sol b/test/libsolidity/semanticTests/structs/calldata/dynamically_encoded.sol index 815de9a28574..b31fe3554774 100644 --- a/test/libsolidity/semanticTests/structs/calldata/dynamically_encoded.sol +++ b/test/libsolidity/semanticTests/structs/calldata/dynamically_encoded.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint256[] a; } @@ -8,5 +8,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f((uint256[])): 32, 32, 2, 42, 23 -> 2, 42, 23 diff --git a/test/libsolidity/semanticTests/structs/global.sol b/test/libsolidity/semanticTests/structs/global.sol index de8fe21b3337..6f943dbf7eb2 100644 --- a/test/libsolidity/semanticTests/structs/global.sol +++ b/test/libsolidity/semanticTests/structs/global.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; struct S { uint256 a; uint256 b; } contract C { @@ -8,5 +8,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f((uint256,uint256)): 42, 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol b/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol index 829345d2e9f9..c17babcf96ed 100644 --- a/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol +++ b/test/libsolidity/semanticTests/structs/lone_struct_array_type.sol @@ -12,5 +12,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3 diff --git a/test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol b/test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol index 6cf5378b4b3f..32c36acaf172 100644 --- a/test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol +++ b/test/libsolidity/semanticTests/structs/memory_struct_named_constructor.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { @@ -14,5 +14,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // s() -> 8, true diff --git a/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol index 49bb6ba7a43b..616104cf912c 100644 --- a/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol +++ b/test/libsolidity/semanticTests/structs/memory_structs_as_function_args.sol @@ -30,5 +30,6 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 1, 2, 3 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_nested.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol index 40dba7f26fd8..972fe766dccd 100644 --- a/test/libsolidity/semanticTests/structs/memory_structs_nested.sol +++ b/test/libsolidity/semanticTests/structs/memory_structs_nested.sol @@ -40,5 +40,6 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 1, 2, 3, 4 diff --git a/test/libsolidity/semanticTests/structs/nested_struct_allocation.sol b/test/libsolidity/semanticTests/structs/nested_struct_allocation.sol index 6a795472684e..787836c93658 100644 --- a/test/libsolidity/semanticTests/structs/nested_struct_allocation.sol +++ b/test/libsolidity/semanticTests/structs/nested_struct_allocation.sol @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/structs/recursive_struct_2.sol b/test/libsolidity/semanticTests/structs/recursive_struct_2.sol new file mode 100644 index 000000000000..5214b1a483e1 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/recursive_struct_2.sol @@ -0,0 +1,26 @@ +contract C { + struct S { + uint16 v; + S[] x; + } + uint8[77] padding; + S s; + constructor() { + s.v = 21; + s.x.push(); s.x.push(); s.x.push(); + s.x[0].v = 101; s.x[1].v = 102; s.x[2].v = 103; + } + function f() public returns (uint256 a, uint256 b, uint256 c, uint256 d) { + S storage sptr1 = s.x[0]; + S storage sptr2 = s.x[1]; + S storage sptr3 = s.x[2]; + uint256 slot1; uint256 slot2; uint256 slot3; + assembly { slot1 := sptr1.slot slot2 := sptr2.slot slot3 := sptr3.slot } + delete s; + assembly { a := sload(s.slot) b := sload(slot1) c := sload(slot2) d := sload(slot3) } + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0, 0, 0, 0 diff --git a/test/libsolidity/semanticTests/structs/recursive_structs.sol b/test/libsolidity/semanticTests/structs/recursive_structs.sol index da568a25548d..8bf9bfc568f9 100644 --- a/test/libsolidity/semanticTests/structs/recursive_structs.sol +++ b/test/libsolidity/semanticTests/structs/recursive_structs.sol @@ -15,5 +15,8 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/structs/simple_struct_allocation.sol b/test/libsolidity/semanticTests/structs/simple_struct_allocation.sol index 7e3a54073be7..e52d2b2f4b41 100644 --- a/test/libsolidity/semanticTests/structs/simple_struct_allocation.sol +++ b/test/libsolidity/semanticTests/structs/simple_struct_allocation.sol @@ -10,5 +10,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol index ca6dffb9d5e1..c6dcaabbf8ab 100644 --- a/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol +++ b/test/libsolidity/semanticTests/structs/struct_assign_reference_to_struct.sol @@ -32,5 +32,8 @@ contract test { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // assign() -> 2, 2, 3, 3 diff --git a/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol b/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol index 7ce22e45aa6d..7868a620e5d7 100644 --- a/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol +++ b/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol @@ -26,5 +26,7 @@ contract C { x2 = s.s3.x2; } } +// ==== +// compileViaYul: also // ---- // get() -> 0x01, 0x00, 0x09, 0x00, 0x04, 0x05 diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol new file mode 100644 index 000000000000..e0f8d9fd9d02 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -0,0 +1,35 @@ +contract c { + struct Struct { uint a; bytes data; uint b; } + Struct data1; + Struct data2; + function set(uint _a, bytes calldata _data, uint _b) external returns (bool) { + data1.a = _a; + data1.b = _b; + data1.data = _data; + return true; + } + function copy() public returns (bool) { + data1 = data2; + return true; + } + function del() public returns (bool) { + delete data1; + return true; + } + function test(uint256 i) public returns (bytes1) { + return data1.data[i]; + } +} +// ==== +// compileViaYul: also +// ---- +// storage: empty +// set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true +// test(uint256): 32 -> "3" +// storage: nonempty +// copy() -> true +// storage: empty +// set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true +// storage: nonempty +// del() -> true +// storage: empty diff --git a/test/libsolidity/semanticTests/structs/struct_copy.sol b/test/libsolidity/semanticTests/structs/struct_copy.sol index 2849c16bcbf3..7c9ef9751a46 100644 --- a/test/libsolidity/semanticTests/structs/struct_copy.sol +++ b/test/libsolidity/semanticTests/structs/struct_copy.sol @@ -34,6 +34,8 @@ contract c { } } +// ==== +// compileViaYul: also // ---- // set(uint256): 7 -> true // retrieve(uint256): 7 -> 1, 3, 4, 2 diff --git a/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol index a1cfcac05df5..e81c6e76f06c 100644 --- a/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol +++ b/test/libsolidity/semanticTests/structs/struct_copy_via_local.sol @@ -3,6 +3,7 @@ contract c { uint256 a; uint256 b; } + uint[75] r; Struct data1; Struct data2; @@ -15,5 +16,8 @@ contract c { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // test() -> true diff --git a/test/libsolidity/semanticTests/structs/struct_delete_member.sol b/test/libsolidity/semanticTests/structs/struct_delete_member.sol index 2b3f41402909..4f83478c9acb 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_member.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_member.sol @@ -16,5 +16,8 @@ contract test { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // deleteMember() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage.sol new file mode 100644 index 000000000000..02602c3ebce3 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage.sol @@ -0,0 +1,24 @@ +contract C { + struct S { + uint256 x; + uint128 y; + uint32 z; + } + uint8 b = 23; + S s; + uint8 a = 17; + function f() public { + s.x = 42; s.y = 42; s.y = 42; + delete s; + assert(s.x == 0); + assert(s.y == 0); + assert(s.z == 0); + assert(b == 23); + assert(a == 17); + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol new file mode 100644 index 000000000000..441c299c3ee5 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol @@ -0,0 +1,35 @@ +contract C { + struct S { + uint32 a; + S[] x; + } + S s; + function f() public returns (uint256 r1, uint256 r2, uint256 r3) { + assembly { + // 2 ** 150 - 1 + sstore(s.slot, 1427247692705959881058285969449495136382746623) + } + s.a = 1; + s.x.push(); s.x.push(); + S storage ptr1 = s.x[0]; + S storage ptr2 = s.x[1]; + assembly { + // 2 ** 150 - 1 + sstore(ptr1.slot, 1427247692705959881058285969449495136382746623) + sstore(ptr2.slot, 1427247692705959881058285969449495136382746623) + } + s.x[0].a = 2; s.x[1].a = 3; + delete s; + assert(s.a == 0); + assert(s.x.length == 0); + assembly { + r1 := sload(s.slot) + r2 := sload(ptr1.slot) + r3 := sload(ptr2.slot) + } + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> 0, 0, 0 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_small.sol new file mode 100644 index 000000000000..342a6c4d804a --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_small.sol @@ -0,0 +1,25 @@ +contract C { + struct S { + uint64 y; + uint64 z; + } + S s; + function f() public returns (uint256 ret) { + assembly { + // 2 ** 150 - 1 + sstore(s.slot, 1427247692705959881058285969449495136382746623) + } + s.y = 1; s.z = 2; + delete s; + assert(s.y == 0); + assert(s.z == 0); + assembly { + ret := sload(s.slot) + } + } +} +// ==== +// compileViaYul: true +// compileToEwasm: also +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol new file mode 100644 index 000000000000..4194ff15bb1f --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol @@ -0,0 +1,47 @@ +pragma abicoder v2; + +contract C { + struct S { + uint128 a; + uint256[] x; + uint240 b; + } + uint8 b = 23; + S s; + uint8 a = 17; + function f() public { + delete s; + s.x.push(42); s.x.push(42); s.x.push(42); + delete s; + assert(s.x.length == 0); + uint256[] storage x = s.x; + assembly { sstore(x.slot, 3) } + assert(s.x[0] == 0); + assert(s.x[1] == 0); + assert(s.x[2] == 0); + assert(b == 23); + assert(a == 17); + } + + function g() public { + delete s; + s.x.push(42); s.x.push(42); s.x.push(42); + s.a = 1; s.b = 2; + delete s.x; + assert(s.x.length == 0); + uint256[] storage x = s.x; + assembly { sstore(x.slot, 3) } + assert(s.x[0] == 0); + assert(s.x[1] == 0); + assert(s.x[2] == 0); + assert(b == 23); + assert(a == 17); + assert(s.a == 1); + assert(s.b == 2); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> +// g() -> diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol new file mode 100644 index 000000000000..65fb741874c0 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol @@ -0,0 +1,29 @@ +contract C { + struct S { + uint32 a; + uint32[3] b; + uint32[] x; + } + S s; + function f() public returns (uint256 ret) { + assembly { + // 2 ** 150 - 1 + sstore(s.slot, 1427247692705959881058285969449495136382746623) + } + s.a = 1; + s.b[0] = 2; s.b[1] = 3; + s.x.push(4); s.x.push(5); + delete s; + assert(s.a == 0); + assert(s.b[0] == 0); + assert(s.b[1] == 0); + assert(s.x.length == 0); + assembly { + ret := sload(s.slot) + } + } +} +// ==== +// compileViaYul: true +// ---- +// f() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol b/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol index b5c8a2cfa622..85d2c05561f3 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_struct_in_mapping.sol @@ -14,5 +14,7 @@ contract test { } } +// ==== +// compileViaYul: also // ---- // deleteIt() -> 0 diff --git a/test/libsolidity/semanticTests/structs/struct_memory_to_storage.sol b/test/libsolidity/semanticTests/structs/struct_memory_to_storage.sol new file mode 100644 index 000000000000..71acc1ab9b34 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_memory_to_storage.sol @@ -0,0 +1,29 @@ +pragma abicoder v2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + } + + struct X { + uint256 a; + S s; + } + + uint[79] r; + X x; + + function f() external returns (uint32, uint128, uint256) { + X memory m = X(12, S(42, 23, 34)); + x = m; + return (x.s.a, x.s.b, x.s.c); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 42, 23, 34 diff --git a/test/libsolidity/semanticTests/structs/struct_memory_to_storage_function_ptr.sol b/test/libsolidity/semanticTests/structs/struct_memory_to_storage_function_ptr.sol new file mode 100644 index 000000000000..bdabb001ad35 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_memory_to_storage_function_ptr.sol @@ -0,0 +1,33 @@ +pragma abicoder v2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + function() internal returns (uint32) f; + } + + struct X { + uint256 a; + S s; + } + + uint[79] r; + X x; + + function f() external returns (uint32, uint128, uint256, uint32, uint32) { + X memory m = X(12, S(42, 23, 34, g)); + x = m; + return (x.s.a, x.s.b, x.s.c, x.s.f(), m.s.f()); + } + + function g() internal returns (uint32) { + return x.s.a; + } +} + +// ==== +// compileViaYul: false +// ---- +// f() -> 42, 23, 34, 42, 42 diff --git a/test/libsolidity/semanticTests/structs/struct_named_constructor.sol b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol index f74cc90bd17a..d22d46fe57cb 100644 --- a/test/libsolidity/semanticTests/structs/struct_named_constructor.sol +++ b/test/libsolidity/semanticTests/structs/struct_named_constructor.sol @@ -6,9 +6,12 @@ contract C { S public s; constructor() { - s = S({a: 1, x: true}); + s = S({x: true, a: 1}); } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // s() -> 1, true diff --git a/test/libsolidity/semanticTests/structs/struct_reference.sol b/test/libsolidity/semanticTests/structs/struct_reference.sol index 79eeccbe711d..c836d3f1d349 100644 --- a/test/libsolidity/semanticTests/structs/struct_reference.sol +++ b/test/libsolidity/semanticTests/structs/struct_reference.sol @@ -18,6 +18,8 @@ contract test { inner.recursive[0].z = inner.recursive[1].z + 1; } } +// ==== +// compileViaYul: also // ---- // check() -> false // set() -> diff --git a/test/libsolidity/semanticTests/structs/struct_storage_push_zero_value.sol b/test/libsolidity/semanticTests/structs/struct_storage_push_zero_value.sol new file mode 100644 index 000000000000..2ef6f4cf48d5 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_storage_push_zero_value.sol @@ -0,0 +1,28 @@ +contract C { + struct S { + uint256 x; + uint128 y; + uint32 z; + uint128[3] a1; + uint128[] a2; + } + uint8 b = 23; + S[] s; + uint8 a = 17; + function f() public { + s.push(); + assert(s[0].x == 0); + assert(s[0].y == 0); + assert(s[0].z == 0); + assert(s[0].a1[0] == 0); + assert(s[0].a1[1] == 0); + assert(s[0].a1[2] == 0); + assert(s[0].a2.length == 0); + assert(b == 23); + assert(a == 17); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> diff --git a/test/libsolidity/semanticTests/structs/struct_storage_to_memory.sol b/test/libsolidity/semanticTests/structs/struct_storage_to_memory.sol new file mode 100644 index 000000000000..51db51b93120 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_storage_to_memory.sol @@ -0,0 +1,27 @@ +pragma abicoder v2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + } + struct X { + uint32 a; + S s; + } + + uint[79] arr; + X x = X(12, S(42, 23, 34)); + + function f() external returns (uint32, uint128, uint256) { + X memory m = x; + return (m.s.a, m.s.b, m.s.c); + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 42, 23, 34 diff --git a/test/libsolidity/semanticTests/structs/struct_storage_to_memory_function_ptr.sol b/test/libsolidity/semanticTests/structs/struct_storage_to_memory_function_ptr.sol new file mode 100644 index 000000000000..74a32a76e564 --- /dev/null +++ b/test/libsolidity/semanticTests/structs/struct_storage_to_memory_function_ptr.sol @@ -0,0 +1,32 @@ +pragma abicoder v2; + +contract C { + struct S { + uint32 a; + uint128 b; + uint256 c; + function() internal returns (uint32) f; + } + + struct X { + uint256 a; + S s; + } + + uint[79] arr; + X x = X(12, S(42, 23, 34, g)); + + function f() external returns (uint32, uint128, uint256, uint32, uint32) { + X memory m = x; + return (m.s.a, m.s.b, m.s.c, m.s.f(), x.s.f()); + } + + function g() internal returns (uint32) { + return x.s.a; + } +} + +// ==== +// compileViaYul: false +// ---- +// f() -> 42, 23, 34, 42, 42 diff --git a/test/libsolidity/semanticTests/tryCatch/create.sol b/test/libsolidity/semanticTests/tryCatch/create.sol index ad7f734c750a..7fd776f6dc7c 100644 --- a/test/libsolidity/semanticTests/tryCatch/create.sol +++ b/test/libsolidity/semanticTests/tryCatch/create.sol @@ -26,6 +26,7 @@ contract C { } } // ==== +// compileViaYul: also // EVMVersion: >=byzantium // ---- // f() -> 0, 0, 96, 13, "test message." diff --git a/test/libsolidity/semanticTests/tryCatch/try_catch_library_call.sol b/test/libsolidity/semanticTests/tryCatch/try_catch_library_call.sol new file mode 100644 index 000000000000..cf061a8c9125 --- /dev/null +++ b/test/libsolidity/semanticTests/tryCatch/try_catch_library_call.sol @@ -0,0 +1,46 @@ +library L { + struct S { uint x; } + function integer(uint t, bool b) public view returns (uint) { + if (b) { + return t; + } else { + revert("failure"); + } + } + function stru(S storage t, bool b) public view returns (uint) { + if (b) { + return t.x; + } else { + revert("failure"); + } + } +} +contract C { + using L for L.S; + L.S t; + function f(bool b) public returns (uint, string memory) { + uint x = 8; + try L.integer(x, b) returns (uint _x) { + return (_x, ""); + } catch Error(string memory message) { + return (18, message); + } + } + function g(bool b) public returns (uint, string memory) { + t.x = 9; + try t.stru(b) returns (uint x) { + return (x, ""); + } catch Error(string memory message) { + return (19, message); + } + } +} +// ==== +// compileViaYul: also +// EVMVersion: >=byzantium +// ---- +// library: L +// f(bool): true -> 8, 0x40, 0 +// f(bool): false -> 18, 0x40, 7, "failure" +// g(bool): true -> 9, 0x40, 0 +// g(bool): false -> 19, 0x40, 7, "failure" diff --git a/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol b/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol index c7d2d8bf85ab..67981106bb67 100644 --- a/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol +++ b/test/libsolidity/semanticTests/types/assign_calldata_value_type.sol @@ -7,5 +7,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 23 -> 42, 23 diff --git a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_greater_size.sol b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_greater_size.sol index b6e4e37b8b11..871778a466f1 100644 --- a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_greater_size.sol +++ b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_greater_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // bytesToBytes(bytes2): "ab" -> "ab" diff --git a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_same_size.sol b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_same_size.sol index 7e3006ff45c0..0ac977fc39d3 100644 --- a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_same_size.sol +++ b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_same_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // bytesToBytes(bytes4): "abcd" -> "abcd" diff --git a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_smaller_size.sol b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_smaller_size.sol index e48fc0b9e910..513494fcd2bf 100644 --- a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_smaller_size.sol +++ b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_fixed_bytes_smaller_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // bytesToBytes(bytes4): "abcd" -> "ab" diff --git a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_greater_size.sol b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_greater_size.sol index f14ace1048b1..fa91940cab56 100644 --- a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_greater_size.sol +++ b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_greater_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // bytesToUint(bytes4): "abcd" -> 0x61626364 diff --git a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_min_size.sol b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_min_size.sol index 54d583e497e5..0048bda8f4d2 100644 --- a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_min_size.sol +++ b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_min_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // bytesToUint(bytes1): "a" -> 0x61 diff --git a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_type.sol b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_type.sol index 3d7a95cdebf6..77ca06efde56 100644 --- a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_type.sol +++ b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_same_type.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // bytesToUint(bytes32): "abc2" -> left(0x61626332) diff --git a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_smaller_size.sol b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_smaller_size.sol index 93ca45b53e5d..bfe0695a40bd 100644 --- a/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_smaller_size.sol +++ b/test/libsolidity/semanticTests/types/convert_fixed_bytes_to_uint_smaller_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // bytesToUint(bytes4): "abcd" -> 0x6364 diff --git a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_greater_size.sol b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_greater_size.sol index c5f546831f28..7c4d5cec091e 100644 --- a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_greater_size.sol +++ b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_greater_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// UintToBytes(uint16): 0x6162 -> "\0\0\0\0\0\0ab" +// UintToBytes(uint16): 0x6162 -> "\x00\x00\x00\x00\x00\x00ab" diff --git a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_min_size.sol b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_min_size.sol index 95c373803f49..69a1c22dc899 100644 --- a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_min_size.sol +++ b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_min_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // UintToBytes(uint8): 0x61 -> "a" diff --git a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_size.sol b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_size.sol index 3d6d3de48453..1b0b79bffc6c 100644 --- a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_size.sol +++ b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_same_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // uintToBytes(uint256): left(0x616263) -> left(0x616263) diff --git a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_smaller_size.sol b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_smaller_size.sol index 2ab4f4bd4d22..388a9f9859d5 100644 --- a/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_smaller_size.sol +++ b/test/libsolidity/semanticTests/types/convert_uint_to_fixed_bytes_smaller_size.sol @@ -5,5 +5,6 @@ contract Test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// uintToBytes(uint32): 0x61626364 -> "cd" +// uintToBytes(uint32): 0x61626364 -> "cd" diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key.sol b/test/libsolidity/semanticTests/types/mapping_enum_key.sol index befc46a87d40..50dde5f5b783 100644 --- a/test/libsolidity/semanticTests/types/mapping_enum_key.sol +++ b/test/libsolidity/semanticTests/types/mapping_enum_key.sol @@ -9,22 +9,24 @@ contract test { } } // ==== -// compileViaYul: also +// EVMVersion: >=byzantium +// ABIEncoderV1Only: true +// compileViaYul: false // ---- // get(uint8): 0 -> 0 // get(uint8): 0x01 -> 0 // get(uint8): 0x02 -> 0 -// get(uint8): 0x03 -> FAILURE -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0x03 -> FAILURE, hex"4e487b71", 33 +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x01, 0xa1 -> // get(uint8): 0 -> 0 // get(uint8): 0x01 -> 0xa1 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x00, 0xef -> // get(uint8): 0 -> 0xef // get(uint8): 0x01 -> 0xa1 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x01, 0x05 -> // get(uint8): 0 -> 0xef // get(uint8): 0x01 -> 0x05 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol index 9b1b23b83cd9..1796b9cdc3f5 100644 --- a/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter.sol @@ -10,31 +10,32 @@ contract test { } // ==== // ABIEncoderV1Only: true +// EVMVersion: >=byzantium // ---- // table(uint8): 0 -> 0 // table(uint8): 0x01 -> 0 // table(uint8): 0xa7 -> 0 // get(uint8): 0 -> 0 // get(uint8): 0x01 -> 0 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x01, 0xa1 -> // table(uint8): 0 -> 0 // table(uint8): 0x01 -> 0xa1 // table(uint8): 0xa7 -> 0 // get(uint8): 0 -> 0 // get(uint8): 0x01 -> 0xa1 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x00, 0xef -> // table(uint8): 0 -> 0xef // table(uint8): 0x01 -> 0xa1 // table(uint8): 0xa7 -> 0 // get(uint8): 0 -> 0xef // get(uint8): 0x01 -> 0xa1 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x01, 0x05 -> // table(uint8): 0 -> 0xef // table(uint8): 0x01 -> 0x05 // table(uint8): 0xa7 -> 0 // get(uint8): 0 -> 0xef // get(uint8): 0x01 -> 0x05 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol index f822a9395979..834e866a3236 100644 --- a/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_getter_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract test { enum E { A, B, C } mapping(E => uint8) public table; diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol index d1484238ddc5..7b638d122505 100644 --- a/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_library.sol @@ -16,20 +16,23 @@ contract test { L.set(table, k, v); } } +// ==== +// EVMVersion: >=byzantium +// ABIEncoderV1Only: true // ---- // library: L // get(uint8): 0 -> 0 // get(uint8): 0x01 -> 0 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x01, 0xa1 -> // get(uint8): 0 -> 0 // get(uint8): 0x01 -> 0xa1 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x00, 0xef -> // get(uint8): 0 -> 0xef // get(uint8): 0x01 -> 0xa1 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 // set(uint8,uint8): 0x01, 0x05 -> // get(uint8): 0 -> 0xef // get(uint8): 0x01 -> 0x05 -// get(uint8): 0xa7 -> FAILURE +// get(uint8): 0xa7 -> FAILURE, hex"4e487b71", 33 diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_library_encoderV2.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_library_encoderV2.sol new file mode 100644 index 000000000000..bb4fa6ad52ca --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_library_encoderV2.sol @@ -0,0 +1,39 @@ +pragma experimental ABIEncoderV2; + +enum E { A, B, C } +library L { + function get(mapping(E => uint8) storage table, E k) external returns (uint8) { + return table[k]; + } + function set(mapping(E => uint8) storage table, E k, uint8 v) external { + table[k] = v; + } +} +contract test { + mapping(E => uint8) table; + function get(E k) public returns (uint8 v) { + return L.get(table, k); + } + function set(E k, uint8 v) public { + L.set(table, k, v); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// library: L +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/mapping_enum_key_v2.sol b/test/libsolidity/semanticTests/types/mapping_enum_key_v2.sol new file mode 100644 index 000000000000..abf9b7163462 --- /dev/null +++ b/test/libsolidity/semanticTests/types/mapping_enum_key_v2.sol @@ -0,0 +1,32 @@ +pragma experimental ABIEncoderV2; +enum E { A, B, C } +contract test { + mapping(E => uint8) table; + function get(E k) public returns (uint8 v) { + return table[k]; + } + function set(E k, uint8 v) public { + table[k] = v; + } +} +// ==== +// compileViaYul: also +// EVMVersion: >=byzantium +// ---- +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0 +// get(uint8): 0x02 -> 0 +// get(uint8): 0x03 -> FAILURE +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0xa1 -> +// get(uint8): 0 -> 0 +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x00, 0xef -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0xa1 +// get(uint8): 0xa7 -> FAILURE +// set(uint8,uint8): 0x01, 0x05 -> +// get(uint8): 0 -> 0xef +// get(uint8): 0x01 -> 0x05 +// get(uint8): 0xa7 -> FAILURE diff --git a/test/libsolidity/semanticTests/types/nested_tuples.sol b/test/libsolidity/semanticTests/types/nested_tuples.sol index b94d4437071e..a12b96ad1232 100644 --- a/test/libsolidity/semanticTests/types/nested_tuples.sol +++ b/test/libsolidity/semanticTests/types/nested_tuples.sol @@ -28,6 +28,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f0() -> 2, true // f1() -> 1 diff --git a/test/libsolidity/semanticTests/types/packing_signed_types.sol b/test/libsolidity/semanticTests/types/packing_signed_types.sol index 3a10fd336317..7ae54c13d519 100644 --- a/test/libsolidity/semanticTests/types/packing_signed_types.sol +++ b/test/libsolidity/semanticTests/types/packing_signed_types.sol @@ -6,5 +6,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // run() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa diff --git a/test/libsolidity/semanticTests/types/packing_unpacking_types.sol b/test/libsolidity/semanticTests/types/packing_unpacking_types.sol index ea2ed58f2365..7b1412dc8888 100644 --- a/test/libsolidity/semanticTests/types/packing_unpacking_types.sol +++ b/test/libsolidity/semanticTests/types/packing_unpacking_types.sol @@ -7,6 +7,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // run(bool,uint32,uint64): true, 0x0f0f0f0f, 0xf0f0f0f0f0f0f0f0 // -> 0x0000000000000000000000000000000000000001f0f0f0f00f0f0f0f0f0f0f0f diff --git a/test/libsolidity/semanticTests/types/strings.sol b/test/libsolidity/semanticTests/types/strings.sol index 0cc4565afae1..745c3c3f299b 100644 --- a/test/libsolidity/semanticTests/types/strings.sol +++ b/test/libsolidity/semanticTests/types/strings.sol @@ -13,7 +13,8 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// fixedBytesHex() -> "\xaa\xbb\0\xff" -// fixedBytes() -> "abc\0\xff__" -// pipeThrough(bytes2, bool): "\0\x02", true -> "\0\x2", true +// fixedBytesHex() -> "\xaa\xbb\x00\xff" +// fixedBytes() -> "abc\x00\xff__" +// pipeThrough(bytes2,bool): "\x00\x02", true -> "\x00\x02", true diff --git a/test/libsolidity/semanticTests/types/tuple_assign_multi_slot_grow.sol b/test/libsolidity/semanticTests/types/tuple_assign_multi_slot_grow.sol index 6735563b9851..5cfb53ed9b30 100644 --- a/test/libsolidity/semanticTests/types/tuple_assign_multi_slot_grow.sol +++ b/test/libsolidity/semanticTests/types/tuple_assign_multi_slot_grow.sol @@ -9,5 +9,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x30, 0x31, 0x32 diff --git a/test/libsolidity/semanticTests/types/type_conversion_cleanup.sol b/test/libsolidity/semanticTests/types/type_conversion_cleanup.sol index 7a12c689c904..f129c6293201 100644 --- a/test/libsolidity/semanticTests/types/type_conversion_cleanup.sol +++ b/test/libsolidity/semanticTests/types/type_conversion_cleanup.sol @@ -1,7 +1,8 @@ contract Test { - function test() public returns (uint ret) { return uint(address(Test(address(0x11223344556677889900112233445566778899001122)))); } + function test() public returns (uint ret) { return uint(uint160(address(uint160(uint128(type(uint200).max))))); } } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// test() -> 0x0000000000000000000000003344556677889900112233445566778899001122 +// test() -> 0xffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/underscore/as_function.sol b/test/libsolidity/semanticTests/underscore/as_function.sol new file mode 100644 index 000000000000..908947ec0535 --- /dev/null +++ b/test/libsolidity/semanticTests/underscore/as_function.sol @@ -0,0 +1,21 @@ +contract C { + function _() public pure returns (uint) { + return 88; + } + + function g() public pure returns (uint){ + return _(); + } + + function h() public pure returns (uint) { + _; + return 33; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// _() -> 88 +// g() -> 88 +// h() -> 33 diff --git a/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidInConstructor.sol b/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidInConstructor.sol index 15ebeca438ce..9fa78f5300c0 100644 --- a/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidInConstructor.sol +++ b/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidInConstructor.sol @@ -19,5 +19,7 @@ contract Test { new C(); } } +// ==== +// compileViaYul: also // ---- -// f() -> FAILURE +// f() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidStoredInConstructor.sol b/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidStoredInConstructor.sol index 323e47f8bd52..2477ad96346c 100644 --- a/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidStoredInConstructor.sol +++ b/test/libsolidity/semanticTests/uninitializedFunctionPointer/invalidStoredInConstructor.sol @@ -19,5 +19,7 @@ contract Test { new C(); } } +// ==== +// compileViaYul: also // ---- -// f() -> FAILURE +// f() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/uninitializedFunctionPointer/store2.sol b/test/libsolidity/semanticTests/uninitializedFunctionPointer/store2.sol index a9ffff0b6b79..424bb39a13fd 100644 --- a/test/libsolidity/semanticTests/uninitializedFunctionPointer/store2.sol +++ b/test/libsolidity/semanticTests/uninitializedFunctionPointer/store2.sol @@ -1,4 +1,4 @@ -pragma solidity >=0.4.0 <0.8.0; +pragma solidity >=0.4.0 <0.9.0; contract InvalidTest { @@ -35,5 +35,8 @@ contract InvalidTest { x++; } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- -// run() -> FAILURE +// run() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/uninitializedFunctionPointer/storeInConstructor.sol b/test/libsolidity/semanticTests/uninitializedFunctionPointer/storeInConstructor.sol index df28ca1cca1a..398f2230f247 100644 --- a/test/libsolidity/semanticTests/uninitializedFunctionPointer/storeInConstructor.sol +++ b/test/libsolidity/semanticTests/uninitializedFunctionPointer/storeInConstructor.sol @@ -14,6 +14,9 @@ contract InvalidTest { storedFn(); } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- -// f() -> FAILURE -// f() -> FAILURE +// f() -> FAILURE, hex"4e487b71", 0x51 +// f() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/variables/delete_local.sol b/test/libsolidity/semanticTests/variables/delete_local.sol index e29d6a9420fb..dd52f1b5b604 100644 --- a/test/libsolidity/semanticTests/variables/delete_local.sol +++ b/test/libsolidity/semanticTests/variables/delete_local.sol @@ -7,5 +7,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // delLocal() -> 0 diff --git a/test/libsolidity/semanticTests/variables/delete_locals.sol b/test/libsolidity/semanticTests/variables/delete_locals.sol index 8486f4999b16..180f3ef11f2d 100644 --- a/test/libsolidity/semanticTests/variables/delete_locals.sol +++ b/test/libsolidity/semanticTests/variables/delete_locals.sol @@ -10,5 +10,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // delLocal() -> 6, 7 diff --git a/test/libsolidity/semanticTests/variables/mapping_local_tuple_assignment.sol b/test/libsolidity/semanticTests/variables/mapping_local_tuple_assignment.sol index 39dd04abbf27..eb5e6b854027 100644 --- a/test/libsolidity/semanticTests/variables/mapping_local_tuple_assignment.sol +++ b/test/libsolidity/semanticTests/variables/mapping_local_tuple_assignment.sol @@ -12,5 +12,7 @@ contract test { return (m1[1], m1[2], m2[1], m2[2]); } } +// ==== +// compileViaYul: also // ---- // f() -> 42, 0, 0, 21 diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding.sol b/test/libsolidity/semanticTests/variables/public_state_overridding.sol index bff486eaaa91..6671010de675 100644 --- a/test/libsolidity/semanticTests/variables/public_state_overridding.sol +++ b/test/libsolidity/semanticTests/variables/public_state_overridding.sol @@ -13,6 +13,7 @@ contract X is A } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // test() -> 0 // set() -> diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol b/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol index f6fa4d45e77a..366b5fb09215 100644 --- a/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol +++ b/test/libsolidity/semanticTests/variables/public_state_overridding_dynamic_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; struct S { uint256 v; string s; } diff --git a/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol b/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol index 404013ee6c6a..5a3df6d21498 100644 --- a/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol +++ b/test/libsolidity/semanticTests/variables/public_state_overridding_mapping_to_dynamic_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; struct S { uint256 v; string s; } diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol new file mode 100644 index 000000000000..ff40d2c0f336 --- /dev/null +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -0,0 +1,20 @@ +contract C { + bytes public initCode; + + constructor() { + // This should catch problems, but lets also test the case the optimiser is buggy. + assert(address(this).code.length == 0); + initCode = address(this).code; + } + + // To avoid dependency on exact length. + function f() public view returns (bool) { return address(this).code.length > 400; } + function g() public view returns (uint) { return address(0).code.length; } + function h() public view returns (uint) { return address(1).code.length; } +} +// ---- +// constructor() -> +// initCode() -> 0x20, 0 +// f() -> true +// g() -> 0 +// h() -> 0 diff --git a/test/libsolidity/semanticTests/various/address_code_complex.sol b/test/libsolidity/semanticTests/various/address_code_complex.sol new file mode 100644 index 000000000000..0d3bf862a5fc --- /dev/null +++ b/test/libsolidity/semanticTests/various/address_code_complex.sol @@ -0,0 +1,19 @@ +contract A { + constructor() { + assembly { + // This is only 7 bytes here. + mstore(0, 0x48aa5566000000) + return(0, 32) + } + } +} + +contract C { + function f() public returns (bytes memory) { return address(new A()).code; } + function g() public returns (uint) { return address(new A()).code.length; } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0x20, 0x20, 0x48aa5566000000 +// g() -> 0x20 diff --git a/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol index 4f849be89fbc..bc7db4493dfa 100644 --- a/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol +++ b/test/libsolidity/semanticTests/various/assignment_to_const_var_involving_expression.sol @@ -8,5 +8,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x57a diff --git a/test/libsolidity/semanticTests/various/byte_optimization_bug.sol b/test/libsolidity/semanticTests/various/byte_optimization_bug.sol index 3d94e4333fe8..5fa1f5bc8641 100644 --- a/test/libsolidity/semanticTests/various/byte_optimization_bug.sol +++ b/test/libsolidity/semanticTests/various/byte_optimization_bug.sol @@ -14,6 +14,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 2 -> 0 // g(uint256): 2 -> 2 diff --git a/test/libsolidity/semanticTests/various/codebalance_assembly.sol b/test/libsolidity/semanticTests/various/codebalance_assembly.sol new file mode 100644 index 000000000000..2df9c5b4f29d --- /dev/null +++ b/test/libsolidity/semanticTests/various/codebalance_assembly.sol @@ -0,0 +1,28 @@ +contract C { + constructor() payable {} + + function f() public returns (uint256 ret) { + assembly { + ret := balance(0) + } + } + function g() public returns (uint256 ret) { + assembly { + ret := balance(1) + } + } + function h() public returns (uint256 ret) { + assembly { + ret := balance(address()) + } + } +} + +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// constructor(), 23 wei -> +// f() -> 0 +// g() -> 1 +// h() -> 23 diff --git a/test/libsolidity/semanticTests/various/codehash.sol b/test/libsolidity/semanticTests/various/codehash.sol new file mode 100644 index 000000000000..c8730cca6310 --- /dev/null +++ b/test/libsolidity/semanticTests/various/codehash.sol @@ -0,0 +1,21 @@ +contract C { + function f() public returns (bytes32) { + // non-existent in tests + return address(0).codehash; + } + function g() public returns (bytes32) { + // precompile + return address(0x1).codehash; + } + function h() public returns (bool) { + return address(this).codehash != 0; + } +} + +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// f() -> 0x0 +// g() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 +// h() -> true diff --git a/test/libsolidity/semanticTests/various/codehash_assembly.sol b/test/libsolidity/semanticTests/various/codehash_assembly.sol new file mode 100644 index 000000000000..4ac2a93f199d --- /dev/null +++ b/test/libsolidity/semanticTests/various/codehash_assembly.sol @@ -0,0 +1,25 @@ +contract C { + function f() public returns (bytes32 ret) { + assembly { + ret := extcodehash(0) + } + } + function g() public returns (bytes32 ret) { + assembly { + ret := extcodehash(1) + } + } + function h() public returns (bool ret) { + assembly { + ret := iszero(iszero(extcodehash(address()))) + } + } +} + +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// f() -> 0 +// g() -> 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 +// h() -> true diff --git a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol index aa3734b3aad7..8f58c115a4ef 100644 --- a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol +++ b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol @@ -18,5 +18,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // constructor() -> diff --git a/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol b/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol index fdd7aa32ebbf..1d03723f2daa 100644 --- a/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol +++ b/test/libsolidity/semanticTests/various/crazy_elementary_typenames_on_stack.sol @@ -11,5 +11,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> -7 diff --git a/test/libsolidity/semanticTests/various/cross_contract_types.sol b/test/libsolidity/semanticTests/various/cross_contract_types.sol index 88528ce07633..c100ab7b7305 100644 --- a/test/libsolidity/semanticTests/various/cross_contract_types.sol +++ b/test/libsolidity/semanticTests/various/cross_contract_types.sol @@ -15,5 +15,6 @@ contract Test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3 diff --git a/test/libsolidity/semanticTests/various/decayed_tuple.sol b/test/libsolidity/semanticTests/various/decayed_tuple.sol index 2f1122f6a64d..df845eff4dc2 100644 --- a/test/libsolidity/semanticTests/various/decayed_tuple.sol +++ b/test/libsolidity/semanticTests/various/decayed_tuple.sol @@ -7,5 +7,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/various/destructuring_assignment.sol b/test/libsolidity/semanticTests/various/destructuring_assignment.sol index 0c8dc3835ff1..345d76ca1d84 100644 --- a/test/libsolidity/semanticTests/various/destructuring_assignment.sol +++ b/test/libsolidity/semanticTests/various/destructuring_assignment.sol @@ -32,5 +32,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f(bytes): 0x20, 0x5, "abcde" -> 0 diff --git a/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol b/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol index d1ea9ab34f0b..9972f7bd2069 100644 --- a/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol +++ b/test/libsolidity/semanticTests/various/empty_name_return_parameter.sol @@ -6,5 +6,6 @@ contract test { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 9 -> 9 diff --git a/test/libsolidity/semanticTests/various/external_types_in_calls.sol b/test/libsolidity/semanticTests/various/external_types_in_calls.sol index 1b3ab196a204..7b3d5def1900 100644 --- a/test/libsolidity/semanticTests/various/external_types_in_calls.sol +++ b/test/libsolidity/semanticTests/various/external_types_in_calls.sol @@ -9,9 +9,9 @@ contract C1 { contract C { function test() public returns (C1 x, C1 y) { - C1 c = new C1(C1(9)); + C1 c = new C1(C1(address(9))); x = c.bla(); - y = this.t1(C1(7)); + y = this.t1(C1(address(7))); } function t1(C1 a) public returns (C1) { @@ -19,7 +19,7 @@ contract C { } function t2() public returns (C1) { - return C1(9); + return C1(address(9)); } } diff --git a/test/libsolidity/semanticTests/various/flipping_sign_tests.sol b/test/libsolidity/semanticTests/various/flipping_sign_tests.sol index 8309615a5c22..922494be778e 100644 --- a/test/libsolidity/semanticTests/various/flipping_sign_tests.sol +++ b/test/libsolidity/semanticTests/various/flipping_sign_tests.sol @@ -1,7 +1,7 @@ contract test { function f() public returns (bool) { int256 x = -2**255; - assert(-x == x); + unchecked { assert(-x == x); } return true; } } diff --git a/test/libsolidity/semanticTests/various/gasleft_decrease.sol b/test/libsolidity/semanticTests/various/gasleft_decrease.sol index b6251205e2d2..9730aac11a86 100644 --- a/test/libsolidity/semanticTests/various/gasleft_decrease.sol +++ b/test/libsolidity/semanticTests/various/gasleft_decrease.sol @@ -17,6 +17,7 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true // g() -> true diff --git a/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol b/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol index 00c0eabed3ba..e32954d34776 100644 --- a/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol +++ b/test/libsolidity/semanticTests/various/gasleft_shadow_resolution.sol @@ -10,5 +10,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/various/inline_member_init.sol b/test/libsolidity/semanticTests/various/inline_member_init.sol index 15ad78fe14ab..66fad2b14de3 100644 --- a/test/libsolidity/semanticTests/various/inline_member_init.sol +++ b/test/libsolidity/semanticTests/various/inline_member_init.sol @@ -16,5 +16,6 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // get() -> 5, 6, 8 diff --git a/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol index 00ea47451f6e..1f345d6b9899 100644 --- a/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol +++ b/test/libsolidity/semanticTests/various/inline_member_init_inheritence.sol @@ -20,6 +20,7 @@ contract Derived is Base { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getBMember() -> 5 // getDMember() -> 6 diff --git a/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol b/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol index 477d14740c30..ec42dfde2dd3 100644 --- a/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol +++ b/test/libsolidity/semanticTests/various/inline_tuple_with_rational_numbers.sol @@ -7,5 +7,6 @@ contract c { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 diff --git a/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol b/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol index 743825d607f4..e957fbc8eb48 100644 --- a/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol +++ b/test/libsolidity/semanticTests/various/iszero_bnot_correct.sol @@ -15,5 +15,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/various/memory_overwrite.sol b/test/libsolidity/semanticTests/various/memory_overwrite.sol index 3ab0a2aa2e63..e11fb40fb007 100644 --- a/test/libsolidity/semanticTests/various/memory_overwrite.sol +++ b/test/libsolidity/semanticTests/various/memory_overwrite.sol @@ -8,5 +8,6 @@ contract C { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x20, 5, "b23a5" diff --git a/test/libsolidity/semanticTests/various/multi_variable_declaration.sol b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol index 75f3fa724efb..c9f492a17bbb 100644 --- a/test/libsolidity/semanticTests/various/multi_variable_declaration.sol +++ b/test/libsolidity/semanticTests/various/multi_variable_declaration.sol @@ -44,5 +44,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct.sol index f02fda353d50..6a05524f55ed 100644 --- a/test/libsolidity/semanticTests/various/nested_calldata_struct.sol +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -22,5 +22,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol index e0a25a857a8c..3e96f2a6f588 100644 --- a/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol +++ b/test/libsolidity/semanticTests/various/nested_calldata_struct_to_memory.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -23,5 +23,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f((uint256,uint256,(uint256,uint256),uint256)): 1, 2, 3, 4, 5 -> 1, 2, 3, 4, 5 diff --git a/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol index bc5e546caca0..954d43fcff3a 100644 --- a/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol +++ b/test/libsolidity/semanticTests/various/positive_integers_to_signed.sol @@ -5,6 +5,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // x() -> 2 // y() -> 127 diff --git a/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol b/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol index 0864121de77b..43ff8a376c97 100644 --- a/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol +++ b/test/libsolidity/semanticTests/various/single_copy_with_multiple_inheritance.sol @@ -29,6 +29,7 @@ contract Derived is Base, B, A {} // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getViaB() -> 0 // setViaA(uint256): 23 -> diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_static_arrays_with_dynamic_elements.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_static_arrays_with_dynamic_elements.sol new file mode 100644 index 000000000000..65e0b4769804 --- /dev/null +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_static_arrays_with_dynamic_elements.sol @@ -0,0 +1,25 @@ +pragma abicoder v2; + +contract C { + struct S { + bool[] b; + } + + function f() public returns (uint256, bool[][2] memory, S[2] memory, uint256) { + return ( + 5, + [new bool[](1), new bool[](2)], + [S(new bool[](2)), S(new bool[](5))], + 6 + ); + } + + function g() public returns (uint256, uint256) { + (uint256 a, , , uint256 b) = this.f(); + return (a, b); + } +} +// ==== +// compileViaYul: also +// ---- +// g() -> 5, 6 diff --git a/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol b/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol index 6f712839c86a..f18cebcde368 100644 --- a/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol +++ b/test/libsolidity/semanticTests/various/state_variable_local_variable_mixture.sol @@ -8,5 +8,6 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // a() -> 2 diff --git a/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol b/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol index 2a2c9b975c9d..8fa2ef5bba35 100644 --- a/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol +++ b/test/libsolidity/semanticTests/various/state_variable_under_contract_name.sol @@ -7,5 +7,6 @@ contract Scope { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // getStateVar() -> 42 diff --git a/test/libsolidity/semanticTests/various/store_bytes.sol b/test/libsolidity/semanticTests/various/store_bytes.sol index 99eb1acff958..34c0b6f3d5d2 100644 --- a/test/libsolidity/semanticTests/various/store_bytes.sol +++ b/test/libsolidity/semanticTests/various/store_bytes.sol @@ -8,7 +8,8 @@ contract C { bytes savedData; } - +// ==== +// compileViaYul: also // ---- // save() -> 24 # empty copy loop # // save(): "abcdefg" -> 24 diff --git a/test/libsolidity/semanticTests/various/string_tuples.sol b/test/libsolidity/semanticTests/various/string_tuples.sol index 0a7a38b8b14c..36caa56b2125 100644 --- a/test/libsolidity/semanticTests/various/string_tuples.sol +++ b/test/libsolidity/semanticTests/various/string_tuples.sol @@ -13,6 +13,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x40, 0x8, 0x3, "abc" // g() -> 0x40, 0x80, 0x3, "abc", 0x3, "def" diff --git a/test/libsolidity/semanticTests/various/super.sol b/test/libsolidity/semanticTests/various/super.sol index 16e4257e157c..1b2efd00c597 100644 --- a/test/libsolidity/semanticTests/various/super.sol +++ b/test/libsolidity/semanticTests/various/super.sol @@ -25,5 +25,8 @@ contract D is B, C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 15 diff --git a/test/libsolidity/semanticTests/various/super_alone.sol b/test/libsolidity/semanticTests/various/super_alone.sol index 623f25330882..a8536d8f19a5 100644 --- a/test/libsolidity/semanticTests/various/super_alone.sol +++ b/test/libsolidity/semanticTests/various/super_alone.sol @@ -6,5 +6,6 @@ contract A { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> diff --git a/test/libsolidity/semanticTests/various/super_parentheses.sol b/test/libsolidity/semanticTests/various/super_parentheses.sol new file mode 100644 index 000000000000..5c45fa3961a1 --- /dev/null +++ b/test/libsolidity/semanticTests/various/super_parentheses.sol @@ -0,0 +1,32 @@ +contract A { + function f() public virtual returns (uint256 r) { + return 1; + } +} + + +contract B is A { + function f() public virtual override returns (uint256 r) { + return ((super).f)() | 2; + } +} + + +contract C is A { + function f() public virtual override returns (uint256 r) { + return ((super).f)() | 4; + } +} + + +contract D is B, C { + function f() public override(B, C) returns (uint256 r) { + return ((super).f)() | 8; + } +} + +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f() -> 15 diff --git a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol index 59907b476b7a..ff6648817020 100644 --- a/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol +++ b/test/libsolidity/semanticTests/various/swap_in_storage_overwrite.sol @@ -23,6 +23,9 @@ contract c { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // x() -> 0, 0 // y() -> 0, 0 diff --git a/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol index fae85cd34e8e..c36ca1a868c9 100644 --- a/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol +++ b/test/libsolidity/semanticTests/various/test_underscore_in_hex.sol @@ -7,7 +7,7 @@ contract test { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> 0x1234ab // f(bool): false -> 0x1234abcd1234 - diff --git a/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol b/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol index 1ab8586fcc25..51ec7086399e 100644 --- a/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol +++ b/test/libsolidity/semanticTests/various/typed_multi_variable_declaration.sol @@ -22,5 +22,8 @@ contract C { } } +// ==== +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol index aa1349cb82a0..45e567e1df57 100644 --- a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol @@ -20,5 +20,5 @@ contract C { // ---- // set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 0, 0, 32, "01234567890123456789012345678901" -> 0 // set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 3, 32, "01234567890123456789012345678901" -> 0 -// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 3, 3, 32, "01234567890123456789012345678901" -> FAILURE -// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 5, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 3, 3, 32, "01234567890123456789012345678901" -> FAILURE, hex"4e487b71", 0x32 +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 5, 32, "01234567890123456789012345678901" -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol index 1f490ba59aa1..8678e1aa3a69 100644 --- a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol @@ -18,5 +18,5 @@ contract C { // ---- // set(string,uint256,uint256): 0x60, 2, 0, 32, "01234567890123456789012345678901" -> 0 // set(string,uint256,uint256): 0x60, 2, 1, 32, "01234567890123456789012345678901" -> 0 -// set(string,uint256,uint256): 0x60, 2, 2, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256): 0x60, 2, 2, 32, "01234567890123456789012345678901" -> FAILURE, hex"4e487b71", 0x32 // set(string,uint256,uint256): 0x60, 200, 199, 32, "01234567890123456789012345678901" -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol index 1f9e70db7bd4..76a412adf4a8 100644 --- a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol @@ -19,4 +19,4 @@ contract C { // set(string,uint256,uint256): 0x60, 5, 0, 32, "01234567890123456789012345678901" -> 0 // set(string,uint256,uint256): 0x60, 5, 1, 32, "01234567890123456789012345678901" -> 0 // set(string,uint256,uint256): 0x60, 5, 4, 32, "01234567890123456789012345678901" -> 0 -// set(string,uint256,uint256): 0x60, 5, 5, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256): 0x60, 5, 5, 32, "01234567890123456789012345678901" -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_as_parameter.sol b/test/libsolidity/semanticTests/viaYul/array_memory_as_parameter.sol index eb1a60d4f8cc..55e10487e83e 100644 --- a/test/libsolidity/semanticTests/viaYul/array_memory_as_parameter.sol +++ b/test/libsolidity/semanticTests/viaYul/array_memory_as_parameter.sol @@ -18,9 +18,9 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- -// test(uint256,uint256): 0,0 -> FAILURE -// test(uint256,uint256): 1,0 -> 1 -// test(uint256,uint256): 10,5 -> 6 -// test(uint256,uint256): 10,50 -> FAILURE +// test(uint256,uint256): 0, 0 -> FAILURE, hex"4e487b71", 0x32 +// test(uint256,uint256): 1, 0 -> 1 +// test(uint256,uint256): 10, 5 -> 6 +// test(uint256,uint256): 10, 50 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_create.sol b/test/libsolidity/semanticTests/viaYul/array_memory_create.sol index 9b2a33442651..d1a9c5e2e628 100644 --- a/test/libsolidity/semanticTests/viaYul/array_memory_create.sol +++ b/test/libsolidity/semanticTests/viaYul/array_memory_create.sol @@ -6,7 +6,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // create(uint256): 0 -> 0 // create(uint256): 7 -> 7 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_index_access.sol index 0ec85912cea0..edea7fc3e504 100644 --- a/test/libsolidity/semanticTests/viaYul/array_memory_index_access.sol +++ b/test/libsolidity/semanticTests/viaYul/array_memory_index_access.sol @@ -22,14 +22,14 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // index(uint256): 0 -> true // index(uint256): 10 -> true // index(uint256): 20 -> true // index(uint256): 0xFF -> true -// accessIndex(uint256,int256): 10,1 -> 2 -// accessIndex(uint256,int256): 10,0 -> 1 -// accessIndex(uint256,int256): 10,11 -> FAILURE -// accessIndex(uint256,int256): 10,10 -> FAILURE -// accessIndex(uint256,int256): 10,-1 -> FAILURE +// accessIndex(uint256,int256): 10, 1 -> 2 +// accessIndex(uint256,int256): 10, 0 -> 1 +// accessIndex(uint256,int256): 10, 11 -> FAILURE, hex"4e487b71", 0x32 +// accessIndex(uint256,int256): 10, 10 -> FAILURE, hex"4e487b71", 0x32 +// accessIndex(uint256,int256): 10, -1 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/array_push_return_reference.sol b/test/libsolidity/semanticTests/viaYul/array_push_return_reference.sol index ea52cd1db491..d83fd53ef643 100644 --- a/test/libsolidity/semanticTests/viaYul/array_push_return_reference.sol +++ b/test/libsolidity/semanticTests/viaYul/array_push_return_reference.sol @@ -17,9 +17,9 @@ contract C { // test(uint256): 42 -> // getLength() -> 1 // fetch(uint256): 0 -> 42 -// fetch(uint256): 1 -> FAILURE +// fetch(uint256): 1 -> FAILURE, hex"4e487b71", 0x32 // test(uint256): 23 -> // getLength() -> 2 // fetch(uint256): 0 -> 42 // fetch(uint256): 1 -> 23 -// fetch(uint256): 2 -> FAILURE \ No newline at end of file +// fetch(uint256): 2 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/array_push_with_arg.sol b/test/libsolidity/semanticTests/viaYul/array_push_with_arg.sol index e24aa3d3db68..a93778b3f982 100644 --- a/test/libsolidity/semanticTests/viaYul/array_push_with_arg.sol +++ b/test/libsolidity/semanticTests/viaYul/array_push_with_arg.sol @@ -17,9 +17,9 @@ contract C { // test(uint256): 42 -> // getLength() -> 1 // fetch(uint256): 0 -> 42 -// fetch(uint256): 1 -> FAILURE +// fetch(uint256): 1 -> FAILURE, hex"4e487b71", 0x32 // test(uint256): 23 -> // getLength() -> 2 // fetch(uint256): 0 -> 42 // fetch(uint256): 1 -> 23 -// fetch(uint256): 2 -> FAILURE \ No newline at end of file +// fetch(uint256): 2 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol index d286fc36c98f..efc67e517879 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol @@ -14,7 +14,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // test_indices(uint256): 1 -> // test_indices(uint256): 129 -> diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_index_boundary_test.sol b/test/libsolidity/semanticTests/viaYul/array_storage_index_boundary_test.sol index 5fe4a381db9d..db007858c36f 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_index_boundary_test.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_index_boundary_test.sol @@ -10,14 +10,14 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- -// test_boundary_check(uint256, uint256): 10, 11 -> FAILURE -// test_boundary_check(uint256, uint256): 10, 9 -> 0 -// test_boundary_check(uint256, uint256): 1, 9 -> FAILURE -// test_boundary_check(uint256, uint256): 1, 1 -> FAILURE -// test_boundary_check(uint256, uint256): 10, 10 -> FAILURE -// test_boundary_check(uint256, uint256): 256, 256 -> FAILURE -// test_boundary_check(uint256, uint256): 256, 255 -> 0 -// test_boundary_check(uint256, uint256): 256, 0xFFFF -> FAILURE -// test_boundary_check(uint256, uint256): 256, 2 -> 0 +// test_boundary_check(uint256,uint256): 10, 11 -> FAILURE, hex"4e487b71", 0x32 +// test_boundary_check(uint256,uint256): 10, 9 -> 0 +// test_boundary_check(uint256,uint256): 1, 9 -> FAILURE, hex"4e487b71", 0x32 +// test_boundary_check(uint256,uint256): 1, 1 -> FAILURE, hex"4e487b71", 0x32 +// test_boundary_check(uint256,uint256): 10, 10 -> FAILURE, hex"4e487b71", 0x32 +// test_boundary_check(uint256,uint256): 256, 256 -> FAILURE, hex"4e487b71", 0x32 +// test_boundary_check(uint256,uint256): 256, 255 -> 0 +// test_boundary_check(uint256,uint256): 256, 0xFFFF -> FAILURE, hex"4e487b71", 0x32 +// test_boundary_check(uint256,uint256): 256, 2 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol b/test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol index c0ae21681a7d..e02dc027d04a 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol @@ -50,7 +50,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // test_zeroed_indicies(uint256): 1 -> // test_zeroed_indicies(uint256): 5 -> diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_length_access.sol b/test/libsolidity/semanticTests/viaYul/array_storage_length_access.sol index 266c5ae3b320..95ca92782266 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_length_access.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_length_access.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // set_get_length(uint256): 0 -> 0 // set_get_length(uint256): 1 -> 1 diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_pop_zero_length.sol b/test/libsolidity/semanticTests/viaYul/array_storage_pop_zero_length.sol index 932a4d642208..2a09ba31edd8 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_pop_zero_length.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_pop_zero_length.sol @@ -6,6 +6,6 @@ contract C { } // ==== // EVMVersion: >=petersburg -// compileViaYul: true +// compileViaYul: also // ---- -// popEmpty() -> FAILURE +// popEmpty() -> FAILURE, hex"4e487b71", 0x31 diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_push_empty.sol b/test/libsolidity/semanticTests/viaYul/array_storage_push_empty.sol index e692b273a290..432ab243b987 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_push_empty.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_push_empty.sol @@ -9,7 +9,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // EVMVersion: >=petersburg // ---- // pushEmpty(uint256): 128 diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_push_empty_length_address.sol b/test/libsolidity/semanticTests/viaYul/array_storage_push_empty_length_address.sol index 7f9bd82dd0cc..e28106443528 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_push_empty_length_address.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_push_empty_length_address.sol @@ -10,7 +10,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // EVMVersion: >=petersburg // ---- // set_get_length(uint256): 0 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_push_pop.sol b/test/libsolidity/semanticTests/viaYul/array_storage_push_pop.sol index 3dce6216ffab..e40223908151 100644 --- a/test/libsolidity/semanticTests/viaYul/array_storage_push_pop.sol +++ b/test/libsolidity/semanticTests/viaYul/array_storage_push_pop.sol @@ -9,7 +9,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // set_get_length(uint256): 0 -> 0 // set_get_length(uint256): 1 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/assert.sol b/test/libsolidity/semanticTests/viaYul/assert.sol index 6b1be606e3fa..5b19d3a0096a 100644 --- a/test/libsolidity/semanticTests/viaYul/assert.sol +++ b/test/libsolidity/semanticTests/viaYul/assert.sol @@ -14,9 +14,10 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> true -// f(bool): false -> FAILURE -// fail() -> FAILURE +// f(bool): false -> FAILURE, hex"4e487b71", 0x01 +// fail() -> FAILURE, hex"4e487b71", 0x01 // succeed() -> true diff --git a/test/libsolidity/semanticTests/viaYul/assert_and_require.sol b/test/libsolidity/semanticTests/viaYul/assert_and_require.sol index 4140653e806d..8c435291b111 100644 --- a/test/libsolidity/semanticTests/viaYul/assert_and_require.sol +++ b/test/libsolidity/semanticTests/viaYul/assert_and_require.sol @@ -11,9 +11,10 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> true -// f(bool): false -> FAILURE +// f(bool): false -> FAILURE, hex"4e487b71", 0x01 // f2(bool): true -> true // f2(bool): false -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol b/test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol index 103c1bf1b3b6..26c5a42ae882 100644 --- a/test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol +++ b/test/libsolidity/semanticTests/viaYul/assign_tuple_from_function_call.sol @@ -11,6 +11,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 3, 2, 1 // h() -> 3 diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_access.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_access.sol index 115015f03446..f8ad6b9ec2f2 100644 --- a/test/libsolidity/semanticTests/viaYul/calldata_array_access.sol +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_access.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[] calldata x, uint256 i) external returns (uint256) { return x[i]; @@ -10,11 +10,11 @@ contract C { // ==== // compileViaYul: also // ---- -// f(uint256[],uint256): 0x40, 0, 0 -> FAILURE +// f(uint256[],uint256): 0x40, 0, 0 -> FAILURE, hex"4e487b71", 0x32 // f(uint256[],uint256): 0x40, 0, 1, 23 -> 23 -// f(uint256[],uint256): 0x40, 1, 1, 23 -> FAILURE +// f(uint256[],uint256): 0x40, 1, 1, 23 -> FAILURE, hex"4e487b71", 0x32 // f(uint256[],uint256): 0x40, 0, 2, 23, 42 -> 23 // f(uint256[],uint256): 0x40, 1, 2, 23, 42 -> 42 -// f(uint256[],uint256): 0x40, 2, 2, 23, 42 -> FAILURE +// f(uint256[],uint256): 0x40, 2, 2, 23, 42 -> FAILURE, hex"4e487b71", 0x32 // f(uint256[][],uint256,uint256): 0x60, 0, 0 -> FAILURE // f(uint256[][],uint256,uint256): 0x60, 0, 0, 1, 0x20, 1, 23 -> 23 diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol index d0e4ee59507e..14c9ef209f95 100644 --- a/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_index_range_access.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[] calldata x, uint256 s, uint256 e) external returns (uint256) { return uint256[](x[s:e]).length; @@ -27,6 +27,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256[],uint256,uint256): 0x60, 2, 4, 5, 1, 2, 3, 4, 5 -> 2 // f(uint256[],uint256,uint256): 0x60, 2, 6, 5, 1, 2, 3, 4, 5 -> FAILURE @@ -40,6 +41,6 @@ contract C { // f_e_only(uint256[],uint256): 0x40, 3, 5, 1, 2, 3, 4, 5 -> 3 // f_e_only(uint256[],uint256): 0x40, 6, 5, 1, 2, 3, 4, 5 -> FAILURE // g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4 -// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE +// g(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE, hex"4e487b71", 0x32 // gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 1, 5, 1, 2, 3, 4, 5 -> 4 -// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE +// gg(uint256[],uint256,uint256,uint256): 0x80, 2, 4, 3, 5, 1, 2, 3, 4, 5 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_length.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_length.sol index 507380667e99..c7c38925ec89 100644 --- a/test/libsolidity/semanticTests/viaYul/calldata_array_length.sol +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_length.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[] calldata x) external returns (uint256) { return x.length; diff --git a/test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol b/test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol index 6742b995a5e7..337325967806 100644 --- a/test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol +++ b/test/libsolidity/semanticTests/viaYul/calldata_array_three_dimensional.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][2][] calldata x, uint256 i, uint256 j, uint256 k) external returns (uint256 a, uint256 b, uint256 c, uint256 d) { @@ -10,11 +10,12 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 0, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> 1, 2, 1, 42 // f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 1, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> 1, 2, 1, 23 // f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 1, 0, 1, 0x20, 0x40, 0x80, 1, 42, 2, 23, 17 -> 1, 2, 2, 23 // f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 1, 1, 1, 0x20, 0x40, 0x80, 1, 42, 2, 23, 17 -> 1, 2, 2, 17 -// f(uint256[][2][],uint256,uint256,uint256): 0x80, 1, 0, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE -// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 2, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE -// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 2, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 1, 0, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE, hex"4e487b71", 0x32 +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 2, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE, hex"4e487b71", 0x32 +// f(uint256[][2][],uint256,uint256,uint256): 0x80, 0, 2, 0, 1, 0x20, 0x40, 0x80, 1, 42, 1, 23 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol b/test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol index f9eebf14e674..6bd1acbbf6a8 100644 --- a/test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol +++ b/test/libsolidity/semanticTests/viaYul/calldata_bytes_array_bounds.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(bytes[] calldata a, uint256 i) external returns (uint) { return uint8(a[0][i]); @@ -9,4 +9,4 @@ contract C { // ---- // f(bytes[],uint256): 0x40, 0, 1, 0x20, 2, hex"6162" -> 0x61 // f(bytes[],uint256): 0x40, 1, 1, 0x20, 2, hex"6162" -> 0x62 -// f(bytes[],uint256): 0x40, 2, 1, 0x20, 2, hex"6162" -> FAILURE +// f(bytes[],uint256): 0x40, 2, 1, 0x20, 2, hex"6162" -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol b/test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol index cc9e449382ff..dd812a856f2f 100644 --- a/test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol +++ b/test/libsolidity/semanticTests/viaYul/cleanup/checked_arithmetic.sol @@ -52,6 +52,7 @@ contract C { } // ==== // compileViaYul: true +// compileToEwasm: also // ---- // add() -> 1, 1 // sub() -> 0, 0 @@ -60,6 +61,6 @@ contract C { // mod() -> 1, 0 // inc_pre() -> 1 // inc_post() -> 0 -// dec_pre() -> FAILURE -// dec_post() -> FAILURE -// neg() -> FAILURE +// dec_pre() -> FAILURE, hex"4e487b71", 0x11 +// dec_post() -> FAILURE, hex"4e487b71", 0x11 +// neg() -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol b/test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol index 86bd8ede7fd4..cadf063bdf69 100644 --- a/test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol +++ b/test/libsolidity/semanticTests/viaYul/cleanup/comparison.sol @@ -32,6 +32,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // eq() -> true // neq() -> false diff --git a/test/libsolidity/semanticTests/viaYul/comparison.sol b/test/libsolidity/semanticTests/viaYul/comparison.sol index c42ae6df9f4b..aa520f53b1f7 100644 --- a/test/libsolidity/semanticTests/viaYul/comparison.sol +++ b/test/libsolidity/semanticTests/viaYul/comparison.sol @@ -37,7 +37,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(address): 0x1234 -> false // f(address): 0x00 -> true diff --git a/test/libsolidity/semanticTests/viaYul/comparison_functions.sol b/test/libsolidity/semanticTests/viaYul/comparison_functions.sol index 60c8717ab317..f0b07ba3b473 100644 --- a/test/libsolidity/semanticTests/viaYul/comparison_functions.sol +++ b/test/libsolidity/semanticTests/viaYul/comparison_functions.sol @@ -1,9 +1,11 @@ contract C { + // If these two functions are identical, the optimiser + // on the old codegen path can deduplicate them, and breaking the test. function internal1() internal pure returns (bool) { return true; } function internal2() internal pure returns (bool) { - return true; + return false; } function equal() public pure returns (bool same, bool diff, bool inv) { @@ -23,7 +25,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // equal() -> true, false, false // unequal() -> false, true, true diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol index 0d577fdb97ae..7fb396b43d31 100644 --- a/test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_multiple.sol @@ -6,5 +6,6 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol index 6891be50058f..2b1205a66754 100644 --- a/test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_true_false_literal.sol @@ -7,5 +7,6 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol index af8ab732bbf9..d330609c81a9 100644 --- a/test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_tuple.sol @@ -6,6 +6,7 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> 1, 2 // f(bool): false -> 3, 4 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol index d263c2cea946..7544852655e2 100644 --- a/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_assignment.sol @@ -9,5 +9,6 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 6, 1, 5, 5 diff --git a/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol index 6d977d7627ce..256b5dbb7207 100644 --- a/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol +++ b/test/libsolidity/semanticTests/viaYul/conditional/conditional_with_variables.sol @@ -9,5 +9,6 @@ contract A { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3, 1, 3, 1 diff --git a/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_assignment.sol b/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_assignment.sol index 6a9291c32b04..afa5c531b4e2 100644 --- a/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_assignment.sol @@ -1,10 +1,11 @@ contract C { function f() public pure returns (uint16 x) { - uint8 y = uint8(0x12345678); + uint8 y = uint8(0x78); x = y; } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x78 diff --git a/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_function_call.sol b/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_function_call.sol index c239b07e447c..67a831de9f6c 100644 --- a/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_function_call.sol +++ b/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_function_call.sol @@ -7,6 +7,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // g() -> 0x1234567800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_local_assignment.sol b/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_local_assignment.sol index 22bb1f7ea358..a857d86c0d0a 100644 --- a/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_local_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/conversion/explicit_cast_local_assignment.sol @@ -5,6 +5,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 0x12345678 -> 0x78 diff --git a/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_assignment.sol b/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_assignment.sol index d6bba1bf55a3..60e553d0b3cc 100644 --- a/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_assignment.sol @@ -9,6 +9,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x78 diff --git a/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_function_call.sol b/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_function_call.sol index 47a9b1cea5a9..1588adef5fe9 100644 --- a/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_function_call.sol +++ b/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_function_call.sol @@ -12,6 +12,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // g() -> 0x78 diff --git a/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_local_assignment.sol b/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_local_assignment.sol index c99b50969034..ebae02779873 100644 --- a/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_local_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/conversion/implicit_cast_local_assignment.sol @@ -8,6 +8,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0x78 diff --git a/test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol b/test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol index b49f21cd436d..093e611e4aee 100644 --- a/test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol +++ b/test/libsolidity/semanticTests/viaYul/define_tuple_from_function_call.sol @@ -13,6 +13,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 3, 2, 1 // h() -> 3 diff --git a/test/libsolidity/semanticTests/viaYul/delete.sol b/test/libsolidity/semanticTests/viaYul/delete.sol index 9d1aaf7f4d11..6b23245eae1b 100644 --- a/test/libsolidity/semanticTests/viaYul/delete.sol +++ b/test/libsolidity/semanticTests/viaYul/delete.sol @@ -19,7 +19,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- -// call_deleted_internal_func() -> FAILURE +// call_deleted_internal_func() -> FAILURE, hex"4e487b71", 0x51 // call_internal_func() -> true diff --git a/test/libsolidity/semanticTests/viaYul/detect_add_overflow.sol b/test/libsolidity/semanticTests/viaYul/detect_add_overflow.sol index f16e4dac589b..6403e5139122 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_add_overflow.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_add_overflow.sol @@ -7,12 +7,12 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(uint256,uint256): 5, 6 -> 11 // f(uint256,uint256): -2, 1 -> -1 -// f(uint256,uint256): -2, 2 -> FAILURE -// f(uint256,uint256): 2, -2 -> FAILURE +// f(uint256,uint256): -2, 2 -> FAILURE, hex"4e487b71", 0x11 +// f(uint256,uint256): 2, -2 -> FAILURE, hex"4e487b71", 0x11 // g(uint8,uint8): 128, 64 -> 192 // g(uint8,uint8): 128, 127 -> 255 -// g(uint8,uint8): 128, 128 -> FAILURE +// g(uint8,uint8): 128, 128 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/detect_add_overflow_signed.sol b/test/libsolidity/semanticTests/viaYul/detect_add_overflow_signed.sol index 6e44c44baa3f..8389a7d2368e 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_add_overflow_signed.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_add_overflow_signed.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(int256,int256): 5, 6 -> 11 // f(int256,int256): -2, 1 -> -1 @@ -16,12 +16,12 @@ contract C { // f(int256,int256): -5, -6 -> -11 // f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0, 0x0F -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF // f(int256,int256): 0x0F, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0 -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 1 -> FAILURE -// f(int256,int256): 1, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE +// f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 1 -> FAILURE, hex"4e487b71", 0x11 +// f(int256,int256): 1, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000001, -1 -> 0x8000000000000000000000000000000000000000000000000000000000000000 // f(int256,int256): -1, 0x8000000000000000000000000000000000000000000000000000000000000001 -> 0x8000000000000000000000000000000000000000000000000000000000000000 -// f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000000, -1 -> FAILURE -// f(int256,int256): -1, 0x8000000000000000000000000000000000000000000000000000000000000000 -> FAILURE +// f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000000, -1 -> FAILURE, hex"4e487b71", 0x11 +// f(int256,int256): -1, 0x8000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): 5, 6 -> 11 // g(int8,int8): -2, 1 -> -1 // g(int8,int8): -2, 2 -> 0 @@ -29,9 +29,11 @@ contract C { // g(int8,int8): -5, -6 -> -11 // g(int8,int8): 126, 1 -> 127 // g(int8,int8): 1, 126 -> 127 -// g(int8,int8): 127, 1 -> FAILURE -// g(int8,int8): 1, 127 -> FAILURE +// g(int8,int8): 127, 1 -> FAILURE, hex"4e487b71", 0x11 +// g(int8,int8): 1, 127 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): -127, -1 -> -128 // g(int8,int8): -1, -127 -> -128 -// g(int8,int8): -127, -2 -> FAILURE -// g(int8,int8): -2, -127 -> FAILURE +// g(int8,int8): -127, -2 -> FAILURE, hex"4e487b71", 0x11 +// g(int8,int8): -2, -127 -> FAILURE, hex"4e487b71", 0x11 +// g(int8,int8): -128, 0 -> -128 +// g(int8,int8): 0, -128 -> -128 diff --git a/test/libsolidity/semanticTests/viaYul/detect_div_overflow.sol b/test/libsolidity/semanticTests/viaYul/detect_div_overflow.sol index 1de251b61f8a..bd53e3dc444e 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_div_overflow.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_div_overflow.sol @@ -10,18 +10,18 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(uint256,uint256): 10, 3 -> 3 -// f(uint256,uint256): 1, 0 -> FAILURE -// f(uint256,uint256): 0, 0 -> FAILURE +// f(uint256,uint256): 1, 0 -> FAILURE, hex"4e487b71", 0x12 +// f(uint256,uint256): 0, 0 -> FAILURE, hex"4e487b71", 0x12 // f(uint256,uint256): 0, 1 -> 0 // g(int8,int8): -10, 3 -> -3 // g(int8,int8): -10, -3 -> 3 -// g(int8,int8): -10, 0 -> FAILURE +// g(int8,int8): -10, 0 -> FAILURE, hex"4e487b71", 0x12 // g(int8,int8): -128, 1 -> -128 // g(int8,int8): -128, -2 -> 64 // g(int8,int8): -128, 2 -> -64 -// g(int8,int8): -128, -1 -> FAILURE +// g(int8,int8): -128, -1 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): -127, -1 -> 127 // h(uint256,uint256): 0x8000000000000000000000000000000000000000000000000000000000000000, -1 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol b/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol index 6340c71c385d..5a4f2d8227ec 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mod_zero.sol @@ -7,19 +7,20 @@ contract C { } } // ==== -// compileViaYul: true +// compileToEwasm: also +// compileViaYul: also // ---- // f(uint256,uint256): 10, 3 -> 1 // f(uint256,uint256): 10, 2 -> 0 // f(uint256,uint256): 11, 2 -> 1 // f(uint256,uint256): 2, 2 -> 0 -// f(uint256,uint256): 1, 0 -> FAILURE -// f(uint256,uint256): 0, 0 -> FAILURE +// f(uint256,uint256): 1, 0 -> FAILURE, hex"4e487b71", 0x12 +// f(uint256,uint256): 0, 0 -> FAILURE, hex"4e487b71", 0x12 // f(uint256,uint256): 0, 1 -> 0 // g(uint8,uint8): 10, 3 -> 1 // g(uint8,uint8): 10, 2 -> 0 // g(uint8,uint8): 11, 2 -> 1 // g(uint8,uint8): 2, 2 -> 0 -// g(uint8,uint8): 1, 0 -> FAILURE -// g(uint8,uint8): 0, 0 -> FAILURE +// g(uint8,uint8): 1, 0 -> FAILURE, hex"4e487b71", 0x12 +// g(uint8,uint8): 0, 0 -> FAILURE, hex"4e487b71", 0x12 // g(uint8,uint8): 0, 1 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol b/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol index d93e18d063c5..7216bd46e9ed 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mod_zero_signed.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(int256,int256): 10, 3 -> 1 // f(int256,int256): 10, 2 -> 0 @@ -16,9 +16,9 @@ contract C { // f(int256,int256): 10, -3 -> 1 // f(int256,int256): -10, -3 -> -1 // f(int256,int256): 2, 2 -> 0 -// f(int256,int256): 1, 0 -> FAILURE -// f(int256,int256): -1, 0 -> FAILURE -// f(int256,int256): 0, 0 -> FAILURE +// f(int256,int256): 1, 0 -> FAILURE, hex"4e487b71", 0x12 +// f(int256,int256): -1, 0 -> FAILURE, hex"4e487b71", 0x12 +// f(int256,int256): 0, 0 -> FAILURE, hex"4e487b71", 0x12 // f(int256,int256): 0, 1 -> 0 // f(int256,int256): 0, -1 -> 0 // g(int8,int8): 10, 3 -> 1 @@ -28,8 +28,10 @@ contract C { // g(int8,int8): 10, -3 -> 1 // g(int8,int8): -10, -3 -> -1 // g(int8,int8): 2, 2 -> 0 -// g(int8,int8): 1, 0 -> FAILURE -// g(int8,int8): -1, 0 -> FAILURE -// g(int8,int8): 0, 0 -> FAILURE +// g(int8,int8): 1, 0 -> FAILURE, hex"4e487b71", 0x12 +// g(int8,int8): -1, 0 -> FAILURE, hex"4e487b71", 0x12 +// g(int8,int8): 0, 0 -> FAILURE, hex"4e487b71", 0x12 // g(int8,int8): 0, 1 -> 0 // g(int8,int8): 0, -1 -> 0 +// g(int8,int8): -128, -128 -> 0 +// g(int8,int8): -128, 127 -> -1 diff --git a/test/libsolidity/semanticTests/viaYul/detect_mul_overflow.sol b/test/libsolidity/semanticTests/viaYul/detect_mul_overflow.sol index c4975868682d..2d3d6ada14b9 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mul_overflow.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mul_overflow.sol @@ -7,31 +7,31 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(uint256,uint256): 5, 6 -> 30 // f(uint256,uint256): -1, 1 -> -1 -// f(uint256,uint256): -1, 2 -> FAILURE -// f(uint256,uint256): 0x8000000000000000000000000000000000000000000000000000000000000000, 2 -> FAILURE +// f(uint256,uint256): -1, 2 -> FAILURE, hex"4e487b71", 0x11 +// f(uint256,uint256): 0x8000000000000000000000000000000000000000000000000000000000000000, 2 -> FAILURE, hex"4e487b71", 0x11 // f(uint256,uint256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 2 -> -2 -// f(uint256,uint256): 2, 0x8000000000000000000000000000000000000000000000000000000000000000 -> FAILURE +// f(uint256,uint256): 2, 0x8000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 // f(uint256,uint256): 2, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> -2 -// f(uint256,uint256): 0x0100000000000000000000000000000000, 0x0100000000000000000000000000000000 -> FAILURE +// f(uint256,uint256): 0x0100000000000000000000000000000000, 0x0100000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 // f(uint256,uint256): 0x0100000000000000000000000000000000, 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 // f(uint256,uint256): 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 0x0100000000000000000000000000000000 -> 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000 // f(uint256,uint256): 0x0100000000000000000000000000000001, 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> -1 // f(uint256,uint256): 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 0x0100000000000000000000000000000001 -> -1 -// f(uint256,uint256): 0x0100000000000000000000000000000002, 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE -// f(uint256,uint256): 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 0x0100000000000000000000000000000002 -> FAILURE +// f(uint256,uint256): 0x0100000000000000000000000000000002, 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE, hex"4e487b71", 0x11 +// f(uint256,uint256): 0x00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 0x0100000000000000000000000000000002 -> FAILURE, hex"4e487b71", 0x11 // f(uint256,uint256): -1, 0 -> 0 // f(uint256,uint256): 0, -1 -> 0 // g(uint8,uint8): 5, 6 -> 30 -// g(uint8,uint8): 0x80, 2 -> FAILURE +// g(uint8,uint8): 0x80, 2 -> FAILURE, hex"4e487b71", 0x11 // g(uint8,uint8): 0x7F, 2 -> 254 // g(uint8,uint8): 2, 0x7F -> 254 -// g(uint8,uint8): 0x10, 0x10 -> FAILURE +// g(uint8,uint8): 0x10, 0x10 -> FAILURE, hex"4e487b71", 0x11 // g(uint8,uint8): 0x0F, 0x11 -> 0xFF -// g(uint8,uint8): 0x0F, 0x12 -> FAILURE -// g(uint8,uint8): 0x12, 0x0F -> FAILURE +// g(uint8,uint8): 0x0F, 0x12 -> FAILURE, hex"4e487b71", 0x11 +// g(uint8,uint8): 0x12, 0x0F -> FAILURE, hex"4e487b71", 0x11 // g(uint8,uint8): 0xFF, 0 -> 0 // g(uint8,uint8): 0, 0xFF -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/detect_mul_overflow_signed.sol b/test/libsolidity/semanticTests/viaYul/detect_mul_overflow_signed.sol index 930a24ed7135..e9db203f921c 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_mul_overflow_signed.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_mul_overflow_signed.sol @@ -7,52 +7,57 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(int256,int256): 5, 6 -> 30 // f(int256,int256): -1, 1 -> -1 -// f(int256,int256): -1, 2 -> -2 -// # positive, positive # +// f(int256,int256): -1, 2 -> -2 # positive, positive # // f(int256,int256): 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 2 -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -// f(int256,int256): 0x4000000000000000000000000000000000000000000000000000000000000000, 2 -> FAILURE +// f(int256,int256): 0x4000000000000000000000000000000000000000000000000000000000000000, 2 -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): 2, 0x3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -// f(int256,int256): 2, 0x4000000000000000000000000000000000000000000000000000000000000000 -> FAILURE -// # positive, negative # +// f(int256,int256): 2, 0x4000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 # positive, negative # +// f(int256,int256): 2, 0x4000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 # positive, negative # +// f(int256,int256): 2, 0x4000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 # positive, negative # // f(int256,int256): 0x4000000000000000000000000000000000000000000000000000000000000000, -2 -> 0x8000000000000000000000000000000000000000000000000000000000000000 -// f(int256,int256): 0x4000000000000000000000000000000000000000000000000000000000000001, -2 -> FAILURE +// f(int256,int256): 0x4000000000000000000000000000000000000000000000000000000000000001, -2 -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): 2, 0xC000000000000000000000000000000000000000000000000000000000000000 -> 0x8000000000000000000000000000000000000000000000000000000000000000 -// f(int256,int256): 2, 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE -// # negative, positive # +// f(int256,int256): 2, 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE, hex"4e487b71", 0x11 # negative, positive # +// f(int256,int256): 2, 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE, hex"4e487b71", 0x11 # negative, positive # +// f(int256,int256): 2, 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE, hex"4e487b71", 0x11 # negative, positive # // f(int256,int256): -2, 0x4000000000000000000000000000000000000000000000000000000000000000 -> 0x8000000000000000000000000000000000000000000000000000000000000000 -// f(int256,int256): -2, 0x4000000000000000000000000000000000000000000000000000000000000001 -> FAILURE +// f(int256,int256): -2, 0x4000000000000000000000000000000000000000000000000000000000000001 -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): 0xC000000000000000000000000000000000000000000000000000000000000000, 2 -> 0x8000000000000000000000000000000000000000000000000000000000000000 -// f(int256,int256): 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 2 -> FAILURE -// # negative, negative # +// f(int256,int256): 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 2 -> FAILURE, hex"4e487b71", 0x11 # negative, negative # +// f(int256,int256): 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 2 -> FAILURE, hex"4e487b71", 0x11 # negative, negative # +// f(int256,int256): 0xBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, 2 -> FAILURE, hex"4e487b71", 0x11 # negative, negative # // f(int256,int256): 0xC000000000000000000000000000000000000000000000000000000000000001, -2 -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -// f(int256,int256): 0xC000000000000000000000000000000000000000000000000000000000000000, -2 -> FAILURE +// f(int256,int256): 0xC000000000000000000000000000000000000000000000000000000000000000, -2 -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): -2, 0xC000000000000000000000000000000000000000000000000000000000000001 -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE -// f(int256,int256): -2, 0xC000000000000000000000000000000000000000000000000000000000000000 -> FAILURE -// # small type # +// f(int256,int256): -2, 0xC000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 # small type # +// f(int256,int256): -2, 0xC000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 # small type # +// f(int256,int256): -2, 0xC000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 # small type # // g(int8,int8): 5, 6 -> 30 // g(int8,int8): -1, 1 -> -1 -// g(int8,int8): -1, 2 -> -2 -// # positive, positive # +// g(int8,int8): -1, 2 -> -2 # positive, positive # // g(int8,int8): 63, 2 -> 126 -// g(int8,int8): 64, 2 -> FAILURE +// g(int8,int8): 64, 2 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): 2, 63 -> 126 -// g(int8,int8): 2, 64 -> FAILURE -// # positive, negative # +// g(int8,int8): 2, 64 -> FAILURE, hex"4e487b71", 0x11 # positive, negative # +// g(int8,int8): 2, 64 -> FAILURE, hex"4e487b71", 0x11 # positive, negative # +// g(int8,int8): 2, 64 -> FAILURE, hex"4e487b71", 0x11 # positive, negative # // g(int8,int8): 64, -2 -> -128 -// g(int8,int8): 65, -2 -> FAILURE +// g(int8,int8): 65, -2 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): 2, -64 -> -128 -// g(int8,int8): 2, -65 -> FAILURE -// # negative, positive # +// g(int8,int8): 2, -65 -> FAILURE, hex"4e487b71", 0x11 # negative, positive # +// g(int8,int8): 2, -65 -> FAILURE, hex"4e487b71", 0x11 # negative, positive # +// g(int8,int8): 2, -65 -> FAILURE, hex"4e487b71", 0x11 # negative, positive # // g(int8,int8): -2, 64 -> -128 -// g(int8,int8): -2, 65 -> FAILURE +// g(int8,int8): -2, 65 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): -64, 2 -> -128 -// g(int8,int8): -65, 2 -> FAILURE -// # negative, negative # +// g(int8,int8): -65, 2 -> FAILURE, hex"4e487b71", 0x11 # negative, negative # +// g(int8,int8): -65, 2 -> FAILURE, hex"4e487b71", 0x11 # negative, negative # +// g(int8,int8): -65, 2 -> FAILURE, hex"4e487b71", 0x11 # negative, negative # // g(int8,int8): -63, -2 -> 126 -// g(int8,int8): -64, -2 -> FAILURE +// g(int8,int8): -64, -2 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): -2, -63 -> 126 -// g(int8,int8): -2, -64 -> FAILURE +// g(int8,int8): -2, -64 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/detect_sub_overflow.sol b/test/libsolidity/semanticTests/viaYul/detect_sub_overflow.sol index 5342052ff514..0ee16e27bec9 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_sub_overflow.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_sub_overflow.sol @@ -7,11 +7,12 @@ contract C { } } // ==== -// compileViaYul: true +// compileToEwasm: also +// compileViaYul: also // ---- // f(uint256,uint256): 6, 5 -> 1 // f(uint256,uint256): 6, 6 -> 0 -// f(uint256,uint256): 5, 6 -> FAILURE +// f(uint256,uint256): 5, 6 -> FAILURE, hex"4e487b71", 0x11 // g(uint8,uint8): 6, 5 -> 1 // g(uint8,uint8): 6, 6 -> 0 -// g(uint8,uint8): 5, 6 -> FAILURE +// g(uint8,uint8): 5, 6 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/detect_sub_overflow_signed.sol b/test/libsolidity/semanticTests/viaYul/detect_sub_overflow_signed.sol index 1a87ef9ec216..d1c2f600671e 100644 --- a/test/libsolidity/semanticTests/viaYul/detect_sub_overflow_signed.sol +++ b/test/libsolidity/semanticTests/viaYul/detect_sub_overflow_signed.sol @@ -7,7 +7,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // f(int256,int256): 5, 6 -> -1 // f(int256,int256): -2, 1 -> -3 @@ -16,16 +16,16 @@ contract C { // f(int256,int256): 2, 2 -> 0 // f(int256,int256): -5, -6 -> 1 // f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0, -15 -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0, -16 -> FAILURE -// f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, -1 -> FAILURE +// f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0, -16 -> FAILURE, hex"4e487b71", 0x11 +// f(int256,int256): 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, -1 -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): 15, 0x8000000000000000000000000000000000000000000000000000000000000010 -> 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// f(int256,int256): 16, 0x8000000000000000000000000000000000000000000000000000000000000010 -> FAILURE -// f(int256,int256): 1, 0x8000000000000000000000000000000000000000000000000000000000000000 -> FAILURE +// f(int256,int256): 16, 0x8000000000000000000000000000000000000000000000000000000000000010 -> FAILURE, hex"4e487b71", 0x11 +// f(int256,int256): 1, 0x8000000000000000000000000000000000000000000000000000000000000000 -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): -1, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> 0x8000000000000000000000000000000000000000000000000000000000000000 -// f(int256,int256): -2, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE +// f(int256,int256): -2, 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> FAILURE, hex"4e487b71", 0x11 // f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000001, 1 -> 0x8000000000000000000000000000000000000000000000000000000000000000 -// f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000001, 2 -> FAILURE -// f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000000, 1 -> FAILURE +// f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000001, 2 -> FAILURE, hex"4e487b71", 0x11 +// f(int256,int256): 0x8000000000000000000000000000000000000000000000000000000000000000, 1 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): 5, 6 -> -1 // g(int8,int8): -2, 1 -> -3 // g(int8,int8): -2, 2 -> -4 @@ -34,9 +34,9 @@ contract C { // g(int8,int8): -5, -6 -> 1 // g(int8,int8): 126, -1 -> 127 // g(int8,int8): 1, -126 -> 127 -// g(int8,int8): 127, -1 -> FAILURE -// g(int8,int8): 1, -127 -> FAILURE +// g(int8,int8): 127, -1 -> FAILURE, hex"4e487b71", 0x11 +// g(int8,int8): 1, -127 -> FAILURE, hex"4e487b71", 0x11 // g(int8,int8): -127, 1 -> -128 // g(int8,int8): -1, 127 -> -128 -// g(int8,int8): -127, 2 -> FAILURE -// g(int8,int8): -2, 127 -> FAILURE +// g(int8,int8): -127, 2 -> FAILURE, hex"4e487b71", 0x11 +// g(int8,int8): -2, 127 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol b/test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol index 633fc45c6d48..66a92469ac26 100644 --- a/test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol +++ b/test/libsolidity/semanticTests/viaYul/dirty_calldata_struct.sol @@ -1,10 +1,10 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint16[] m; } function f(S calldata s) public pure returns (bool correct) { - int8 x = int8(s.m[0]); + int8 x = int8(int16(s.m[0])); uint r; assembly { r := x diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol index 36a253b02452..0ec8f9a75774 100644 --- a/test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_dynamic_array.sol @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: true +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol index f4f0525bd1ae..594a97abbf9b 100644 --- a/test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_int32.sol @@ -4,7 +4,7 @@ contract C { assembly { mstore(m, 0xdeadbeef15dead) } - int32 x = int32(m[0]); + int32 x = int32(uint32(m[0])); uint r; assembly { r := x @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: true +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol index 4742bae2dfdf..7a9aa542bff7 100644 --- a/test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_static_array.sol @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: true +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol index 411cbe069e6a..1cbfd84d988f 100644 --- a/test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_struct.sol @@ -18,5 +18,6 @@ contract C { } // ==== // compileViaYul: true +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol b/test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol index a23a566c5bbe..b838991764ae 100644 --- a/test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol +++ b/test/libsolidity/semanticTests/viaYul/dirty_memory_uint32.sol @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: true +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/erc20.sol b/test/libsolidity/semanticTests/viaYul/erc20.sol index 9a49a053bb22..d9493d8e88fe 100644 --- a/test/libsolidity/semanticTests/viaYul/erc20.sol +++ b/test/libsolidity/semanticTests/viaYul/erc20.sol @@ -1,4 +1,4 @@ -pragma solidity >=0.4.0 <0.8.0; +pragma solidity >=0.4.0 <0.9.0; contract ERC20 { event Transfer(address indexed from, address indexed to, uint256 value); @@ -99,6 +99,6 @@ contract ERC20 { // totalSupply() -> 20 // transfer(address,uint256): 2, 5 -> true // decreaseAllowance(address,uint256): 2, 0 -> true -// decreaseAllowance(address,uint256): 2, 1 -> FAILURE +// decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11 // transfer(address,uint256): 2, 14 -> true -// transfer(address,uint256): 2, 2 -> FAILURE \ No newline at end of file +// transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/exp.sol b/test/libsolidity/semanticTests/viaYul/exp.sol new file mode 100644 index 000000000000..ea13f14069a4 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp.sol @@ -0,0 +1,20 @@ +contract C { + function f(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint256,uint256): 0, 0 -> 1 +// f(uint256,uint256): 0, 1 -> 0x00 +// f(uint256,uint256): 0, 2 -> 0x00 +// f(uint256,uint256): 1, 0 -> 1 +// f(uint256,uint256): 1, 1 -> 1 +// f(uint256,uint256): 1, 2 -> 1 +// f(uint256,uint256): 2, 0 -> 1 +// f(uint256,uint256): 2, 1 -> 2 +// f(uint256,uint256): 2, 2 -> 4 +// f(uint256,uint256): 7, 63 -> 174251498233690814305510551794710260107945042018748343 +// f(uint256,uint256): 128, 2 -> 0x4000 diff --git a/test/libsolidity/semanticTests/viaYul/exp_literals.sol b/test/libsolidity/semanticTests/viaYul/exp_literals.sol new file mode 100644 index 000000000000..f4e9b272098b --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_literals.sol @@ -0,0 +1,50 @@ +contract C { + function exp_2(uint y) public returns (uint) { + return 2**y; + } + function exp_minus_2(uint y) public returns (int) { + return (-2)**y; + } + + function exp_uint_max(uint y) public returns (uint) { + return (2**256 - 1)**y; + } + function exp_int_max(uint y) public returns (int) { + return ((-2)**255)**y; + } + + function exp_5(uint y) public returns (uint) { + return 5**y; + } + function exp_minus_5(uint y) public returns (int) { + return (-5)**y; + } + + function exp_256(uint y) public returns (uint) { + return 256**y; + } + function exp_minus_256(uint y) public returns (int) { + return (-256)**y; + } + +} +// ==== +// compileViaYul: true +// compileToEwasm: also +// ---- +// exp_2(uint256): 255 -> 57896044618658097711785492504343953926634992332820282019728792003956564819968 +// exp_2(uint256): 256 -> FAILURE, hex"4e487b71", 0x11 +// exp_minus_2(uint256): 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// exp_minus_2(uint256): 256 -> FAILURE, hex"4e487b71", 0x11 +// exp_uint_max(uint256): 1 -> 115792089237316195423570985008687907853269984665640564039457584007913129639935 +// exp_uint_max(uint256): 2 -> FAILURE, hex"4e487b71", 0x11 +// exp_int_max(uint256): 1 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// exp_int_max(uint256): 2 -> FAILURE, hex"4e487b71", 0x11 +// exp_5(uint256): 110 -> 77037197775489434122239117703397092741524065928615527809597551822662353515625 +// exp_5(uint256): 111 -> FAILURE, hex"4e487b71", 0x11 +// exp_minus_5(uint256): 109 -> -15407439555097886824447823540679418548304813185723105561919510364532470703125 +// exp_minus_5(uint256): 110 -> FAILURE, hex"4e487b71", 0x11 +// exp_256(uint256): 31 -> 452312848583266388373324160190187140051835877600158453279131187530910662656 +// exp_256(uint256): 32 -> FAILURE, hex"4e487b71", 0x11 +// exp_minus_256(uint256): 31 -> -452312848583266388373324160190187140051835877600158453279131187530910662656 +// exp_minus_256(uint256): 32 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/exp_literals_success.sol b/test/libsolidity/semanticTests/viaYul/exp_literals_success.sol new file mode 100644 index 000000000000..765645c70672 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_literals_success.sol @@ -0,0 +1,42 @@ +contract C { + function exp_2(uint y) public returns (uint) { + return 2**y; + } + function exp_minus_2(uint y) public returns (int) { + return (-2)**y; + } + + function exp_uint_max(uint y) public returns (uint) { + return (2**256 - 1)**y; + } + function exp_int_max(uint y) public returns (int) { + return ((-2)**255)**y; + } + + function exp_5(uint y) public returns (uint) { + return 5**y; + } + function exp_minus_5(uint y) public returns (int) { + return (-5)**y; + } + + function exp_256(uint y) public returns (uint) { + return 256**y; + } + function exp_minus_256(uint y) public returns (int) { + return (-256)**y; + } + +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// exp_2(uint256): 255 -> 57896044618658097711785492504343953926634992332820282019728792003956564819968 +// exp_minus_2(uint256): 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// exp_uint_max(uint256): 1 -> 115792089237316195423570985008687907853269984665640564039457584007913129639935 +// exp_int_max(uint256): 1 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// exp_5(uint256): 110 -> 77037197775489434122239117703397092741524065928615527809597551822662353515625 +// exp_minus_5(uint256): 109 -> -15407439555097886824447823540679418548304813185723105561919510364532470703125 +// exp_256(uint256): 31 -> 452312848583266388373324160190187140051835877600158453279131187530910662656 +// exp_minus_256(uint256): 31 -> -452312848583266388373324160190187140051835877600158453279131187530910662656 diff --git a/test/libsolidity/semanticTests/viaYul/exp_neg.sol b/test/libsolidity/semanticTests/viaYul/exp_neg.sol new file mode 100644 index 000000000000..149e9159a22e --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_neg.sol @@ -0,0 +1,33 @@ +contract C { + function f(int x, uint y) public returns (int) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(int256,uint256): 0, 0 -> 1 +// f(int256,uint256): 0, 1 -> 0x00 +// f(int256,uint256): 0, 2 -> 0x00 +// f(int256,uint256): 1, 0 -> 1 +// f(int256,uint256): 1, 1 -> 1 +// f(int256,uint256): 1, 2 -> 1 +// f(int256,uint256): 2, 0 -> 1 +// f(int256,uint256): 2, 1 -> 2 +// f(int256,uint256): 2, 2 -> 4 +// f(int256,uint256): 7, 63 -> 174251498233690814305510551794710260107945042018748343 +// f(int256,uint256): 128, 2 -> 0x4000 +// f(int256,uint256): -1, 0 -> 1 +// f(int256,uint256): -1, 1 -> -1 +// f(int256,uint256): -1, 2 -> 1 +// f(int256,uint256): -2, 0 -> 1 +// f(int256,uint256): -2, 1 -> -2 +// f(int256,uint256): -2, 2 -> 4 +// f(int256,uint256): -7, 63 -> -174251498233690814305510551794710260107945042018748343 +// f(int256,uint256): -128, 2 -> 0x4000 +// f(int256,uint256): -1, 115792089237316195423570985008687907853269984665640564039457584007913129639935 -> -1 +// f(int256,uint256): -2, 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// f(int256,uint256): -8, 85 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// f(int256,uint256): -131072, 15 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// f(int256,uint256): -32, 51 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 +// f(int256,uint256): -57896044618658097711785492504343953926634992332820282019728792003956564819968, 1 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 diff --git a/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol b/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol new file mode 100644 index 000000000000..93617ffbbffa --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_neg_overflow.sol @@ -0,0 +1,38 @@ +contract C { + function f(int8 x, uint y) public returns (int) { + return x**y; + } + function g(int256 x, uint y) public returns (int) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(int8,uint256): 2, 6 -> 64 +// f(int8,uint256): 2, 7 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): 2, 8 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): -2, 6 -> 64 +// f(int8,uint256): -2, 7 -> -128 +// f(int8,uint256): -2, 8 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): 6, 3 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): 7, 2 -> 0x31 +// f(int8,uint256): 7, 3 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): -7, 2 -> 0x31 +// f(int8,uint256): -7, 3 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): -7, 4 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): 127, 31 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): 127, 131 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): -128, 0 -> 1 +// f(int8,uint256): -128, 1 -> -128 +// f(int8,uint256): -128, 31 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): -128, 131 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): -11, 2 -> 121 +// f(int8,uint256): -12, 2 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): 12, 2 -> FAILURE, hex"4e487b71", 0x11 +// f(int8,uint256): -5, 3 -> -125 +// f(int8,uint256): -6, 3 -> FAILURE, hex"4e487b71", 0x11 +// g(int256,uint256): -7, 90 -> 11450477594321044359340126713545146077054004823284978858214566372120240027249 +// g(int256,uint256): -7, 91 -> FAILURE, hex"4e487b71", 0x11 +// g(int256,uint256): -63, 42 -> 3735107253208426854890677539053540390278853997836851167913009474475553834369 +// g(int256,uint256): -63, 43 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/exp_overflow.sol b/test/libsolidity/semanticTests/viaYul/exp_overflow.sol new file mode 100644 index 000000000000..b3d998517817 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_overflow.sol @@ -0,0 +1,31 @@ +contract C { + function f(uint8 x, uint8 y) public returns (uint) { + return x**y; + } + function g(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint8,uint8): 2, 7 -> 0x80 +// f(uint8,uint8): 2, 8 -> FAILURE, hex"4e487b71", 0x11 +// f(uint8,uint8): 15, 2 -> 225 +// f(uint8,uint8): 6, 3 -> 0xd8 +// f(uint8,uint8): 7, 2 -> 0x31 +// f(uint8,uint8): 7, 3 -> FAILURE, hex"4e487b71", 0x11 +// f(uint8,uint8): 7, 4 -> FAILURE, hex"4e487b71", 0x11 +// f(uint8,uint8): 255, 31 -> FAILURE, hex"4e487b71", 0x11 +// f(uint8,uint8): 255, 131 -> FAILURE, hex"4e487b71", 0x11 +// g(uint256,uint256): 0x200000000000000000000000000000000, 1 -> 0x0200000000000000000000000000000000 +// g(uint256,uint256): 0x100000000000000000000000000000010, 2 -> FAILURE, hex"4e487b71", 0x11 +// g(uint256,uint256): 0x200000000000000000000000000000000, 2 -> FAILURE, hex"4e487b71", 0x11 +// g(uint256,uint256): 0x200000000000000000000000000000000, 3 -> FAILURE, hex"4e487b71", 0x11 +// g(uint256,uint256): 255, 31 -> 400631961586894742455537928461950192806830589109049416147172451019287109375 +// g(uint256,uint256): 255, 32 -> -13630939032658036097408813250890608687528184442832962921928608997994916749311 +// g(uint256,uint256): 255, 33 -> FAILURE, hex"4e487b71", 0x11 +// g(uint256,uint256): 255, 131 -> FAILURE, hex"4e487b71", 0x11 +// g(uint256,uint256): 258, 31 -> 575719427506838823084316385994930914701079543089399988096291424922125729792 +// g(uint256,uint256): 258, 37 -> FAILURE, hex"4e487b71", 0x11 +// g(uint256,uint256): 258, 131 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/viaYul/exp_various.sol b/test/libsolidity/semanticTests/viaYul/exp_various.sol new file mode 100644 index 000000000000..0ea53790f63f --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/exp_various.sol @@ -0,0 +1,51 @@ +contract C { + function f(uint8 x, uint8 y) public returns (uint) { + return x**y; + } + function g(uint x, uint y) public returns (uint) { + return x**y; + } +} +// ==== +// compileViaYul: also +// compileToEwasm: also +// ---- +// f(uint8,uint8): 0, 0 -> 1 +// f(uint8,uint8): 0, 1 -> 0x00 +// f(uint8,uint8): 0, 2 -> 0x00 +// f(uint8,uint8): 0, 3 -> 0x00 +// f(uint8,uint8): 1, 0 -> 1 +// f(uint8,uint8): 1, 1 -> 1 +// f(uint8,uint8): 1, 2 -> 1 +// f(uint8,uint8): 1, 3 -> 1 +// f(uint8,uint8): 2, 0 -> 1 +// f(uint8,uint8): 2, 1 -> 2 +// f(uint8,uint8): 2, 2 -> 4 +// f(uint8,uint8): 2, 3 -> 8 +// f(uint8,uint8): 3, 0 -> 1 +// f(uint8,uint8): 3, 1 -> 3 +// f(uint8,uint8): 3, 2 -> 9 +// f(uint8,uint8): 3, 3 -> 0x1b +// f(uint8,uint8): 10, 0 -> 1 +// f(uint8,uint8): 10, 1 -> 0x0a +// f(uint8,uint8): 10, 2 -> 100 +// g(uint256,uint256): 0, 0 -> 1 +// g(uint256,uint256): 0, 1 -> 0x00 +// g(uint256,uint256): 0, 2 -> 0x00 +// g(uint256,uint256): 0, 3 -> 0x00 +// g(uint256,uint256): 1, 0 -> 1 +// g(uint256,uint256): 1, 1 -> 1 +// g(uint256,uint256): 1, 2 -> 1 +// g(uint256,uint256): 1, 3 -> 1 +// g(uint256,uint256): 2, 0 -> 1 +// g(uint256,uint256): 2, 1 -> 2 +// g(uint256,uint256): 2, 2 -> 4 +// g(uint256,uint256): 2, 3 -> 8 +// g(uint256,uint256): 3, 0 -> 1 +// g(uint256,uint256): 3, 1 -> 3 +// g(uint256,uint256): 3, 2 -> 9 +// g(uint256,uint256): 3, 3 -> 0x1b +// g(uint256,uint256): 10, 10 -> 10000000000 +// g(uint256,uint256): 10, 77 -> -15792089237316195423570985008687907853269984665640564039457584007913129639936 +// g(uint256,uint256): 256, 2 -> 0x010000 +// g(uint256,uint256): 256, 31 -> 0x0100000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/viaYul/function_entry_checks.sol b/test/libsolidity/semanticTests/viaYul/function_entry_checks.sol index 4343419cc8fe..44466d911c77 100644 --- a/test/libsolidity/semanticTests/viaYul/function_entry_checks.sol +++ b/test/libsolidity/semanticTests/viaYul/function_entry_checks.sol @@ -17,7 +17,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0 // g(uint256,uint256): 1, -2 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/function_pointers.sol b/test/libsolidity/semanticTests/viaYul/function_pointers.sol index 5bd433a3d2a4..0ea938d66e12 100644 --- a/test/libsolidity/semanticTests/viaYul/function_pointers.sol +++ b/test/libsolidity/semanticTests/viaYul/function_pointers.sol @@ -18,8 +18,9 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- -// f() -> FAILURE +// f() -> FAILURE, hex"4e487b71", 0x51 // g() -> FAILURE -// h2() -> FAILURE +// h2() -> FAILURE, hex"4e487b71", 0x51 // k2() -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/if.sol b/test/libsolidity/semanticTests/viaYul/if.sol index 18d3445dc04b..49c9453b8640 100644 --- a/test/libsolidity/semanticTests/viaYul/if.sol +++ b/test/libsolidity/semanticTests/viaYul/if.sol @@ -60,7 +60,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(bool): 0 -> 23 // f(bool): 1 -> 42 diff --git a/test/libsolidity/semanticTests/viaYul/keccak.sol b/test/libsolidity/semanticTests/viaYul/keccak.sol index 58c37ebd6256..1befe8f7c0e6 100644 --- a/test/libsolidity/semanticTests/viaYul/keccak.sol +++ b/test/libsolidity/semanticTests/viaYul/keccak.sol @@ -8,7 +8,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // keccak1() -> 0x64e604787cbf194841e7b68d7cd28786f6c9a0a3ab9f8b0a0e87cb4387ab0107 // keccak2() -> 0x64e604787cbf194841e7b68d7cd28786f6c9a0a3ab9f8b0a0e87cb4387ab0107 diff --git a/test/libsolidity/semanticTests/viaYul/local_address_assignment.sol b/test/libsolidity/semanticTests/viaYul/local_address_assignment.sol index 84c38a324a76..438108da3345 100644 --- a/test/libsolidity/semanticTests/viaYul/local_address_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/local_address_assignment.sol @@ -5,6 +5,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(address): 0x1234 -> 0x1234 diff --git a/test/libsolidity/semanticTests/viaYul/local_assignment.sol b/test/libsolidity/semanticTests/viaYul/local_assignment.sol index b423cbf25b77..cdb84c3cb2b4 100644 --- a/test/libsolidity/semanticTests/viaYul/local_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/local_assignment.sol @@ -5,6 +5,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(uint256): 6 -> 6 diff --git a/test/libsolidity/semanticTests/viaYul/local_bool_assignment.sol b/test/libsolidity/semanticTests/viaYul/local_bool_assignment.sol index 07189b6fb73a..4b1371f33cd8 100644 --- a/test/libsolidity/semanticTests/viaYul/local_bool_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/local_bool_assignment.sol @@ -5,6 +5,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(bool): true -> true diff --git a/test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol b/test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol index 2fb33f08af11..d94e8710b5a4 100644 --- a/test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/local_tuple_assignment.sol @@ -23,6 +23,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // x() -> 17 // f(uint256,uint256): 23, 42 -> 23, 42 diff --git a/test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol b/test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol index 2788e6370c60..2001307e057a 100644 --- a/test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol +++ b/test/libsolidity/semanticTests/viaYul/local_variable_without_init.sol @@ -6,5 +6,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/loops/break.sol b/test/libsolidity/semanticTests/viaYul/loops/break.sol index 25b2ff291228..2f683c0cf6f5 100644 --- a/test/libsolidity/semanticTests/viaYul/loops/break.sol +++ b/test/libsolidity/semanticTests/viaYul/loops/break.sol @@ -24,7 +24,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2 // g() -> 2 diff --git a/test/libsolidity/semanticTests/viaYul/loops/continue.sol b/test/libsolidity/semanticTests/viaYul/loops/continue.sol index 5d6aa13ba6c2..14e5ca5f4c6d 100644 --- a/test/libsolidity/semanticTests/viaYul/loops/continue.sol +++ b/test/libsolidity/semanticTests/viaYul/loops/continue.sol @@ -30,7 +30,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 11 // g() -> 11 diff --git a/test/libsolidity/semanticTests/viaYul/loops/return.sol b/test/libsolidity/semanticTests/viaYul/loops/return.sol index a28df28a7f5c..b8ced9fe6eef 100644 --- a/test/libsolidity/semanticTests/viaYul/loops/return.sol +++ b/test/libsolidity/semanticTests/viaYul/loops/return.sol @@ -27,7 +27,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1 // g() -> 1 diff --git a/test/libsolidity/semanticTests/viaYul/loops/simple.sol b/test/libsolidity/semanticTests/viaYul/loops/simple.sol index 6cdbc6bac15e..b42f79c444e3 100644 --- a/test/libsolidity/semanticTests/viaYul/loops/simple.sol +++ b/test/libsolidity/semanticTests/viaYul/loops/simple.sol @@ -30,7 +30,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 1024 // g() -> 1024 diff --git a/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol b/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol index 78d9a39b9b16..c7790aa0cfc6 100644 --- a/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol +++ b/test/libsolidity/semanticTests/viaYul/mapping_enum_key_getter.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract test { enum E { A, B, C } mapping(E => uint8) public table; diff --git a/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol b/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol index 880522d60178..299f790cefed 100644 --- a/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol +++ b/test/libsolidity/semanticTests/viaYul/mapping_string_key.sol @@ -5,6 +5,6 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // set(string): 0x20, 32, "01234567890123456789012345678901" -> diff --git a/test/libsolidity/semanticTests/viaYul/memory_struct_allow.sol b/test/libsolidity/semanticTests/viaYul/memory_struct_allow.sol index b1af4fab8563..80848200100e 100644 --- a/test/libsolidity/semanticTests/viaYul/memory_struct_allow.sol +++ b/test/libsolidity/semanticTests/viaYul/memory_struct_allow.sol @@ -17,5 +17,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 0, 0 diff --git a/test/libsolidity/semanticTests/viaYul/msg_sender.sol b/test/libsolidity/semanticTests/viaYul/msg_sender.sol index 146911b33f14..5ce5b530b072 100644 --- a/test/libsolidity/semanticTests/viaYul/msg_sender.sol +++ b/test/libsolidity/semanticTests/viaYul/msg_sender.sol @@ -6,6 +6,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // test() -> true diff --git a/test/libsolidity/semanticTests/viaYul/require.sol b/test/libsolidity/semanticTests/viaYul/require.sol index 09e96c0f4035..6376dba95f38 100644 --- a/test/libsolidity/semanticTests/viaYul/require.sol +++ b/test/libsolidity/semanticTests/viaYul/require.sol @@ -30,6 +30,7 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // EVMVersion: >=byzantium // ---- // f(bool): true -> true diff --git a/test/libsolidity/semanticTests/viaYul/return.sol b/test/libsolidity/semanticTests/viaYul/return.sol index 32a21ae7617f..1b5a983c1873 100644 --- a/test/libsolidity/semanticTests/viaYul/return.sol +++ b/test/libsolidity/semanticTests/viaYul/return.sol @@ -5,6 +5,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 7 diff --git a/test/libsolidity/semanticTests/viaYul/return_and_convert.sol b/test/libsolidity/semanticTests/viaYul/return_and_convert.sol index 613184aa996f..082dc4048f18 100644 --- a/test/libsolidity/semanticTests/viaYul/return_and_convert.sol +++ b/test/libsolidity/semanticTests/viaYul/return_and_convert.sol @@ -6,6 +6,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 255 diff --git a/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol index d0085dec19f6..bb7da7a73af1 100644 --- a/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol +++ b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol @@ -12,5 +12,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 0, 0 diff --git a/test/libsolidity/semanticTests/viaYul/short_circuit.sol b/test/libsolidity/semanticTests/viaYul/short_circuit.sol index 6e92b6ee24c8..7acb4cbfdddc 100644 --- a/test/libsolidity/semanticTests/viaYul/short_circuit.sol +++ b/test/libsolidity/semanticTests/viaYul/short_circuit.sol @@ -9,7 +9,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // or(uint256): 0 -> true, 0 // and(uint256): 0 -> true, 8 diff --git a/test/libsolidity/semanticTests/viaYul/simple_assignment.sol b/test/libsolidity/semanticTests/viaYul/simple_assignment.sol index 4ed155322536..0d54426a9a6a 100644 --- a/test/libsolidity/semanticTests/viaYul/simple_assignment.sol +++ b/test/libsolidity/semanticTests/viaYul/simple_assignment.sol @@ -5,6 +5,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(uint256,uint256): 5, 6 -> 5, 6 diff --git a/test/libsolidity/semanticTests/viaYul/simple_inline_asm.sol b/test/libsolidity/semanticTests/viaYul/simple_inline_asm.sol index f0779639feec..a2aec51dfbcc 100644 --- a/test/libsolidity/semanticTests/viaYul/simple_inline_asm.sol +++ b/test/libsolidity/semanticTests/viaYul/simple_inline_asm.sol @@ -12,6 +12,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 6 diff --git a/test/libsolidity/semanticTests/viaYul/smoke_test.sol b/test/libsolidity/semanticTests/viaYul/smoke_test.sol index d5c02573471a..7acfd831a643 100644 --- a/test/libsolidity/semanticTests/viaYul/smoke_test.sol +++ b/test/libsolidity/semanticTests/viaYul/smoke_test.sol @@ -1,7 +1,8 @@ contract C { } // ==== +// compileViaYul: also +// compileToEwasm: also // allowNonExistingFunctions: true -// compileViaYul: true // ---- // f() -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol index d79bceb55d0c..bf15d5f4d39d 100644 --- a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes.sol @@ -4,7 +4,7 @@ contract C { assembly { sstore(b.slot, or("deadbeef", 0x08)) } - byte s = b[3]; + bytes1 s = b[3]; uint r; assembly { r := s @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol index 6770df0f3c85..31f77407d5b4 100644 --- a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_bytes_long.sol @@ -6,7 +6,7 @@ contract C { mstore(0, b.slot) sstore(keccak256(0, 0x20), "deadbeefdeadbeefdeadbeefdeadbeef") } - byte s = b[31]; + bytes1 s = b[31]; uint r; assembly { r := s diff --git a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol index e3bec359dd47..a587ef2e69c3 100644 --- a/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol +++ b/test/libsolidity/semanticTests/viaYul/storage/dirty_storage_static_array.sol @@ -14,5 +14,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> true diff --git a/test/libsolidity/semanticTests/viaYul/storage/mappings.sol b/test/libsolidity/semanticTests/viaYul/storage/mappings.sol index 4713a42d3828..5666635b528e 100644 --- a/test/libsolidity/semanticTests/viaYul/storage/mappings.sol +++ b/test/libsolidity/semanticTests/viaYul/storage/mappings.sol @@ -6,8 +6,8 @@ contract C { function test_simple(uint _off) public returns (uint _a, uint _b, uint _c) { simple[_off + 2] = 3; simple[_off + 3] = 4; - simple[uint(-1)] = 5; - _c = simple[uint(-1)]; + simple[type(uint256).max] = 5; + _c = simple[type(uint256).max]; _b = simple[3 + _off]; _a = simple[2 + _off]; } @@ -29,7 +29,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also // ---- // test_simple(uint256): 0 -> 3, 4, 5 // test_simple(uint256): 1 -> 3, 4, 5 diff --git a/test/libsolidity/semanticTests/viaYul/storage/packed_storage.sol b/test/libsolidity/semanticTests/viaYul/storage/packed_storage.sol index 5ee7b6f4d8c5..51ff343d83d3 100644 --- a/test/libsolidity/semanticTests/viaYul/storage/packed_storage.sol +++ b/test/libsolidity/semanticTests/viaYul/storage/packed_storage.sol @@ -1,16 +1,17 @@ contract C { uint16 x; - byte y; + bytes1 y; uint16 z; function f(uint8 a) public returns (uint _x) { x = a; - y = byte(uint8(x) + 1); + y = bytes1(uint8(x) + 1); z = uint8(y) + 1; x = z + 1; _x = x; } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f(uint8): 6 -> 9 diff --git a/test/libsolidity/semanticTests/viaYul/storage/simple_storage.sol b/test/libsolidity/semanticTests/viaYul/storage/simple_storage.sol index 51bf2efcb5d2..6c51435340c0 100644 --- a/test/libsolidity/semanticTests/viaYul/storage/simple_storage.sol +++ b/test/libsolidity/semanticTests/viaYul/storage/simple_storage.sol @@ -11,7 +11,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // setX(uint256): 6 -> 6 // setY(uint256): 2 -> 2 diff --git a/test/libsolidity/semanticTests/viaYul/string_format.sol b/test/libsolidity/semanticTests/viaYul/string_format.sol index 98e4d1081949..669de6e9ad65 100644 --- a/test/libsolidity/semanticTests/viaYul/string_format.sol +++ b/test/libsolidity/semanticTests/viaYul/string_format.sol @@ -5,7 +5,8 @@ contract C { function h() external pure returns (bytes4) { return 0xcafecafe; } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f1() -> 0x20, 6, left(0x616263616263) // f2() -> 32, 47, 44048183223289766195424279195050628400112610419087780792899004030957505095210, 18165586057823232067963737336409268114628061002662705707816940456850361417728 diff --git a/test/libsolidity/semanticTests/viaYul/string_literals.sol b/test/libsolidity/semanticTests/viaYul/string_literals.sol index 7245b7e6ec7f..57682e20ac18 100644 --- a/test/libsolidity/semanticTests/viaYul/string_literals.sol +++ b/test/libsolidity/semanticTests/viaYul/string_literals.sol @@ -19,7 +19,8 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // short_dyn() -> 0x20, 3, "abc" // long_dyn() -> 0x20, 80, "12345678901234567890123456789012", "34567890123456789012345678901234", "5678901234567890" diff --git a/test/libsolidity/semanticTests/viaYul/struct_member_access.sol b/test/libsolidity/semanticTests/viaYul/struct_member_access.sol index 957226195fb3..4dc22ef40c0d 100644 --- a/test/libsolidity/semanticTests/viaYul/struct_member_access.sol +++ b/test/libsolidity/semanticTests/viaYul/struct_member_access.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { diff --git a/test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol b/test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol index 304c8aa3b724..c4a12481ea4d 100644 --- a/test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol +++ b/test/libsolidity/semanticTests/viaYul/tuple_evaluation_order.sol @@ -10,5 +10,6 @@ contract C { } // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3, 1 diff --git a/test/libsolidity/semanticTests/viaYul/unary_fixedbytes.sol b/test/libsolidity/semanticTests/viaYul/unary_fixedbytes.sol new file mode 100644 index 000000000000..d24bc04b5004 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/unary_fixedbytes.sol @@ -0,0 +1,78 @@ +contract C { + function conv(bytes25 a) public pure returns (bytes32) { + // truncating and widening + return ~bytes32(bytes16(~a)); + } + + function upcast(bytes25 a) public pure returns (bytes32) { + // implicit widening is allowed + return ~a; + } + + function downcast(bytes25 a) public pure returns (bytes12) { + // truncating cast must be explicit + return bytes12(~a); + } + + function r_b32() public pure returns (bytes32) { + return ~bytes32(hex"ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"); + } + function r_b25() public pure returns (bytes25) { + return ~bytes25(hex"ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff"); + } + function r_b16() public pure returns (bytes16) { + return ~bytes16(hex"ff00ff00ff00ff00ff00ff00ff00ff00"); + } + function r_b8() public pure returns (bytes8) { + return ~bytes8(hex"ff00ff00ff00ff00"); + } + function r_b4() public pure returns (bytes4) { + return ~bytes4(hex"ff00ff00"); + } + function r_b1() public pure returns (bytes1) { + return ~bytes1(hex"55"); + } + + function a_b32() public pure returns (bytes32) { + bytes32 r = ~bytes32(hex"ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"); + return r; + } + function a_b25() public pure returns (bytes25) { + bytes25 r = ~bytes25(hex"ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff"); + return r; + } + function a_b16() public pure returns (bytes16) { + bytes16 r = ~bytes16(hex"ff00ff00ff00ff00ff00ff00ff00ff00"); + return r; + } + function a_b8() public pure returns (bytes8) { + bytes8 r = ~bytes8(hex"ff00ff00ff00ff00"); + return r; + } + function a_b4() public pure returns (bytes4) { + bytes4 r = ~bytes4(hex"ff00ff00"); + return r; + } + function a_b1() public pure returns (bytes1) { + bytes1 r = ~bytes1(hex"55"); + return r; + } +} +// ==== +// compileViaYul: also +// ---- +// conv(bytes25): left(0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff) -> 0xff00ff00ff00ff00ff00ff00ff00ff00ffffffffffffffffffffffffffffffff +// upcast(bytes25): left(0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff) -> 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0000000000000000 +// downcast(bytes25): left(0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff) -> 0xff00ff00ff00ff00ff00ff0000000000000000000000000000000000000000 +// r_b32() -> 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff +// r_b25() -> 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0000000000000000 +// r_b16() -> 0xff00ff00ff00ff00ff00ff00ff00ff00000000000000000000000000000000 +// r_b8() -> 0xff00ff00ff00ff000000000000000000000000000000000000000000000000 +// r_b4() -> 0xff00ff00000000000000000000000000000000000000000000000000000000 +// r_b1() -> 0xaa00000000000000000000000000000000000000000000000000000000000000 +// a_b32() -> 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff +// a_b25() -> 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0000000000000000 +// a_b16() -> 0xff00ff00ff00ff00ff00ff00ff00ff00000000000000000000000000000000 +// a_b8() -> 0xff00ff00ff00ff000000000000000000000000000000000000000000000000 +// a_b4() -> 0xff00ff00000000000000000000000000000000000000000000000000000000 +// a_b1() -> 0xaa00000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/viaYul/unary_operations.sol b/test/libsolidity/semanticTests/viaYul/unary_operations.sol index 17f7dba4f09a..17d8dad03472 100644 --- a/test/libsolidity/semanticTests/viaYul/unary_operations.sol +++ b/test/libsolidity/semanticTests/viaYul/unary_operations.sol @@ -87,24 +87,24 @@ contract C { // ---- // preincr_s8(int8): 128 -> FAILURE // postincr_s8(int8): 128 -> FAILURE -// preincr_s8(int8): 127 -> FAILURE -// postincr_s8(int8): 127 -> FAILURE +// preincr_s8(int8): 127 -> FAILURE, hex"4e487b71", 0x11 +// postincr_s8(int8): 127 -> FAILURE, hex"4e487b71", 0x11 // preincr_s8(int8): 126 -> 127, 127 // postincr_s8(int8): 126 -> 126, 127 -// predecr_s8(int8): -128 -> FAILURE -// postdecr_s8(int8): -128 -> FAILURE +// predecr_s8(int8): -128 -> FAILURE, hex"4e487b71", 0x11 +// postdecr_s8(int8): -128 -> FAILURE, hex"4e487b71", 0x11 // predecr_s8(int8): -127 -> -128, -128 // postdecr_s8(int8): -127 -> -127, -128 // preincr_s8(int8): -5 -> -4, -4 // postincr_s8(int8): -5 -> -5, -4 // predecr_s8(int8): -5 -> -6, -6 // postdecr_s8(int8): -5 -> -5, -6 -// preincr_u8(uint8): 255 -> FAILURE -// postincr_u8(uint8): 255 -> FAILURE -// preincr_u8(uint8): 254 -> FAILURE -// postincr_u8(uint8): 254 -> FAILURE -// predecr_u8(uint8): 0 -> FAILURE -// postdecr_u8(uint8): 0 -> FAILURE +// preincr_u8(uint8): 255 -> FAILURE, hex"4e487b71", 0x11 +// postincr_u8(uint8): 255 -> FAILURE, hex"4e487b71", 0x11 +// preincr_u8(uint8): 254 -> FAILURE, hex"4e487b71", 0x11 +// postincr_u8(uint8): 254 -> FAILURE, hex"4e487b71", 0x11 +// predecr_u8(uint8): 0 -> FAILURE, hex"4e487b71", 0x11 +// postdecr_u8(uint8): 0 -> FAILURE, hex"4e487b71", 0x11 // predecr_u8(uint8): 1 -> 0 // postdecr_u8(uint8): 1 -> 1 // preincr_u8(uint8): 2 -> 6 @@ -123,14 +123,14 @@ contract C { // bitnot(int256): -100 -> 99 // bitnot_u8(uint8): 100 -> 155 // bitnot_s8() -> 99 -// negate(int256): -57896044618658097711785492504343953926634992332820282019728792003956564819968 -> FAILURE +// negate(int256): -57896044618658097711785492504343953926634992332820282019728792003956564819968 -> FAILURE, hex"4e487b71", 0x11 // negate(int256): -57896044618658097711785492504343953926634992332820282019728792003956564819967 -> 57896044618658097711785492504343953926634992332820282019728792003956564819967 // negate(int256): 0 -> 0 // negate(int256): 1 -> -1 // negate(int256): -1 -> 1 -// negate_s8(int8): -128 -> FAILURE +// negate_s8(int8): -128 -> FAILURE, hex"4e487b71", 0x11 // negate_s8(int8): -138 -> FAILURE // negate_s8(int8): -127 -> 127 // negate_s8(int8): 127 -> -127 -// negate_s16(int16): -32768 -> FAILURE +// negate_s16(int16): -32768 -> FAILURE, hex"4e487b71", 0x11 // negate_s16(int16): -32767 -> 32767 diff --git a/test/libsolidity/semanticTests/viaYul/various_inline_asm.sol b/test/libsolidity/semanticTests/viaYul/various_inline_asm.sol index ebdbb7522863..f3bcf56fceb5 100644 --- a/test/libsolidity/semanticTests/viaYul/various_inline_asm.sol +++ b/test/libsolidity/semanticTests/viaYul/various_inline_asm.sol @@ -18,6 +18,7 @@ contract C { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 70 diff --git a/test/libsolidity/semanticTests/viaYul/virtual_functions.sol b/test/libsolidity/semanticTests/viaYul/virtual_functions.sol index c69e4b73e488..45d4ccf46f6c 100644 --- a/test/libsolidity/semanticTests/viaYul/virtual_functions.sol +++ b/test/libsolidity/semanticTests/viaYul/virtual_functions.sol @@ -24,7 +24,8 @@ contract C is X { } } // ==== -// compileViaYul: true +// compileViaYul: also +// compileToEwasm: also // ---- // f() -> 3 // f1() -> 3 diff --git a/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls.sol b/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls.sol index b06a89c4663c..bc57e6feb126 100644 --- a/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls.sol +++ b/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls.sol @@ -17,5 +17,6 @@ contract Derived is Base { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // f() -> 2 diff --git a/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls_through_dispatch.sol b/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls_through_dispatch.sol index f3bc0845f99d..e1b415c74c72 100644 --- a/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls_through_dispatch.sol +++ b/test/libsolidity/semanticTests/virtualFunctions/internal_virtual_function_calls_through_dispatch.sol @@ -22,5 +22,6 @@ contract Derived is Base { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // h() -> 2 diff --git a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol index 33d151f82acc..41a0d608a570 100644 --- a/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol +++ b/test/libsolidity/semanticTests/virtualFunctions/virtual_function_calls.sol @@ -17,6 +17,7 @@ contract Derived is Base { // ==== // compileViaYul: also +// compileToEwasm: also // ---- // g() -> 2 // f() -> 2 diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol new file mode 100644 index 000000000000..0d5c23650caa --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.push(); + } +} +// ---- +// Warning 2529: (82-89): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol new file mode 100644 index 000000000000..e6d298861087 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.length; + } +} +// ---- +// Warning 2529: (82-89): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol new file mode 100644 index 000000000000..a1b9717d79fb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.pop(); + } +} +// ---- +// Warning 2529: (82-89): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() +// Warning 2529: (93-100): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol new file mode 100644 index 000000000000..d05081d5d424 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_4.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.length; + a.pop(); + } +} +// ---- +// Warning 2529: (94-101): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol new file mode 100644 index 000000000000..37c21e6201a4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_5.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function g() internal { + a.push(); + } + function f() public { + a.pop(); + g(); + } +} +// ---- +// Warning 2529: (122-129): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol new file mode 100644 index 000000000000..ccc5523d8fee --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_6.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function g() internal view { + a.length; + } + function f() public { + a.pop(); + g(); + } +} +// ---- +// Warning 2529: (127-134): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol new file mode 100644 index 000000000000..34663d931f7e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_7.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.push(); + a.pop(); + } +} diff --git a/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol new file mode 100644 index 000000000000..bc44acc10de6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/array_pop_length_8.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function f() public { + a.pop(); + a.push(); + a.push(); + a.push(); + a.pop(); + a.pop(); + a.pop(); + } +} +// ---- +// Warning 2529: (82-89): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_memory_to_memory.sol b/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_memory_to_memory.sol index 2e3ef86fdd45..bc1f7021bb72 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_memory_to_memory.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_memory_to_memory.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint[][] memory arr) public pure { diff --git a/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_storage_to_storage.sol b/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_storage_to_storage.sol index acf6a1e1d70a..6c4516ec7b6a 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_storage_to_storage.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_1d_assignment_2d_storage_to_storage.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { uint[][] arr; diff --git a/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_memory_to_storage.sol b/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_memory_to_storage.sol index ce0d9ef5dc0f..90c873d10a21 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_memory_to_storage.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_memory_to_storage.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { uint[][] arr; diff --git a/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_storage_to_memory.sol b/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_storage_to_memory.sol index 4aa03c222b55..355a9187e1ce 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_storage_to_memory.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_1d_copy_2d_storage_to_memory.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { uint[][] arr; diff --git a/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_1.sol b/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_1.sol index 3312aede366e..8857b91dbbd3 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_1.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_1.sol @@ -11,10 +11,3 @@ contract C { } } // ---- -// Warning 6328: (119-157): Assertion violation happens here -// Warning 8115: (76-80): Assertion checker does not yet support the type of this variable. -// Warning 8115: (83-87): Assertion checker does not yet support the type of this variable. -// Warning 7650: (126-132): Assertion checker does not yet support this expression. -// Warning 8364: (126-128): Assertion checker does not yet implement type struct C.S storage ref -// Warning 7650: (143-149): Assertion checker does not yet support this expression. -// Warning 8364: (143-145): Assertion checker does not yet implement type struct C.S storage ref diff --git a/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol b/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol index c07ab4aadd67..67852d556883 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_1d_struct_array_2d_1.sol @@ -11,12 +11,3 @@ contract C { } } // ---- -// Warning 6328: (121-165): Assertion violation happens here -// Warning 8115: (78-82): Assertion checker does not yet support the type of this variable. -// Warning 8115: (85-89): Assertion checker does not yet support the type of this variable. -// Warning 7650: (128-134): Assertion checker does not yet support this expression. -// Warning 8364: (128-130): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9118: (128-137): Assertion checker does not yet implement this expression. -// Warning 7650: (148-154): Assertion checker does not yet support this expression. -// Warning 8364: (148-150): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9118: (148-157): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/array_members/length_assignment_2d_memory_to_memory.sol b/test/libsolidity/smtCheckerTests/array_members/length_assignment_2d_memory_to_memory.sol index d85f011043e6..cfac3d721b0c 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_assignment_2d_memory_to_memory.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_assignment_2d_memory_to_memory.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint[][] memory arr) public pure { diff --git a/test/libsolidity/smtCheckerTests/array_members/length_basic.sol b/test/libsolidity/smtCheckerTests/array_members/length_basic.sol index b5f4a07d3701..99f01621d292 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_basic.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_basic.sol @@ -10,4 +10,4 @@ contract C { } } // ---- -// Warning 6328: (153-176): Assertion violation happens here +// Warning 6328: (153-176): CHC: Assertion violation happens here.\nCounterexample:\narr = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_2_fail.sol b/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_2_fail.sol index 741ecff3d062..1c32c48afca8 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_2_fail.sol @@ -14,6 +14,6 @@ contract C { } } // ---- -// Warning 6328: (198-224): Assertion violation happens here -// Warning 6328: (228-254): Assertion violation happens here -// Warning 6328: (258-281): Assertion violation happens here +// Warning 6328: (198-224): CHC: Assertion violation happens here.\nCounterexample:\narr = [], arr2 = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = [], arr2 = []\nf() +// Warning 6328: (228-254): CHC: Assertion violation happens here.\nCounterexample:\narr = [], arr2 = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = [], arr2 = []\nf() +// Warning 6328: (258-281): CHC: Assertion violation happens here.\nCounterexample:\narr = [], arr2 = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = [], arr2 = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol b/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol index eff6b2fbd941..dbab6795fe04 100644 --- a/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol +++ b/test/libsolidity/smtCheckerTests/array_members/length_same_after_assignment_3_fail.sol @@ -16,7 +16,7 @@ contract C { } } // ---- -// Warning 6328: (222-248): Assertion violation happens here -// Warning 6328: (252-278): Assertion violation happens here -// Warning 6328: (282-305): Assertion violation happens here -// Warning 6328: (309-335): Assertion violation happens here +// Warning 6328: (222-248): CHC: Assertion violation happens here.\nCounterexample:\narr = [], arr2 = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = [], arr2 = []\nf() +// Warning 6328: (252-278): CHC: Assertion violation happens here.\nCounterexample:\narr = [], arr2 = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = [], arr2 = []\nf() +// Warning 6328: (282-305): CHC: Assertion violation happens here.\nCounterexample:\narr = [], arr2 = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = [], arr2 = []\nf() +// Warning 6328: (309-335): CHC: Assertion violation happens here.\nCounterexample:\narr = [], arr2 = []\n\n\n\nTransaction trace:\nconstructor()\nState: arr = [], arr2 = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/pop_1_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/pop_1_unsafe.sol index 75e51df74d59..4da5ef8e8afe 100644 --- a/test/libsolidity/smtCheckerTests/array_members/pop_1_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/pop_1_unsafe.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// Warning 2529: (82-89): Empty array "pop" detected here +// Warning 2529: (82-89): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol index 5d4bd3a97b8c..160da0d09b20 100644 --- a/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/pop_2d_unsafe.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 2529: (111-121): Empty array "pop" detected here +// Warning 2529: (111-121): CHC: Empty array "pop" happens here.\nCounterexample:\na = [[0]]\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/pop_constructor_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/pop_constructor_unsafe.sol index bb9d37c0fb8f..bb8f14f0c99b 100644 --- a/test/libsolidity/smtCheckerTests/array_members/pop_constructor_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/pop_constructor_unsafe.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// Warning 2529: (76-83): Empty array "pop" detected here +// Warning 2529: (76-83): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/array_members/pop_loop_safe.sol b/test/libsolidity/smtCheckerTests/array_members/pop_loop_safe.sol index 25986da7b017..4626a7d08550 100644 --- a/test/libsolidity/smtCheckerTests/array_members/pop_loop_safe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/pop_loop_safe.sol @@ -9,3 +9,5 @@ contract C { } } } +// ---- +// Warning 4984: (112-115): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/array_members/pop_loop_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/pop_loop_unsafe.sol index ed1f8fc659db..deac0b2c99ae 100644 --- a/test/libsolidity/smtCheckerTests/array_members/pop_loop_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/pop_loop_unsafe.sol @@ -11,4 +11,5 @@ contract C { } } // ---- -// Warning 2529: (150-157): Empty array "pop" detected here +// Warning 4984: (112-115): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 2529: (150-157): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\nl = 0\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf(0) diff --git a/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol index c409a0e098b8..8e54d1166d7b 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_2d_arg_1_unsafe.sol @@ -9,6 +9,8 @@ contract C { assert(a[0][a[0].length - 1] == y); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (150-184): Assertion violation happens here -// Warning 4144: (162-177): Underflow (resulting value less than 0) happens here +// Warning 3944: (162-177): CHC: Underflow (resulting value less than 0) happens here. +// Warning 6328: (150-184): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_1d.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_1d.sol new file mode 100644 index 000000000000..752a83d55b7a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_1d.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + uint[] b; + + function f() public { + require(b.length == 0); + b.push() = 1; + assert(b[0] == 1); + } + + function g() public { + b.push() = 1; + assert(b[b.length - 1] == 1); + // Fails + assert(b[b.length - 1] == 100); + } + +} +// ---- +// Warning 6328: (232-262): CHC: Assertion violation happens here.\nCounterexample:\nb = [1]\n\n\n\nTransaction trace:\nconstructor()\nState: b = []\ng() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d.sol new file mode 100644 index 000000000000..69ec4f51611f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract C { + uint[][] c; + + function f() public { + require(c.length == 0); + c.push().push() = 2; + assert(c.length == 1); + assert(c[0].length == 1); + assert(c[0][0] == 2); + } + + function g() public { + c.push().push() = 2; + assert(c.length > 0); + assert(c[c.length - 1].length == 1); + assert(c[c.length - 1][c[c.length - 1].length - 1] == 2); + // Fails + assert(c[c.length - 1][c[c.length - 1].length - 1] == 200); + } +} +// ---- +// Warning 6328: (395-453): CHC: Assertion violation happens here.\nCounterexample:\nc = [[2]]\n\n\n\nTransaction trace:\nconstructor()\nState: c = []\ng() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d_2.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d_2.sol new file mode 100644 index 000000000000..90222a591fbc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_2d_2.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + int[][] array2d; + function s() public returns (int[] memory) { + array2d.push() = array2d.push(); + assert(array2d[array2d.length - 1].length == array2d[array2d.length - 2].length); + return array2d[2]; + } +} diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_3d.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_3d.sol new file mode 100644 index 000000000000..fa8965eabb85 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_3d.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C { + uint[][][] c; + + function f() public { + require(c.length == 0); + c.push().push().push() = 2; + assert(c.length == 1); + assert(c[0].length == 1); + assert(c[0][0].length == 1); + assert(c[0][0][0] == 2); + } + + function g() public { + c.push().push().push() = 2; + uint length1 = c.length; + uint length2 = c[length1 - 1].length; + uint length3 = c[length1 - 1][length2 - 1].length; + assert(length1 > 0); + assert(length2 == 1); + assert(length3 == 1); + assert(c[length1 - 1][length2 - 1][length3 - 1] == 2); + // Fails + assert(c[length1 - 1][length2 - 1][length3 - 1] == 200); + } +} +// ---- +// Warning 6328: (570-625): CHC: Assertion violation might happen here. +// Warning 4661: (570-625): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_1d.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_1d.sol new file mode 100644 index 000000000000..9ef18c2acfeb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_1d.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint[] b; + function f() public { + b.push() = b.push(); + uint length = b.length; + assert(length >= 2); + assert(b[length - 1] == 0); + assert(b[length - 1] == b[length - 2]); + // Fails + assert(b[length - 1] == 1); + } +} +// ---- +// Warning 6328: (237-263): CHC: Assertion violation happens here.\nCounterexample:\nb = [0, 0]\n\n\n\nTransaction trace:\nconstructor()\nState: b = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_2d_1.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_2d_1.sol new file mode 100644 index 000000000000..1596cfb0aa01 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_2d_1.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + uint[][] b; + function f() public { + require(b.length == 0); + b.push().push() = b.push().push(); + assert(b.length == 2); + assert(b[0].length == 1); + assert(b[0].length == 1); + assert(b[0][0] == 0); + assert(b[1][0] == 0); + assert(b[0][0] == b[1][0]); + // Fails + assert(b[0][0] != b[1][0]); + } +} +// ---- +// Warning 6328: (317-343): CHC: Assertion violation happens here.\nCounterexample:\nb = [[0], [0]]\n\n\n\nTransaction trace:\nconstructor()\nState: b = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_2d_2.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_2d_2.sol new file mode 100644 index 000000000000..69b62803a557 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_2d_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + uint[][] b; + function f() public { + b.push().push() = b.push().push(); + uint length = b.length; + assert(length >= 2); + uint length1 = b[length - 1].length; + uint length2 = b[length - 2].length; + assert(length1 == 1); + assert(length2 == 1); + assert(b[length - 1][length1 - 1] == 0); + } +} diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_bytes.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_bytes.sol new file mode 100644 index 000000000000..cbe09d4f0dce --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_and_rhs_bytes.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + bytes b; + function f() public { + b.push() = b.push(); + uint length = b.length; + assert(length >= 2); + assert(b[length - 1] == 0); + assert(b[length - 1] == b[length - 2]); + // Fails + assert(b[length - 1] == bytes1(uint8(1))); + } +} +// ---- +// Warning 6328: (236-277): CHC: Assertion violation happens here.\nCounterexample:\nb = [0, 0]\n\n\n\nTransaction trace:\nconstructor()\nState: b = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_bytes.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_bytes.sol new file mode 100644 index 000000000000..7b403f18e7f6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_bytes.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract C { + bytes b; + + function f() public { + require(b.length == 0); + b.push() = bytes1(uint8(1)); + assert(b[0] == bytes1(uint8(1))); + } + + function g() public { + bytes1 one = bytes1(uint8(1)); + b.push() = one; + assert(b[b.length - 1] == one); + // Fails + assert(b[b.length - 1] == bytes1(uint8(100))); + } + +} +// ---- +// Warning 6328: (298-343): CHC: Assertion violation happens here.\nCounterexample:\nb = [1]\n\n\n\nTransaction trace:\nconstructor()\nState: b = []\ng() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_bytes_2d.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_bytes_2d.sol new file mode 100644 index 000000000000..22bc28401f9f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_bytes_2d.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract C { + bytes[] c; + + function f() public { + bytes1 val = bytes1(uint8(2)); + require(c.length == 0); + c.push().push() = val; + assert(c.length == 1); + assert(c[0].length == 1); + assert(c[0][0] == val); + } + + function g() public { + bytes1 val = bytes1(uint8(2)); + c.push().push() = val; + assert(c.length > 0); + assert(c[c.length - 1].length == 1); + assert(c[c.length - 1][c[c.length - 1].length - 1] == val); + // Fails + assert(c[c.length - 1][c[c.length - 1].length - 1] == bytes1(uint8(100))); + } +} +// ---- +// Warning 6328: (468-541): CHC: Assertion violation happens here.\nCounterexample:\nc = [[2]]\n\n\n\nTransaction trace:\nconstructor()\nState: c = []\ng() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_struct.sol b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_struct.sol new file mode 100644 index 000000000000..87773a7ea8f1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/array_members/push_as_lhs_struct.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + struct S { + int[] b; + } + S s; + struct T { + S s; + } + T t; + function f() public { + s.b.push() = t.s.b.push(); + assert(s.b[s.b.length -1] == t.s.b[t.s.b.length - 1]); + } +} diff --git a/test/libsolidity/smtCheckerTests/array_members/push_overflow_2_safe_no_overflow_assumption.sol b/test/libsolidity/smtCheckerTests/array_members/push_overflow_2_safe_no_overflow_assumption.sol index 1f468f5923ff..21c96a7bde30 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_overflow_2_safe_no_overflow_assumption.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_overflow_2_safe_no_overflow_assumption.sol @@ -11,3 +11,5 @@ contract C { assert(x[0] == 42); } } +// ---- +// Warning 4984: (174-177): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_1_fail.sol b/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_1_fail.sol index 7827950d1d45..a57ec703125f 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_1_fail.sol @@ -8,5 +8,5 @@ contract C { } } // ---- -// Warning 6328: (113-139): Assertion violation happens here -// Warning 6328: (143-189): Assertion violation happens here +// Warning 6328: (113-139): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[0]]\n\n\n\nTransaction trace:\nconstructor()\nState: array2d = []\nl() +// Warning 6328: (143-189): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[0]]\n\n\n\nTransaction trace:\nconstructor()\nState: array2d = []\nl() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_2_fail.sol b/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_2_fail.sol index cfcbbfa68594..f15b25e0f298 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_push_no_args_2_fail.sol @@ -10,6 +10,6 @@ contract C { } } // ---- -// Warning 6328: (122-148): Assertion violation happens here -// Warning 6328: (202-218): Assertion violation happens here -// Warning 6328: (222-278): Assertion violation happens here +// Warning 6328: (122-148): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[[0]]]\n\n\n\nTransaction trace:\nconstructor()\nState: array2d = []\nl() +// Warning 6328: (202-218): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[[0]]]\n\n\n\nTransaction trace:\nconstructor()\nState: array2d = []\nl() +// Warning 6328: (222-278): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[[0]]]\n\n\n\nTransaction trace:\nconstructor()\nState: array2d = []\nl() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol index 8192f673d05e..3e984a624823 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_safe_aliasing.sol @@ -11,6 +11,8 @@ contract C { assert(a[0][a[0].length - 1] == 8); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (205-239): Assertion violation happens here -// Warning 4144: (217-232): Underflow (resulting value less than 0) happens here +// Warning 3944: (217-232): CHC: Underflow (resulting value less than 0) happens here. +// Warning 6328: (205-239): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol index 89d89c108b99..0b2955154aaa 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_aliasing.sol @@ -12,4 +12,4 @@ contract C { } } // ---- -// Warning 6328: (167-188): Assertion violation happens here +// Warning 6328: (167-188): CHC: Assertion violation happens here.\nCounterexample:\na = [[17, 12, 12, 12, 12], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15]]\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_length.sol b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_length.sol index 61bdb3d5a15f..5fff9d312d84 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_length.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_storage_ref_unsafe_length.sol @@ -14,10 +14,10 @@ contract C { // Safe but knowledge about `c` is erased because `b` could be pointing to `c[x][y]`. assert(c[0][0][0] == 12); // Safe but knowledge about `d` is erased because `b` could be pointing to `d`. - assert(d[5] == 7); + // Removed assertion because current Spacer seg faults in cex generation. + //assert(d[5] == 7); } } // ---- -// Warning 6328: (193-217): Assertion violation happens here -// Warning 6328: (309-333): Assertion violation happens here -// Warning 6328: (419-436): Assertion violation happens here +// Warning 6328: (193-217): CHC: Assertion violation happens here.\nCounterexample:\na = [[12, 12, 12, 12, 12, 12, 12, 6, 12, 8, 12, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15], [15, 15]], c = [], d = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = [], c = [], d = []\nf() +// Warning 6328: (309-333): CHC: Assertion violation happens here.\nCounterexample:\na = [[24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 23, 24, 24, 24, 24, 24, 24, 16, 24, 24, 19, 24, 21, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27], [27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27]], c = [[[3, 15, 15, 15, 15, 15], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36], [36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]], [[42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]]], d = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = [], c = [], d = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_struct_member_1.sol b/test/libsolidity/smtCheckerTests/array_members/push_struct_member_1.sol index f2c30c84dc0f..b2e500c74aeb 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_struct_member_1.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_struct_member_1.sol @@ -15,13 +15,3 @@ contract C { } // ---- -// Warning 8115: (72-75): Assertion checker does not yet support the type of this variable. -// Warning 8115: (100-103): Assertion checker does not yet support the type of this variable. -// Warning 7650: (130-133): Assertion checker does not yet support this expression. -// Warning 8364: (130-131): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9599: (130-133): Assertion checker does not yet implement this expression. -// Warning 7650: (144-149): Assertion checker does not yet support this expression. -// Warning 8364: (144-147): Assertion checker does not yet implement type struct C.S storage ref -// Warning 7650: (144-147): Assertion checker does not yet support this expression. -// Warning 8364: (144-145): Assertion checker does not yet implement type struct C.T storage ref -// Warning 9599: (144-149): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_struct_member_2.sol b/test/libsolidity/smtCheckerTests/array_members/push_struct_member_2.sol index a717c8d4b238..ba9a847139c9 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_struct_member_2.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_struct_member_2.sol @@ -16,18 +16,3 @@ contract C { } // ---- -// Warning 8115: (72-75): Assertion checker does not yet support the type of this variable. -// Warning 8115: (102-105): Assertion checker does not yet support the type of this variable. -// Warning 7650: (132-135): Assertion checker does not yet support this expression. -// Warning 8364: (132-133): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9599: (132-135): Assertion checker does not yet implement this expression. -// Warning 7650: (146-149): Assertion checker does not yet support this expression. -// Warning 8364: (146-147): Assertion checker does not yet implement type struct C.T storage ref -// Warning 8364: (146-156): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9599: (146-149): Assertion checker does not yet implement this expression. -// Warning 7650: (160-168): Assertion checker does not yet support this expression. -// Warning 7650: (160-163): Assertion checker does not yet support this expression. -// Warning 8364: (160-161): Assertion checker does not yet implement type struct C.T storage ref -// Warning 8364: (160-166): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9118: (160-166): Assertion checker does not yet implement this expression. -// Warning 9599: (160-168): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/array_members/push_zero_2d_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/push_zero_2d_unsafe.sol index ac278a22a287..6dd0530280bf 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_zero_2d_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_zero_2d_unsafe.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (111-144): Assertion violation happens here +// Warning 6328: (111-144): CHC: Assertion violation happens here.\nCounterexample:\na = [[0]]\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/push_zero_unsafe.sol b/test/libsolidity/smtCheckerTests/array_members/push_zero_unsafe.sol index 504d5a95c4f1..d99bc7ce20b1 100644 --- a/test/libsolidity/smtCheckerTests/array_members/push_zero_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/array_members/push_zero_unsafe.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// Warning 6328: (94-124): Assertion violation happens here +// Warning 6328: (94-124): CHC: Assertion violation happens here.\nCounterexample:\na = [0]\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/array_members/storage_pointer_push_1.sol b/test/libsolidity/smtCheckerTests/array_members/storage_pointer_push_1.sol index 721c4a8ba66b..43390ad3bccf 100644 --- a/test/libsolidity/smtCheckerTests/array_members/storage_pointer_push_1.sol +++ b/test/libsolidity/smtCheckerTests/array_members/storage_pointer_push_1.sol @@ -15,4 +15,4 @@ contract C { } } // ---- -// Warning 6328: (184-213): Assertion violation happens here +// Warning 6328: (184-213): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[], [], []]\n\n\n\nTransaction trace:\nconstructor()\nState: array2d = []\nl() diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/decreasing_balance.sol b/test/libsolidity/smtCheckerTests/blockchain_state/decreasing_balance.sol new file mode 100644 index 000000000000..45bb781ba5ff --- /dev/null +++ b/test/libsolidity/smtCheckerTests/blockchain_state/decreasing_balance.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract C { + uint t; + constructor() { + t = address(this).balance; + } + function f(address payable a, uint x) public { + require(address(this).balance >= x); + a.transfer(x); + } + function inv() public view { + // If only looking at `f`, it looks like this.balance always decreases. + // However, the edge case of a contract `selfdestruct` sending its remaining balance + // to this contract should make the claim false (since there's no fallback/receive here). + // Removed because current Spacer seg faults in cex generation. + //assert(address(this).balance == t); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change.sol b/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change.sol new file mode 100644 index 000000000000..816b9af32ef1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + address t; + constructor() { + t = address(this); + } + function inv() public view { + assert(address(this) == t); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change_external_call.sol b/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change_external_call.sol new file mode 100644 index 000000000000..c0adb1fe9b54 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change_external_call.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() external virtual; +} + +contract C { + address t; + constructor() { + t = address(this); + } + function f(D d) public { + address a = address(this); + d.d(); + assert(address(this) == t); + assert(a == t); + } +} diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change_internal_call.sol b/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change_internal_call.sol new file mode 100644 index 000000000000..70feedc3e5d8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/blockchain_state/this_does_not_change_internal_call.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + address t; + constructor() { + t = address(this); + } + function f() public view { + g(address(this)); + } + function g(address a) internal view { + assert(a == t); + assert(a == address(this)); + } +} diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/transfer.sol b/test/libsolidity/smtCheckerTests/blockchain_state/transfer.sol new file mode 100644 index 000000000000..3f8296d466cb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/blockchain_state/transfer.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f(address payable a) public { + require(address(this).balance > 1000); + a.transfer(666); + assert(address(this).balance > 100); + // Fails. + assert(address(this).balance > 500); + } +} +// ---- +// Warning 6328: (199-234): CHC: Assertion violation happens here.\nCounterexample:\n\na = 2437\n\n\nTransaction trace:\nconstructor()\nf(2437) diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/assert.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/assert.sol new file mode 100644 index 000000000000..9043689f53be --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/assert.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; +contract C { + function f(uint x) public pure { + assert(x > 0); + } + function g(uint x) public pure { + require(x >= 0); + } + function h(uint x) public pure { + require(x == 2); + require(x != 2); + } + function i(uint x) public pure { + if (false) { + if (x != 2) { + } + } + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (81-94): BMC: Assertion violation happens here. +// Warning 6838: (143-149): BMC: Condition is always true. +// Warning 6838: (218-224): BMC: Condition is always false. +// Warning 2512: (286-292): BMC: Condition unreachable. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/assert_in_constructor.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/assert_in_constructor.sol new file mode 100644 index 000000000000..2ceae7304568 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/assert_in_constructor.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + uint x = initX(); + + function initX() internal pure returns (uint) { + return 42; + } +} + +contract D is C { + uint y; + + constructor() { + assert(x == 42); + y = x; + } +} +// ==== +// SMTEngine: bmc +// ---- diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/branches_in_modifiers.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/branches_in_modifiers.sol new file mode 100644 index 000000000000..fb87df2b5d8c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/branches_in_modifiers.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + + modifier check() { + require(x == 0); + _; + assert(x == 1); // should fail; + assert(x == 0); // should hold; + } + + modifier inc() { + if (x == 0) { + return; + } + x = x + 1; + _; + } + + function test() check inc public { + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (103-117): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/branches_in_modifiers_2.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/branches_in_modifiers_2.sol new file mode 100644 index 000000000000..47e3a27ef327 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/branches_in_modifiers_2.sol @@ -0,0 +1,45 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + + function reset_if_overflow() internal postinc { + if (x < 10) + return; + x = 0; + } + + modifier postinc() { + if (x == 0) { + return; + } + _; + x = x + 1; + } + + function test() public { + if (x == 0) { + reset_if_overflow(); + assert(x == 1); // should fail; + assert(x == 0); // should hold; + return; + } + if (x < 10) { + uint oldx = x; + reset_if_overflow(); + assert(oldx + 1 == x); // should hold; + assert(oldx == x); // should fail; + return; + } + reset_if_overflow(); + assert(x == 1); // should hold; + assert(x == 0); // should fail; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (384-398): BMC: Assertion violation happens here. +// Warning 4661: (635-652): BMC: Assertion violation happens here. +// Warning 4661: (781-795): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init.sol new file mode 100644 index 000000000000..6d960112adf2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init.sol @@ -0,0 +1,43 @@ +pragma experimental SMTChecker; + +contract A { + int x; + constructor (int a) { x = a;} +} + +contract B is A { + int y; + constructor(int a) A(-a) { + if (a > 0) { + y = 2; + return; + } + else { + y = 3; + } + y = 4; // overwrites the else branch + } +} + +contract C is B { + constructor(int a) B(a) { + assert(y != 3); // should hold + assert(y == 4); // should fail + if (a > 0) { + assert(x < 0 && y == 2); // should hold + assert(x < 0 && y == 4); // should fail + } + else { + assert(x >= 0 && y == 4); // should hold + assert(x >= 0 && y == 2); // should fail + assert(x > 0); // should fail + } + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (330-344): BMC: Assertion violation happens here. +// Warning 4661: (422-445): BMC: Assertion violation happens here. +// Warning 4661: (522-546): BMC: Assertion violation happens here. +// Warning 4661: (566-579): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init_chain_alternate.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init_chain_alternate.sol new file mode 100644 index 000000000000..511cfbb60698 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init_chain_alternate.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract A { + uint x = 1; +} + +contract B is A { + constructor(int a) { + if (a > 0) { + x = 2; + return; + } + x = 3; + } +} + +abstract contract C is B { +} + +contract D is C { + constructor(int a) B(a) { + assert(a > 0 || x == 3); // should hold + assert(a <= 0 || x == 2); // should hold + assert(x == 1); // should fail + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (319-333): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init_diamond.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init_diamond.sol new file mode 100644 index 000000000000..c962d10037eb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructor_state_variable_init_diamond.sol @@ -0,0 +1,72 @@ +pragma experimental SMTChecker; + +contract A { + int x; +} + +contract B is A { + int y; + constructor (int a) { + if (a >= 0) { + y = 1; + return; + } + x = 1; + y = 2; + } +} + +contract C is A { + int z; + constructor (int a) { + if (a >= 0) { + z = 1; + return; + } + x = -1; + z = 2; + } +} + +contract D1 is B, C { + constructor() B(1) C(1) { + assert(x == 0); // should hold + assert(x == 1); // should fail + assert(x == -1); // should fail + } +} + +contract D2 is B, C { + constructor() B(1) C(-1) { + assert(x == 0); // should fail + assert(x == 1); // should fail + assert(x == -1); // should hold (constructor of C is executed AFTER constructor of B) + } +} + +contract D3 is B, C { + constructor() B(-1) C(1) { + assert(x == 0); // should fail + assert(x == 1); // should hold + assert(x == -1); // should fail + } +} + +contract D4 is B, C { + constructor() B(-1) C(-1) { + assert(x == 0); // should fail + assert(x == 1); // should fail + assert(x == -1); // should hold (constructor of C is executed AFTER constructor of B) + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (370-384): BMC: Assertion violation happens here. +// Warning 4661: (403-418): BMC: Assertion violation happens here. +// Warning 4661: (493-507): BMC: Assertion violation happens here. +// Warning 4661: (526-540): BMC: Assertion violation happens here. +// Warning 4661: (703-717): BMC: Assertion violation happens here. +// Warning 4661: (769-784): BMC: Assertion violation happens here. +// Warning 4661: (860-874): BMC: Assertion violation happens here. +// Warning 4661: (893-907): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructors.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructors.sol new file mode 100644 index 000000000000..9ed5ddcbfd55 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/constructors.sol @@ -0,0 +1,33 @@ +pragma experimental SMTChecker; + +contract B { + int x; + constructor(int b) { + if (b > 0) { + x = 1; + return; + } + else { + x = 2; + return; + } + x = 3; // dead code + } +} + +contract C is B { + constructor(int a) B(a) { + assert(a > 0 || x == 2); // should hold + assert(a <= 0 || x == 1); // should hold + assert(x == 3); // should fail + assert(x == 2); // should fail + assert(x == 1); // should fail + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 5740: (152-157): Unreachable code. +// Warning 4661: (310-324): BMC: Assertion violation happens here. +// Warning 4661: (343-357): BMC: Assertion violation happens here. +// Warning 4661: (376-390): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/nested_if.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/nested_if.sol new file mode 100644 index 000000000000..c941c6421a7f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/nested_if.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract C { + + function test(uint256 a, uint256 b) public pure { + assert(nested_if(a,b) != 42); // should hold + assert(nested_if(a,b) == 1); // should fail + } + + function nested_if(uint256 a, uint256 b) internal pure returns (uint256) { + if (a < 5) { + if (b > 1) { + return 0; + } + } + if (a == 2 && b == 2) { + return 42; // unreachable + } + else { + return 1; + } + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (147-174): BMC: Assertion violation happens here. +// Warning 6838: (332-348): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/return_in_both_branches.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/return_in_both_branches.sol new file mode 100644 index 000000000000..0ad554c31b4e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/return_in_both_branches.sol @@ -0,0 +1,23 @@ +pragma experimental SMTChecker; + +contract C { + + function test() public pure { + assert(branches(0) == 0); + assert(branches(1) == 42); + } + + function branches(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + else { + return 42; + } + return 1; // dead code + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 5740: (265-273): Unreachable code. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if.sol new file mode 100644 index 000000000000..783a28543078 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function test(uint256 a, uint256 b) public pure returns (uint256) { + if (a == 0) { + return 0; + } + return b / a; // This division is safe because of the early return in if-block. + } +} +// ==== +// SMTEngine: bmc +// ---- diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if2.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if2.sol new file mode 100644 index 000000000000..d614edb55ed9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if2.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + + function test(uint256 a) public pure { + assert(simple_if(a) == 1); // should fail for a == 0 + } + + function simple_if(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + return 1; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (89-114): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_array.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_array.sol new file mode 100644 index 000000000000..70cc1ed386c4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_array.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C { + + uint[] a; + + constructor () { + a.push(); + a.push(); + } + + function check() public { + require(a.length >= 2); + require(a[1] == 0); + conditional_store(); + assert(a[1] == 1); // should fail; + assert(a[1] == 0); // should hold; + } + + function conditional_store() internal { + if (a[1] == 0) { + return; + } + a[1] = 1; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (205-222): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_state_var.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_state_var.sol new file mode 100644 index 000000000000..5acc7fcbebb5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_state_var.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + + function check() public { + require(x == 0); + conditional_increment(); + assert(x == 1); // should fail; + assert(x == 0); // should hold; + } + + function conditional_increment() internal { + if (x == 0) { + return; + } + x = 1; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (132-146): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_struct.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_struct.sol new file mode 100644 index 000000000000..de3cfda1b3cd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_struct.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + S s; + + function check() public { + require(s.x == 0); + conditional_increment(); + assert(s.x == 1); // should fail; + assert(s.x == 0); // should hold; + } + + function conditional_increment() internal { + if (s.x == 0) { + return; + } + s.x = 1; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (156-172): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_struct_2.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_struct_2.sol new file mode 100644 index 000000000000..d709be0c701e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_struct_2.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + S s; + + function check() public { + require(s.x == 0); + conditional_increment(); + assert(s.x == 1); // should fail; + assert(s.x == 0); // should hold; + } + + function conditional_increment() internal { + if (s.x == 0) { + return; + } + s = S(1); + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (156-172): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_tuple.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_tuple.sol new file mode 100644 index 000000000000..e1e6b323ccac --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/simple_if_tuple.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + uint y; + + function check() public { + require(x == 0); + require(y == 0); + conditional_increment(); + assert(x == 0); // should fail; + assert(x == 1); // should fail; + assert(x == 2); // should hold; + } + + function conditional_increment() internal { + if (x == 0) { + (x,y) = (2,2); + return; + } + (x,y) = (1,1); + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (160-174): BMC: Assertion violation happens here. +// Warning 4661: (194-208): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/triple_nested_if.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/triple_nested_if.sol new file mode 100644 index 000000000000..38a5c852b7b0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/branches_with_return/triple_nested_if.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract C { + + uint a; + uint b; + uint c; + + function test() public view { + if (a == 0) { + if (b == 0) { + if (c == 0) { + return; + } + } + } + assert(a != 0 || b != 0 || c != 0); + } +} +// ==== +// SMTEngine: bmc +// ---- diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/funds.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/funds.sol new file mode 100644 index 000000000000..12936122451d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/funds.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C { + function f(address payable a) public { + a.transfer(200); + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 1236: (87-102): BMC: Insufficient funds happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/implicit_constructor_with_function_calls.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/implicit_constructor_with_function_calls.sol new file mode 100644 index 000000000000..2f09c6a9bda8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/implicit_constructor_with_function_calls.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + uint x = initX(); + uint y = initY(); + + function initX() internal pure returns (uint) { + return 42; + } + + function initY() internal view returns (uint) { + assert(x == 42); + return x; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (205-220): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/math.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/math.sol new file mode 100644 index 000000000000..ca63aceac822 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/math.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; +contract C { + uint z = 1; + uint w = z - 3; + function a(uint x, uint y) public pure returns (uint) { + return x + y; + } + function s(uint x, uint y) public pure returns (uint) { + return x - y; + } + function m(uint x, uint y) public pure returns (uint) { + return x * y; + } + function d(uint x, uint y) public pure returns (uint) { + return x / y; + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 2661: (141-146): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4144: (217-222): BMC: Underflow (resulting value less than 0) happens here. +// Warning 2661: (293-298): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 3046: (369-374): BMC: Division by zero happens here. +// Warning 4144: (68-73): BMC: Underflow (resulting value less than 0) happens here. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/unary_add_minus_overflow_detected.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/unary_add_minus_overflow_detected.sol new file mode 100644 index 000000000000..9d375fd5849d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/unary_add_minus_overflow_detected.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; +contract C { + uint8 x; + + function inc_pre() public { + ++x; + } + + function dec_pre() public { + --x; + } + + /* Commented out because Spacer segfaults in Z3 4.8.9 + function inc_post() public { + x++; + } + + function dec_post() public { + x--; + } + */ + +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 2661: (87-90): BMC: Overflow (resulting value larger than 255) happens here. +// Warning 4144: (127-130): BMC: Underflow (resulting value less than 0) happens here. diff --git a/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol b/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol index 5d6d3d3cb705..187bf87f7693 100644 --- a/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol +++ b/test/libsolidity/smtCheckerTests/complex/MerkleProof.sol @@ -34,7 +34,7 @@ library MerkleProof { } // ---- -// Warning 8364: (988-991): Assertion checker does not yet implement type abi // Warning 4588: (988-1032): Assertion checker does not yet implement this type of function call. -// Warning 8364: (1175-1178): Assertion checker does not yet implement type abi +// Warning 4588: (1175-1219): Assertion checker does not yet implement this type of function call. +// Warning 4588: (988-1032): Assertion checker does not yet implement this type of function call. // Warning 4588: (1175-1219): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol index 44808ce4c71c..92a45e5ac07a 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/const_state_variables.sol @@ -50,8 +50,9 @@ contract MyConc{ } } +// ==== +// SMTIgnoreCex: yes // ---- // Warning 2519: (773-792): This declaration shadows an existing declaration. // Warning 2018: (1009-1086): Function state mutability can be restricted to view -// Warning 6084: (985-1002): Underflow (resulting value less than 0) happens here. -// Warning 6084: (985-1002): Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (985-1002): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol b/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol index 6fc041fb115e..074bc5b1036e 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/data_dependency.sol @@ -118,23 +118,3 @@ contract PropagateThroughReturnValue { } // ---- // Warning 2018: (1879-1947): Function state mutability can be restricted to view -// Warning 8115: (318-332): Assertion checker does not yet support the type of this variable. -// Warning 8115: (338-347): Assertion checker does not yet support the type of this variable. -// Warning 8115: (353-378): Assertion checker does not yet support the type of this variable. -// Warning 8115: (384-409): Assertion checker does not yet support the type of this variable. -// Warning 7650: (464-479): Assertion checker does not yet support this expression. -// Warning 8364: (464-475): Assertion checker does not yet implement type struct Reference.St storage ref -// Warning 8182: (464-494): Assertion checker does not yet implement such assignments. -// Warning 7650: (539-554): Assertion checker does not yet support this expression. -// Warning 8364: (539-550): Assertion checker does not yet implement type struct Reference.St storage ref -// Warning 7650: (557-567): Assertion checker does not yet support this expression. -// Warning 8364: (557-563): Assertion checker does not yet implement type struct Reference.St storage ref -// Warning 8182: (539-567): Assertion checker does not yet implement such assignments. -// Warning 8115: (629-643): Assertion checker does not yet support the type of this variable. -// Warning 8364: (646-668): Assertion checker does not yet implement type struct Reference.St storage ref -// Warning 8364: (700-703): Assertion checker does not yet implement type struct Reference.St storage pointer -// Warning 8364: (706-728): Assertion checker does not yet implement type struct Reference.St storage ref -// Warning 8364: (700-728): Assertion checker does not yet implement type struct Reference.St storage pointer -// Warning 7650: (748-755): Assertion checker does not yet support this expression. -// Warning 8364: (748-751): Assertion checker does not yet implement type struct Reference.St storage pointer -// Warning 8182: (748-770): Assertion checker does not yet implement such assignments. diff --git a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol index 885200cf5096..69da6b331aee 100644 --- a/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol +++ b/test/libsolidity/smtCheckerTests/complex/slither/external_function.sol @@ -83,7 +83,6 @@ contract InternalCall { // Warning 2018: (1144-1206): Function state mutability can be restricted to pure // Warning 2018: (1212-1274): Function state mutability can be restricted to pure // Warning 2018: (1280-1342): Function state mutability can be restricted to pure -// Warning 8364: (771-774): Assertion checker does not yet implement type abi -// Warning 5084: (782-813): Type conversion is not yet fully supported and might yield false positives. // Warning 4588: (771-814): Assertion checker does not yet implement this type of function call. -// Warning 5729: (1403-1408): Assertion checker does not yet implement this type of function call. +// Warning 4588: (771-814): Assertion checker does not yet implement this type of function call. +// Warning 5729: (1403-1408): BMC does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol b/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol deleted file mode 100644 index 287e7e8f6a0c..000000000000 --- a/test/libsolidity/smtCheckerTests/complex/warn_on_struct.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma experimental SMTChecker; - -contract C { - struct A { uint a; uint b; } - function f() public pure returns (uint) { - A memory a = A({ a: 1, b: 2 }); - } -} -// ---- -// Warning 2072: (133-143): Unused local variable. -// Warning 8115: (133-143): Assertion checker does not yet support the type of this variable. -// Warning 8364: (146-147): Assertion checker does not yet implement type type(struct C.A storage pointer) -// Warning 8364: (146-163): Assertion checker does not yet implement type struct C.A memory -// Warning 4639: (146-163): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol b/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol index 383f907e92ed..b37bd1a196bc 100644 --- a/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol +++ b/test/libsolidity/smtCheckerTests/complex/warn_on_typecast.sol @@ -5,4 +5,3 @@ contract C { } } // ---- -// Warning 5084: (106-114): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_1.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_1.sol new file mode 100644 index 000000000000..252686135450 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_1.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract C { + uint x; + modifier m(uint z) { + uint y = 3; + if (z == 10) + x = 2 + y; + _; + if (z == 10) + x = 4 + y; + } + function f() m(10) internal { + x = 3; + } + function g() public { + x = 0; + f(); + assert(x == 7); + // Fails + assert(x == 6); + } +} +// ---- +// Warning 6328: (359-373): CHC: Assertion violation happens here.\nCounterexample:\nx = 7\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_2.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_2.sol new file mode 100644 index 000000000000..61763bd764a4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_2.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract C { + uint x; + modifier m(uint z) { + uint y = 3; + if (z == 10) + x = 2 + y; + _; + if (z == 10) + x = 4 + y; + } + function f() m(10) m(12) internal { + x = 3; + } + function g() public { + x = 0; + f(); + assert(x == 3); + // Fails + assert(x == 6); + } +} +// ---- +// Warning 6328: (365-379): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_3.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_3.sol new file mode 100644 index 000000000000..57e9af5312b0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_3.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract C { + uint x; + modifier m(uint z) { + uint y = 3; + if (z == 10) + x = 2 + y; + _; + if (z == 10) + x = 4 + y; + } + function f() m(8) internal { + x = 3; + } + function g() public { + x = 0; + f(); + assert(x == 3); + // Fails + assert(x == 6); + } +} +// ---- +// Warning 6328: (358-372): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_4.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_4.sol new file mode 100644 index 000000000000..6fd732d88269 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_inside_modifiers_4.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; +contract C { + uint x; + modifier m(uint z) { + uint y = 3; + if (z >= 10) + x = 2 + y; + _; + if (z >= 10) + x = 4 + y; + } + function f() m(10) m(12) internal { + x = 3; + } + function g() public { + x = 0; + f(); + assert(x == 7); + // Fails + assert(x == 6); + } +} +// ---- +// Warning 6328: (365-379): CHC: Assertion violation happens here.\nCounterexample:\nx = 7\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/branches_in_modifiers.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/branches_in_modifiers.sol new file mode 100644 index 000000000000..03d7ceab42f2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/branches_in_modifiers.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + + modifier check() { + require(x == 0); + _; + assert(x == 1); // should fail; + assert(x == 0); // should hold; + } + + modifier inc() { + if (x == 0) { + return; + } + x = x + 1; + _; + } + + function test() check inc public { + } +} +// ---- +// Warning 6328: (103-117): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ntest() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/branches_in_modifiers_2.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/branches_in_modifiers_2.sol new file mode 100644 index 000000000000..aaa57aa46312 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/branches_in_modifiers_2.sol @@ -0,0 +1,47 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + + function reset_if_overflow() internal postinc { + if (x < 10) + return; + x = 0; + } + + modifier postinc() { + if (x == 0) { + return; + } + _; + x = x + 1; + } + + function test() public { + if (x == 0) { + reset_if_overflow(); + assert(x == 1); // should fail; + assert(x == 0); // should hold; + return; + } + if (x < 10) { + uint oldx = x; + reset_if_overflow(); + assert(oldx + 1 == x); // should hold; + assert(oldx == x); // should fail; + return; + } + reset_if_overflow(); + assert(x == 1); // should hold; + assert(x == 0); // should fail; + } + + function set(uint _x) public { + x = _x; + } +} +// ---- +// Warning 6328: (384-398): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ntest() +// Warning 6328: (635-652): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nset(1)\nState: x = 1\ntest() +// Warning 6328: (781-795): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nset(10)\nState: x = 10\ntest() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init.sol new file mode 100644 index 000000000000..891dc86652cf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init.sol @@ -0,0 +1,41 @@ +pragma experimental SMTChecker; + +contract A { + int x; + constructor (int a) { x = a;} +} + +contract B is A { + int y; + constructor(int a) A(-a) { + if (a > 0) { + y = 2; + return; + } + else { + y = 3; + } + y = 4; // overwrites the else branch + } +} + +contract C is B { + constructor(int a) B(a) { + assert(y != 3); // should hold + assert(y == 4); // should fail + if (a > 0) { + assert(x < 0 && y == 2); // should hold + assert(x < 0 && y == 4); // should fail + } + else { + assert(x >= 0 && y == 4); // should hold + assert(x >= 0 && y == 2); // should fail + assert(x > 0); // should fail + } + } +} +// ---- +// Warning 6328: (330-344): CHC: Assertion violation happens here.\nCounterexample:\ny = 2, x = (- 1)\na = 1\n\n\nTransaction trace:\nconstructor(1) +// Warning 6328: (422-445): CHC: Assertion violation happens here.\nCounterexample:\ny = 2, x = (- 1)\na = 1\n\n\nTransaction trace:\nconstructor(1) +// Warning 6328: (522-546): CHC: Assertion violation happens here.\nCounterexample:\ny = 4, x = 0\na = 0\n\n\nTransaction trace:\nconstructor(0) +// Warning 6328: (566-579): CHC: Assertion violation happens here.\nCounterexample:\ny = 4, x = 0\na = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init_chain_alternate.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init_chain_alternate.sol new file mode 100644 index 000000000000..f82b7de230ef --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init_chain_alternate.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract A { + uint x = 1; +} + +contract B is A { + constructor(int a) { + if (a > 0) { + x = 2; + return; + } + x = 3; + } +} + +abstract contract C is B { +} + +contract D is C { + constructor(int a) B(a) { + assert(a > 0 || x == 3); // should hold + assert(a <= 0 || x == 2); // should hold + assert(x == 1); // should fail + } +} +// ---- +// Warning 6328: (319-333): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\na = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init_diamond.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init_diamond.sol new file mode 100644 index 000000000000..b93bb6b2f4d3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructor_state_variable_init_diamond.sol @@ -0,0 +1,70 @@ +pragma experimental SMTChecker; + +contract A { + int x; +} + +contract B is A { + int y; + constructor (int a) { + if (a >= 0) { + y = 1; + return; + } + x = 1; + y = 2; + } +} + +contract C is A { + int z; + constructor (int a) { + if (a >= 0) { + z = 1; + return; + } + x = -1; + z = 2; + } +} + +contract D1 is B, C { + constructor() B(1) C(1) { + assert(x == 0); // should hold + assert(x == 1); // should fail + assert(x == -1); // should fail + } +} + +contract D2 is B, C { + constructor() B(1) C(-1) { + assert(x == 0); // should fail + assert(x == 1); // should fail + assert(x == -1); // should hold (constructor of C is executed AFTER constructor of B) + } +} + +contract D3 is B, C { + constructor() B(-1) C(1) { + assert(x == 0); // should fail + assert(x == 1); // should hold + assert(x == -1); // should fail + } +} + +contract D4 is B, C { + constructor() B(-1) C(-1) { + assert(x == 0); // should fail + assert(x == 1); // should fail + assert(x == -1); // should hold (constructor of C is executed AFTER constructor of B) + } +} +// ---- +// Warning 6328: (370-384): CHC: Assertion violation happens here.\nCounterexample:\nz = 1, y = 1, x = 0\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (403-418): CHC: Assertion violation happens here.\nCounterexample:\nz = 1, y = 1, x = 0\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (493-507): CHC: Assertion violation happens here.\nCounterexample:\nz = 2, y = 1, x = (- 1)\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (526-540): CHC: Assertion violation happens here.\nCounterexample:\nz = 2, y = 1, x = (- 1)\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (703-717): CHC: Assertion violation happens here.\nCounterexample:\nz = 1, y = 2, x = 1\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (769-784): CHC: Assertion violation happens here.\nCounterexample:\nz = 1, y = 2, x = 1\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (860-874): CHC: Assertion violation happens here.\nCounterexample:\nz = 2, y = 2, x = (- 1)\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (893-907): CHC: Assertion violation happens here.\nCounterexample:\nz = 2, y = 2, x = (- 1)\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructors.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructors.sol new file mode 100644 index 000000000000..2a014ecc785d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/constructors.sol @@ -0,0 +1,31 @@ +pragma experimental SMTChecker; + +contract B { + int x; + constructor(int b) { + if (b > 0) { + x = 1; + return; + } + else { + x = 2; + return; + } + x = 3; // dead code + } +} + +contract C is B { + constructor(int a) B(a) { + assert(a > 0 || x == 2); // should hold + assert(a <= 0 || x == 1); // should hold + assert(x == 3); // should fail + assert(x == 2); // should fail + assert(x == 1); // should fail + } +} +// ---- +// Warning 5740: (152-157): Unreachable code. +// Warning 6328: (310-324): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 1\n\n\nTransaction trace:\nconstructor(1) +// Warning 6328: (343-357): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 1\n\n\nTransaction trace:\nconstructor(1) +// Warning 6328: (376-390): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\na = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/nested_if.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/nested_if.sol new file mode 100644 index 000000000000..3f7c2dd1b72f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/nested_if.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract C { + + function test(uint256 a, uint256 b) public pure { + assert(nested_if(a,b) != 42); // should hold + assert(nested_if(a,b) == 1); // should fail + } + + function nested_if(uint256 a, uint256 b) internal pure returns (uint256) { + if (a < 5) { + if (b > 1) { + return 0; + } + } + if (a == 2 && b == 2) { + return 42; // unreachable + } + else { + return 1; + } + } +} +// ---- +// Warning 6328: (147-174): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\nb = 2\n\n\nTransaction trace:\nconstructor()\ntest(0, 2) +// Warning 6838: (332-348): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/return_in_both_branches.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/return_in_both_branches.sol new file mode 100644 index 000000000000..07bb637e9758 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/return_in_both_branches.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + + function test() public pure { + assert(branches(0) == 0); + assert(branches(1) == 42); + } + + function branches(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + else { + return 42; + } + return 1; // dead code + } +} +// ---- +// Warning 5740: (265-273): Unreachable code. diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if.sol new file mode 100644 index 000000000000..b77bc2b2b875 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function test(uint256 a, uint256 b) public pure returns (uint256) { + if (a == 0) { + return 0; + } + return b / a; // This division is safe because of the early return in if-block. + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if2.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if2.sol new file mode 100644 index 000000000000..69eb57c8d52b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if2.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + + function test(uint256 a) public pure { + assert(simple_if(a) == 1); // should fail for a == 0 + } + + function simple_if(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + return 1; + } +} +// ---- +// Warning 6328: (89-114): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\n\n\nTransaction trace:\nconstructor()\ntest(0) diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_array.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_array.sol new file mode 100644 index 000000000000..2333c8b470dd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_array.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract C { + + uint[] a; + + constructor () { + a.push(); + a.push(); + } + + function check() public { + require(a.length >= 2); + require(a[1] == 0); + conditional_store(); + assert(a[1] == 1); // should fail; + assert(a[1] == 0); // should hold; + } + + function conditional_store() internal { + if (a[1] == 0) { + return; + } + a[1] = 1; + } +} +// ---- +// Warning 6328: (205-222): CHC: Assertion violation happens here.\nCounterexample:\na = [0, 0]\n\n\n\nTransaction trace:\nconstructor()\nState: a = [0, 0]\ncheck() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_state_var.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_state_var.sol new file mode 100644 index 000000000000..52d1b3ad7492 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_state_var.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + + function check() public { + require(x == 0); + conditional_increment(); + assert(x == 1); // should fail; + assert(x == 0); // should hold; + } + + function conditional_increment() internal { + if (x == 0) { + return; + } + x = 1; + } +} +// ---- +// Warning 6328: (132-146): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ncheck() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_struct.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_struct.sol new file mode 100644 index 000000000000..37e3d1d95cfa --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_struct.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + S s; + + function check() public { + require(s.x == 0); + conditional_increment(); + assert(s.x == 1); // should fail; + assert(s.x == 0); // should hold; + } + + function conditional_increment() internal { + if (s.x == 0) { + return; + } + s.x = 1; + } +} +// ---- +// Warning 6328: (156-172): CHC: Assertion violation happens here.\nCounterexample:\ns = {x: 0}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 0}\ncheck() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_struct_2.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_struct_2.sol new file mode 100644 index 000000000000..4ec0f8d7c3f5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_struct_2.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + S s; + + function check() public { + require(s.x == 0); + conditional_increment(); + assert(s.x == 1); // should fail; + assert(s.x == 0); // should hold; + } + + function conditional_increment() internal { + if (s.x == 0) { + return; + } + s = S(1); + } +} +// ---- +// Warning 6328: (156-172): CHC: Assertion violation happens here.\nCounterexample:\ns = {x: 0}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 0}\ncheck() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_tuple.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_tuple.sol new file mode 100644 index 000000000000..33c63368e355 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/simple_if_tuple.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C { + + uint x; + uint y; + + function check() public { + require(x == 0); + require(y == 0); + conditional_increment(); + assert(x == 0); // should fail; + assert(x == 1); // should fail; + assert(x == 2); // should hold; + } + + function conditional_increment() internal { + if (x == 0) { + (x,y) = (2,2); + return; + } + (x,y) = (1,1); + } +} +// ---- +// Warning 6328: (160-174): CHC: Assertion violation happens here.\nCounterexample:\nx = 2, y = 2\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\ncheck() +// Warning 6328: (194-208): CHC: Assertion violation happens here.\nCounterexample:\nx = 2, y = 2\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\ncheck() diff --git a/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/triple_nested_if.sol b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/triple_nested_if.sol new file mode 100644 index 000000000000..06caf8a2a2ce --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/branches_with_return/triple_nested_if.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + + uint a; + uint b; + uint c; + + function test() public view { + if (a == 0) { + if (b == 0) { + if (c == 0) { + return; + } + } + } + assert(a != 0 || b != 0 || c != 0); + } +} diff --git a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch.sol index fc54d5a01040..c32bdd983531 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch.sol @@ -15,6 +15,3 @@ contract C } } // ---- -// Warning 5084: (208-218): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (123-133): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (208-218): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_2.sol b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_2.sol index 0114ab9886cc..1bff12f860ee 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_2.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_2.sol @@ -20,8 +20,3 @@ contract C } } // ---- -// Warning 5084: (271-281): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (123-133): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (271-281): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (186-196): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (271-281): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_3.sol b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_3.sol index f0003590518f..d14a2e6bc1c5 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_3.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_3.sol @@ -20,8 +20,3 @@ contract C } } // ---- -// Warning 5084: (275-285): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (123-133): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (275-285): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (189-199): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (275-285): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_4.sol b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_4.sol index e8f08cc3edd2..bdb7b9fd8e68 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_4.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_branch_4.sol @@ -25,7 +25,3 @@ contract C } // ---- -// Warning 5084: (275-285): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (123-133): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (189-199): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (275-285): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_else_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_else_branch.sol index 23ba4ff054ce..8c3099bf14f2 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_else_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_else_branch.sol @@ -16,6 +16,3 @@ contract C } } // ---- -// Warning 5084: (219-229): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (134-144): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (219-229): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_modifier_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_modifier_branch.sol index 755d44ff678d..f79be22988e5 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_modifier_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_modifier_branch.sol @@ -19,6 +19,3 @@ contract C } } // ---- -// Warning 5084: (249-259): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (118-128): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (249-259): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_placeholder_inside_modifier_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_placeholder_inside_modifier_branch.sol index e8d22edcf2c5..5366b22d433e 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_placeholder_inside_modifier_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/function_call_inside_placeholder_inside_modifier_branch.sol @@ -20,6 +20,3 @@ contract C } } // ---- -// Warning 5084: (247-257): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (162-172): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (247-257): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/control_flow/require.sol b/test/libsolidity/smtCheckerTests/control_flow/require.sol new file mode 100644 index 000000000000..7672a7418d4f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/require.sol @@ -0,0 +1,34 @@ +pragma experimental SMTChecker; + +contract C { + function f() pure public { + require(false); + // This is not reachable. + assert(false); + } + + function g() pure public { + require(false, "require message"); + // This is not reachable. + assert(false); + } + + function h(bool b) pure public { + if (b) + require(false); + assert(!b); + } + + // Check that arguments are evaluated. + bool x = false; + function m() view internal returns (string memory) { + assert(x != true); + } + function i() public { + x = true; + require(false, m()); + } +} +// ---- +// Warning 6321: (429-442): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (448-465): CHC: Assertion violation happens here.\nCounterexample:\nx = true\n\n\n\nTransaction trace:\nconstructor()\nState: x = false\ni() diff --git a/test/libsolidity/smtCheckerTests/control_flow/return_1.sol b/test/libsolidity/smtCheckerTests/control_flow/return_1.sol new file mode 100644 index 000000000000..3dabd2feff20 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/return_1.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function add(uint x, uint y) internal pure returns (uint) { + if (y == 0) + return x; + if (y == 1) + return ++x; + if (y == 2) + return x + 2; + return x + y; + } + + function f() public pure { + assert(add(100, 0) == 100); + assert(add(100, 1) == 101); + assert(add(100, 2) == 102); + assert(add(100, 100) == 200); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/control_flow/return_1_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/return_1_fail.sol new file mode 100644 index 000000000000..02b88c9819a1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/return_1_fail.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C { + function add(uint x, uint y) internal pure returns (uint) { + if (y == 0) + return x; + if (y == 1) + return ++x; + if (y == 2) + return x + 2; + return x + y; + } + + function f() public pure { + assert(add(100, 0) != 100); + assert(add(100, 1) != 101); + assert(add(100, 2) != 102); + assert(add(100, 100) != 200); + } +} +// ---- +// Warning 6328: (244-270): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (274-300): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (304-330): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (334-362): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/control_flow/return_2.sol b/test/libsolidity/smtCheckerTests/control_flow/return_2.sol new file mode 100644 index 000000000000..c6005bb08cdc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/return_2.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C { + uint c; + function add(uint x, uint y) internal returns (uint) { + c = 0xff; + if (y == 0) + return x; + c = 0xffff; + if (y == 1) + return ++x; + c = 0xffffff; + if (y == 2) + return x + 2; + c = 0xffffffff; + return x + y; + } + + function f() public { + assert(add(100, 0) == 100); + assert(c == 0xff); + assert(add(100, 1) == 101); + assert(c == 0xffff); + assert(add(100, 2) == 102); + assert(c == 0xffffff); + assert(add(100, 100) == 200); + assert(c == 0xffffffff); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/control_flow/return_2_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/return_2_fail.sol new file mode 100644 index 000000000000..34c798a2ad22 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/return_2_fail.sol @@ -0,0 +1,40 @@ +pragma experimental SMTChecker; + +contract C { + uint c; + function add(uint x, uint y) internal returns (uint) { + c = 0xff; + if (y == 0) + return x; + c = 0xffff; + if (y == 1) + return ++x; + c = 0xffffff; + if (y == 2) + return x + 2; + c = 0xffffffff; + return x + y; + } + + function f() public { + assert(add(100, 0) != 100); + assert(c != 0xff); + assert(add(100, 1) != 101); + assert(c != 0xffff); + assert(add(100, 2) != 102); + assert(c != 0xffffff); + assert(add(100, 100) != 200); + assert(c != 0xffffffff); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (303-329): CHC: Assertion violation happens here. +// Warning 6328: (333-350): CHC: Assertion violation happens here. +// Warning 6328: (354-380): CHC: Assertion violation happens here. +// Warning 6328: (384-403): CHC: Assertion violation happens here. +// Warning 6328: (407-433): CHC: Assertion violation happens here. +// Warning 6328: (437-458): CHC: Assertion violation happens here. +// Warning 6328: (462-490): CHC: Assertion violation happens here. +// Warning 6328: (494-517): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/control_flow/revert.sol b/test/libsolidity/smtCheckerTests/control_flow/revert.sol new file mode 100644 index 000000000000..828c9eae02bc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/revert.sol @@ -0,0 +1,36 @@ +pragma experimental SMTChecker; + +contract C { + function f() pure public { + revert(); + // This is not reachable. + assert(false); + } + + function g() pure public { + revert("revert message"); + // This is not reachable. + assert(false); + } + + function h(bool b) pure public { + if (b) + revert(); + assert(!b); + } + + // Check that arguments are evaluated. + bool x = false; + function m() view internal returns (string memory) { + assert(x != true); + } + function i() public { + x = true; + revert(m()); + } +} +// ---- +// Warning 5740: (116-129): Unreachable code. +// Warning 5740: (221-234): Unreachable code. +// Warning 6321: (408-421): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (427-444): CHC: Assertion violation happens here.\nCounterexample:\nx = true\n\n\n\nTransaction trace:\nconstructor()\nState: x = false\ni() diff --git a/test/libsolidity/smtCheckerTests/control_flow/revert_complex_flow.sol b/test/libsolidity/smtCheckerTests/control_flow/revert_complex_flow.sol new file mode 100644 index 000000000000..1aa461d582cb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/control_flow/revert_complex_flow.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b, uint a) pure public { + require(a <= 256); + if (b) + revert(); + uint c = a + 1; + if (b) + c--; + else + c++; + assert(c == a); + } +} +// ---- +// Warning 6328: (183-197): CHC: Assertion violation happens here.\nCounterexample:\n\nb = false\na = 0\n\n\nTransaction trace:\nconstructor()\nf(false, 0) +// Warning 6838: (155-156): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol index e969e3c3d3e0..66f2627876d4 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol index 186a2e1a80e5..922e676f75ab 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_fail.sol @@ -15,5 +15,4 @@ contract c { } } // ---- -// Warning 6328: (227-236): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (227-236): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n = false\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol index 1dd387cc9439..f82d2157c0a1 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_inside_branch.sol @@ -17,6 +17,5 @@ contract c { } } // ---- -// Warning 6328: (202-218): Assertion violation happens here -// Warning 6328: (242-252): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (202-218): CHC: Assertion violation happens here.\nCounterexample:\nx = 101\n\n = false\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (242-252): CHC: Assertion violation happens here.\nCounterexample:\nx = 101\n\n = false\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol index ff526db604d3..905964ed5bcd 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol index 32fbccdd7c22..6a9c7c48b14c 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_need_both_fail.sol @@ -15,5 +15,4 @@ contract c { } } // ---- -// Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (225-235): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n = false\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched.sol index c596f4f90682..d4b50a29e3a4 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched.sol @@ -12,8 +12,8 @@ contract C } } // ---- -// Warning 6838: (84-110): Condition is always false. -// Warning 6838: (121-147): Condition is always true. -// Warning 6838: (158-183): Condition is always false. -// Warning 6838: (194-221): Condition is always false. -// Warning 6838: (232-247): Condition is always true. +// Warning 6838: (84-110): BMC: Condition is always false. +// Warning 6838: (121-147): BMC: Condition is always true. +// Warning 6838: (158-183): BMC: Condition is always false. +// Warning 6838: (194-221): BMC: Condition is always false. +// Warning 6838: (232-247): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched_function.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched_function.sol index 127ebfccec3c..cf4424d5ad86 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched_function.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_and_touched_function.sol @@ -16,8 +16,8 @@ contract C } } // ---- -// Warning 6838: (156-179): Condition is always false. -// Warning 6838: (190-213): Condition is always true. -// Warning 6838: (224-243): Condition is always false. -// Warning 6838: (254-277): Condition is always false. -// Warning 6838: (288-300): Condition is always true. +// Warning 6838: (156-179): BMC: Condition is always false. +// Warning 6838: (190-213): BMC: Condition is always true. +// Warning 6838: (224-243): BMC: Condition is always false. +// Warning 6838: (254-277): BMC: Condition is always false. +// Warning 6838: (288-300): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol index d6007e6764ca..1a6cb909a4a5 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol index edada1444cb2..07127b976d15 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_fail.sol @@ -15,5 +15,4 @@ contract c { } } // ---- -// Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (225-235): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n = false\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol index eb54e2236d7e..ba1536a5cc20 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_inside_branch.sol @@ -24,5 +24,4 @@ contract c { } } // ---- -// Warning 6328: (360-370): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (360-370): CHC: Assertion violation happens here.\nCounterexample:\nx = 102\na = false\n = false\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(false) diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol index c585abdb486e..c4a823dbc785 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both.sol @@ -15,4 +15,3 @@ contract c { } } // ---- -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol index 1a055a9d7da0..bddbd8e00c65 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_need_both_fail.sol @@ -15,5 +15,4 @@ contract c { } } // ---- -// Warning 6328: (225-235): Assertion violation happens here -// Warning 2661: (101-106): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (225-235): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n = false\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched.sol index e629b188cc92..c5d9bb08df97 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched.sol @@ -12,8 +12,8 @@ contract C } } // ---- -// Warning 6838: (84-110): Condition is always true. -// Warning 6838: (121-147): Condition is always true. -// Warning 6838: (158-183): Condition is always true. -// Warning 6838: (194-221): Condition is always true. -// Warning 6838: (232-248): Condition is always false. +// Warning 6838: (84-110): BMC: Condition is always true. +// Warning 6838: (121-147): BMC: Condition is always true. +// Warning 6838: (158-183): BMC: Condition is always true. +// Warning 6838: (194-221): BMC: Condition is always true. +// Warning 6838: (232-248): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched_function.sol b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched_function.sol index 3ec6a53dbcd8..c97365981088 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched_function.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/short_circuit_or_touched_function.sol @@ -16,8 +16,8 @@ contract C } } // ---- -// Warning 6838: (156-179): Condition is always true. -// Warning 6838: (190-213): Condition is always true. -// Warning 6838: (224-243): Condition is always true. -// Warning 6838: (254-277): Condition is always true. -// Warning 6838: (288-301): Condition is always false. +// Warning 6838: (156-179): BMC: Condition is always true. +// Warning 6838: (190-213): BMC: Condition is always true. +// Warning 6838: (224-243): BMC: Condition is always true. +// Warning 6838: (254-277): BMC: Condition is always true. +// Warning 6838: (288-301): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/control_flow/try_catch_1.sol b/test/libsolidity/smtCheckerTests/control_flow/try_catch_1.sol index 56d4dd804a43..921aeea7b2e8 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/try_catch_1.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/try_catch_1.sol @@ -8,5 +8,8 @@ contract C { // ==== // EVMVersion: >=byzantium // ---- +// Warning 6321: (75-79): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 7645: (98-121): Assertion checker does not support try/catch clauses. +// Warning 7645: (124-159): Assertion checker does not support try/catch clauses. // Warning 7645: (98-121): Assertion checker does not support try/catch clauses. // Warning 7645: (124-159): Assertion checker does not support try/catch clauses. diff --git a/test/libsolidity/smtCheckerTests/control_flow/try_catch_2.sol b/test/libsolidity/smtCheckerTests/control_flow/try_catch_2.sol index dfe2a4428308..ed1971b4a0eb 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/try_catch_2.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/try_catch_2.sol @@ -12,3 +12,5 @@ contract C { // ---- // Warning 7645: (83-85): Assertion checker does not support try/catch clauses. // Warning 7645: (88-122): Assertion checker does not support try/catch clauses. +// Warning 7645: (83-85): Assertion checker does not support try/catch clauses. +// Warning 7645: (88-122): Assertion checker does not support try/catch clauses. diff --git a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_1.sol b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_1.sol index 37adaa0d3f60..c297471db31c 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_1.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_1.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (159-173): Assertion violation happens here +// Warning 6328: (159-173): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 11\n\n\nTransaction trace:\nconstructor()\nf(11) diff --git a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_2.sol b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_2.sol index 44124a100f0f..1809fe1c0297 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_2.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_2.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (159-173): Assertion violation happens here +// Warning 6328: (159-173): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 11\n\n\nTransaction trace:\nconstructor()\nf(11) diff --git a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_3.sol b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_3.sol index 2369aa5a9be6..77a739f0aad3 100644 --- a/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_3.sol +++ b/test/libsolidity/smtCheckerTests/control_flow/ways_to_merge_variables_3.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (161-175): Assertion violation happens here +// Warning 6328: (161-175): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 11\n\n\nTransaction trace:\nconstructor()\nf(11) diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_compare_hashes.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_compare_hashes.sol new file mode 100644 index 000000000000..6e2534e20bf4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_compare_hashes.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + function f(bytes memory data) public pure { + bytes32 k = keccak256(data); + bytes32 s = sha256(data); + bytes32 r = ripemd160(data); + assert(k == s); + assert(s == r); + assert(r == k); + } +} +// ---- +// Warning 6328: (183-197): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [7, 7]\n\n\nTransaction trace:\nconstructor()\nf([7, 7]) +// Warning 6328: (201-215): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [9, 9]\n\n\nTransaction trace:\nconstructor()\nf([9, 9]) +// Warning 6328: (219-233): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [7, 7]\n\n\nTransaction trace:\nconstructor()\nf([7, 7]) diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol new file mode 100644 index 000000000000..34ff43ccd9bf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_fail.sol @@ -0,0 +1,35 @@ +pragma experimental SMTChecker; + +contract C { + function k(bytes memory b0, bytes memory b1) public pure { + bytes32 k0 = keccak256(b0); + bytes32 k1 = keccak256(b1); + assert(k0 == k1); + } + function s(bytes memory b0, bytes memory b1) public pure { + bytes32 s0 = sha256(b0); + bytes32 s1 = sha256(b1); + assert(s0 == s1); + } + function r(bytes memory b0, bytes memory b1) public pure { + bytes32 r0 = ripemd160(b0); + bytes32 r1 = ripemd160(b1); + assert(r0 == r1); + } + function e(bytes32 h0, uint8 v0, bytes32 r0, bytes32 s0, bytes32 h1, uint8 v1, bytes32 r1, bytes32 s1) public pure { + address a0 = ecrecover(h0, v0, r0, s0); + address a1 = ecrecover(h1, v1, r1, s1); + assert(a0 == a1); + } +} +// ---- +// Warning 1218: (168-184): CHC: Error trying to invoke SMT solver. +// Warning 6328: (168-184): CHC: Assertion violation might happen here. +// Warning 1218: (305-321): CHC: Error trying to invoke SMT solver. +// Warning 6328: (305-321): CHC: Assertion violation might happen here. +// Warning 1218: (448-464): CHC: Error trying to invoke SMT solver. +// Warning 6328: (448-464): CHC: Assertion violation might happen here. +// Warning 6328: (673-689): CHC: Assertion violation happens here.\nCounterexample:\n\nh0 = 21238\nv0 = 173\nr0 = 30612\ns0 = 32285\nh1 = 7719\nv1 = 21\nr1 = 10450\ns1 = 8855\n\n\nTransaction trace:\nconstructor()\ne(21238, 173, 30612, 32285, 7719, 21, 10450, 8855) +// Warning 4661: (168-184): BMC: Assertion violation happens here. +// Warning 4661: (305-321): BMC: Assertion violation happens here. +// Warning 4661: (448-464): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_not_same.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_not_same.sol new file mode 100644 index 000000000000..13537a23d6d5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_not_same.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + function f(bytes memory data) public pure { + bytes32 k = keccak256(data); + fi(data, k); + } + function fi(bytes memory data, bytes32 k) internal pure { + bytes32 h = sha256(data); + assert(h == k); + } +} +// ---- +// Warning 6328: (229-243): CHC: Assertion violation happens here.\nCounterexample:\n\ndata = [7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]\n\n\nTransaction trace:\nconstructor()\nf([7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]) diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_over_blocks.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_over_blocks.sol new file mode 100644 index 000000000000..b277d88c94f4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_over_blocks.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f(bytes memory data) public pure { + bytes32 k = keccak256(data); + fi(data, k); + } + function fi(bytes memory data, bytes32 k) internal pure { + bytes32 h = keccak256(data); + assert(h == k); + } +} diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output.sol new file mode 100644 index 000000000000..ea2fd162b8e4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output.sol @@ -0,0 +1,38 @@ +pragma experimental SMTChecker; + +contract C { + bytes data; + bytes32 h; + uint8 v; + bytes32 r; + bytes32 s; + + bytes32 kec; + bytes32 sha; + bytes32 rip; + address erc; + + constructor(bytes memory _data, bytes32 _h, uint8 _v, bytes32 _r, bytes32 _s) { + data = _data; + h = _h; + v = _v; + r = _r; + s = _s; + + kec = keccak256(data); + sha = sha256(data); + rip = ripemd160(data); + erc = ecrecover(h, v, r, s); + } + + function f() public view { + bytes32 _kec = keccak256(data); + bytes32 _sha = sha256(data); + bytes32 _rip = ripemd160(data); + address _erc = ecrecover(h, v, r, s); + assert(_kec == kec); + assert(_sha == sha); + assert(_rip == rip); + assert(_erc == erc); + } +} diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol new file mode 100644 index 000000000000..10c6ada8eff2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_over_state_same_output_fail.sol @@ -0,0 +1,57 @@ +pragma experimental SMTChecker; + +contract C { + bytes data; + bytes32 h; + uint8 v; + bytes32 r; + bytes32 s; + + bytes32 kec; + bytes32 sha; + bytes32 rip; + address erc; + + constructor(bytes memory _data, bytes32 _h, uint8 _v, bytes32 _r, bytes32 _s) { + data = _data; + h = _h; + v = _v; + r = _r; + s = _s; + + kec = keccak256(data); + sha = sha256(data); + rip = ripemd160(data); + erc = ecrecover(h, v, r, s); + } + + function set(bytes memory _data, bytes32 _h, uint8 _v, bytes32 _r, bytes32 _s) public { + data = _data; + h = _h; + v = _v; + r = _r; + s = _s; + } + + function f() public view { + bytes32 _kec = keccak256(data); + bytes32 _sha = sha256(data); + bytes32 _rip = ripemd160(data); + address _erc = ecrecover(h, v, r, s); + assert(_kec == kec); + assert(_sha == sha); + assert(_rip == rip); + assert(_erc == erc); + } +} +// ---- +// Warning 1218: (726-745): CHC: Error trying to invoke SMT solver. +// Warning 6328: (726-745): CHC: Assertion violation might happen here. +// Warning 1218: (749-768): CHC: Error trying to invoke SMT solver. +// Warning 6328: (749-768): CHC: Assertion violation might happen here. +// Warning 1218: (772-791): CHC: Error trying to invoke SMT solver. +// Warning 6328: (772-791): CHC: Assertion violation might happen here. +// Warning 6328: (795-814): CHC: Assertion violation happens here. +// Warning 4661: (726-745): BMC: Assertion violation happens here. +// Warning 4661: (749-768): BMC: Assertion violation happens here. +// Warning 4661: (772-791): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_same_output.sol b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_same_output.sol new file mode 100644 index 000000000000..f9e17f2591f2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/crypto/crypto_functions_same_input_same_output.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract C { + function k(bytes memory b0) public pure { + bytes memory b1 = b0; + bytes32 k0 = keccak256(b0); + bytes32 k1 = keccak256(b1); + assert(k0 == k1); + } + function s(bytes memory b0) public pure { + bytes memory b1 = b0; + bytes32 s0 = sha256(b0); + bytes32 s1 = sha256(b1); + assert(s0 == s1); + } + function r(bytes memory b0) public pure { + bytes memory b1 = b0; + bytes32 r0 = ripemd160(b0); + bytes32 r1 = ripemd160(b1); + assert(r0 == r1); + } + function e(bytes32 h0, uint8 v0, bytes32 r0, bytes32 s0) public pure { + (bytes32 h1, uint8 v1, bytes32 r1, bytes32 s1) = (h0, v0, r0, s0); + address a0 = ecrecover(h0, v0, r0, s0); + address a1 = ecrecover(h1, v1, r1, s1); + assert(a0 == a1); + } +} diff --git a/test/libsolidity/smtCheckerTests/external_calls/external.sol b/test/libsolidity/smtCheckerTests/external_calls/external.sol index 7e14fa9278ed..2f0b822159a3 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external.sol @@ -17,4 +17,4 @@ contract C { } } // ---- -// Warning 6328: (200-214): Assertion violation happens here +// Warning 6328: (200-214): CHC: Assertion violation happens here.\nCounterexample:\nx = 10, d = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, d = 0\nf()\nState: x = 1, d = 0\nf()\nState: x = 2, d = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash.sol index 322af8129806..be0a13abc08b 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_hash.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash.sol @@ -25,5 +25,7 @@ contract C { assert(sig_1 == sig_2); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (423-445): Assertion violation happens here +// Warning 6328: (423-445): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure.sol index 510950f414e2..0f488aa99248 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_pure.sol @@ -27,5 +27,7 @@ contract C { assert(sig_1 == sig_2); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (431-453): Assertion violation happens here +// Warning 6328: (431-453): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state.sol index cd24a87fd2ba..b006f4b6e5d2 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state.sol @@ -34,5 +34,4 @@ contract C { } } // ---- -// Warning 6328: (528-565): Assertion violation happens here -// Warning 5084: (544-554): Type conversion is not yet fully supported and might yield false positives. +// Warning 6328: (528-565): CHC: Assertion violation happens here.\nCounterexample:\nowner = 1, y = 0, z = 0, s = 0\n\n\n\nTransaction trace:\nconstructor()\nState: owner = 1, y = 0, z = 0, s = 0\ninv() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy.sol index fd2198fe5aa0..e92f63559990 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy.sol @@ -29,4 +29,4 @@ contract C { } } // ---- -// Warning 6328: (299-313): Assertion violation happens here +// Warning 6328: (299-313): CHC: Assertion violation happens here.\nCounterexample:\nowner = 0, y = 0, s = 0\n\n\n\nTransaction trace:\nconstructor()\nState: owner = 0, y = 0, s = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect.sol index 698a6c647219..f07a4530220e 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_indirect.sol @@ -41,7 +41,8 @@ contract C { return y; } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (452-466): Assertion violation happens here -// Warning 6328: (470-496): Assertion violation happens here -// Warning 5084: (92-102): Type conversion is not yet fully supported and might yield false positives. +// Warning 6328: (452-466): CHC: Assertion violation happens here. +// Warning 6328: (470-496): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe.sol index 74c71e398ac3..ea70f99c5a58 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_reentrancy_unsafe.sol @@ -33,7 +33,8 @@ contract C { return y; } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (381-395): Assertion violation happens here -// Warning 6328: (399-425): Assertion violation happens here -// Warning 5084: (116-126): Type conversion is not yet fully supported and might yield false positives. +// Warning 6328: (381-395): CHC: Assertion violation happens here. +// Warning 6328: (399-425): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe.sol b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe.sol index c37614fd677b..b75510a581cd 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_hash_known_code_state_unsafe.sol @@ -37,7 +37,8 @@ contract C { assert(owner == address(0) || y != z); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (435-461): Assertion violation happens here -// Warning 6328: (594-631): Assertion violation happens here -// Warning 5084: (610-620): Type conversion is not yet fully supported and might yield false positives. +// Warning 6328: (435-461): CHC: Assertion violation happens here. +// Warning 6328: (594-631): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol b/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol index 38b588463572..deab0cc9a57d 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_inc.sol @@ -18,5 +18,6 @@ contract C { } } // ---- -// Warning 6328: (189-203): Assertion violation happens here -// Warning 2661: (146-149): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (146-149): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (189-203): CHC: Assertion violation happens here.\nCounterexample:\nx = 10, d = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, d = 0\ninc()\nState: x = 1, d = 0\ninc()\nState: x = 2, d = 0\nf() +// Warning 2661: (146-149): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_inc1_inc2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_inc1_inc2.sol index e96f8b279fbf..30c61b2cf8b4 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_inc1_inc2.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_inc1_inc2.sol @@ -20,9 +20,10 @@ contract C { function f() public { uint oldX = x; - d.d(); + // Removed because Spacer 4.8.9 seg faults. + //d.d(); assert(oldX == x); } } // ---- -// Warning 6328: (286-303): Assertion violation happens here +// Warning 2018: (236-355): Function state mutability can be restricted to view diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol b/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol index 6a2b664910d6..aa4a8a6c13a1 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_safe.sol @@ -16,3 +16,6 @@ contract C { assert(x < 11); } } +// ---- +// Warning 6328: (200-214): CHC: Assertion violation might happen here. +// Warning 4661: (200-214): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_single_inc.sol b/test/libsolidity/smtCheckerTests/external_calls/external_single_inc.sol index a6e4917ccc7c..e4473c567856 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_single_inc.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_single_inc.sol @@ -23,4 +23,4 @@ contract C { } } // ---- -// Warning 6328: (256-273): Assertion violation happens here +// Warning 6328: (256-273): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/mutex_f_no_guard.sol b/test/libsolidity/smtCheckerTests/external_calls/mutex_f_no_guard.sol index 22e9d53f3b00..371ab10f8f85 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/mutex_f_no_guard.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/mutex_f_no_guard.sol @@ -27,4 +27,4 @@ contract C { } } // ---- -// Warning 6328: (307-321): Assertion violation happens here +// Warning 6328: (307-321): CHC: Assertion violation happens here.\nCounterexample:\nx = 1, d = 0, lock = false\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, d = 0, lock = false\nf() diff --git a/test/libsolidity/smtCheckerTests/file_level/free_constant_1.sol b/test/libsolidity/smtCheckerTests/file_level/free_constant_1.sol new file mode 100644 index 000000000000..afb79abaa26c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_constant_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +uint constant A = 42; +contract C { + function f(uint x) public pure returns (uint) { + return x + A; + } +} +// ---- +// Warning 8195: (32-52): Model checker analysis was not possible because file level constants are not supported. +// Warning 8195: (32-52): Model checker analysis was not possible because file level constants are not supported. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_constant_2.sol b/test/libsolidity/smtCheckerTests/file_level/free_constant_2.sol new file mode 100644 index 000000000000..8a628541b88c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_constant_2.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; +uint256 constant x = 56; +enum ActionChoices {GoLeft, GoRight, GoStraight, Sit} +ActionChoices constant choices = ActionChoices.GoRight; +bytes32 constant st = "abc\x00\xff__"; +contract C { + function i() public returns (uint, ActionChoices, bytes32) { + return (x, choices, st); + } +} +// ---- +// Warning 2018: (220-310): Function state mutability can be restricted to pure +// Warning 8195: (32-55): Model checker analysis was not possible because file level constants are not supported. +// Warning 8195: (111-165): Model checker analysis was not possible because file level constants are not supported. +// Warning 8195: (167-204): Model checker analysis was not possible because file level constants are not supported. +// Warning 8195: (32-55): Model checker analysis was not possible because file level constants are not supported. +// Warning 8195: (111-165): Model checker analysis was not possible because file level constants are not supported. +// Warning 8195: (167-204): Model checker analysis was not possible because file level constants are not supported. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol new file mode 100644 index 000000000000..0295f070a9a2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_1.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C { + uint[] data; + function f(uint x, uint[] calldata input) public view returns (uint, uint) { + (uint a, uint[] calldata b) = fun(input, data); + return (a, b.length + x); + } +} +function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ---- +// Warning 6660: (220-334): Model checker analysis was not possible because file level functions are not supported. +// Warning 6660: (220-334): Model checker analysis was not possible because file level functions are not supported. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_2.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_2.sol new file mode 100644 index 000000000000..e480f121913f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_2.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract C { + function g() external { + f(); + } +} +function f() {} +// ---- +// Warning 6660: (82-97): Model checker analysis was not possible because file level functions are not supported. +// Warning 6660: (82-97): Model checker analysis was not possible because file level functions are not supported. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_3.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_3.sol new file mode 100644 index 000000000000..6b04e3550f71 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_3.sol @@ -0,0 +1,5 @@ +pragma experimental SMTChecker; +function f() view {} +// ---- +// Warning 6660: (32-52): Model checker analysis was not possible because file level functions are not supported. +// Warning 6660: (32-52): Model checker analysis was not possible because file level functions are not supported. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_4.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_4.sol new file mode 100644 index 000000000000..c2f284fda690 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_4.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +function f()pure { + ufixed a = uint64(1) + ufixed(2); +} +// ---- +// Warning 2072: (52-60): Unused local variable. +// Warning 6660: (32-87): Model checker analysis was not possible because file level functions are not supported. +// Warning 6660: (32-87): Model checker analysis was not possible because file level functions are not supported. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_5.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_5.sol new file mode 100644 index 000000000000..28e10360fbfb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_5.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract K {} +function f() pure { + (abi.encode, ""); +} +// ---- +// Warning 6133: (67-83): Statement has no effect. +// Warning 6660: (46-86): Model checker analysis was not possible because file level functions are not supported. +// Warning 6660: (46-86): Model checker analysis was not possible because file level functions are not supported. diff --git a/test/libsolidity/smtCheckerTests/file_level/free_function_and_constant_1.sol b/test/libsolidity/smtCheckerTests/file_level/free_function_and_constant_1.sol new file mode 100644 index 000000000000..c19514c2269a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/free_function_and_constant_1.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; +uint constant A = 42; +contract C { + uint[] data; + function f(uint x, uint[] calldata input) public view returns (uint, uint) { + (uint a, uint[] calldata b) = fun(input, data); + return (a, b.length + x + A); + } +} +function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ---- +// Warning 8195: (32-52): Model checker analysis was not possible because file level constants are not supported. +// Warning 6660: (246-360): Model checker analysis was not possible because file level functions are not supported. +// Warning 8195: (32-52): Model checker analysis was not possible because file level constants are not supported. +// Warning 6660: (246-360): Model checker analysis was not possible because file level functions are not supported. diff --git a/test/libsolidity/smtCheckerTests/function_selector/function_selector_via_contract_name.sol b/test/libsolidity/smtCheckerTests/function_selector/function_selector_via_contract_name.sol new file mode 100644 index 000000000000..a1690519de0c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/function_selector/function_selector_via_contract_name.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract A { + function f() external {} + function g(uint256) external {} +} +contract B { + function f() external returns (uint256) {} + function g(uint256) external returns (uint256) {} +} +contract C { + function test1() external pure returns(bytes4, bytes4, bytes4, bytes4) { + return (A.f.selector, A.g.selector, B.f.selector, B.g.selector); + } + function test2() external pure returns(bytes4, bytes4, bytes4, bytes4) { + A a; B b; + return (a.f.selector, a.g.selector, b.f.selector, b.g.selector); + } +} +// ==== diff --git a/test/libsolidity/smtCheckerTests/function_selector/function_types_sig.sol b/test/libsolidity/smtCheckerTests/function_selector/function_types_sig.sol new file mode 100644 index 000000000000..c498790b6a19 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/function_selector/function_types_sig.sol @@ -0,0 +1,33 @@ +pragma experimental SMTChecker; + +contract C { + uint256 public x; + + function f() public pure returns (bytes4) { + return this.f.selector; + } + + function g() public view returns (bytes4) { + function () pure external returns (bytes4) fun = this.f; + return fun.selector; + } + + function i() public pure returns (bytes4) { + return this.x.selector; + } + + function check() public view { + assert(f() == 0x26121ff0); + assert(g() == 0x26121ff0); + assert(i() == 0x0c55699c); + assert(i() == 0x26121ff0); + } +} +// ---- +// Warning 6031: (261-267): Internal error: Expression undefined for SMT solver. +// Warning 7650: (284-296): Assertion checker does not yet support this expression. +// Warning 6328: (470-495): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ncheck() +// Warning 6328: (540-565): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ncheck() +// Warning 6031: (261-267): Internal error: Expression undefined for SMT solver. +// Warning 7650: (284-296): Assertion checker does not yet support this expression. +// Warning 7650: (284-296): Assertion checker does not yet support this expression. diff --git a/test/libsolidity/smtCheckerTests/function_selector/homer.sol b/test/libsolidity/smtCheckerTests/function_selector/homer.sol new file mode 100644 index 000000000000..db186029d469 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/function_selector/homer.sol @@ -0,0 +1,46 @@ +pragma experimental SMTChecker; + +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +interface Simpson { + function is2D() external returns (bool); + function skinColor() external returns (string memory); +} + +interface PeaceMaker { + function achieveWorldPeace() external; +} + +contract Homer is ERC165, Simpson { + function supportsInterface(bytes4 interfaceID) public pure override returns (bool) { + return + interfaceID == this.supportsInterface.selector || // ERC165 + interfaceID == this.is2D.selector ^ this.skinColor.selector; // Simpson + } + + function is2D() external pure override returns (bool) { + return true; + } + + function skinColor() external pure override returns (string memory) { + return "yellow"; + } + + function check() public pure { + assert(supportsInterface(type(Simpson).interfaceId)); + assert(supportsInterface(type(ERC165).interfaceId)); + assert(supportsInterface(type(PeaceMaker).interfaceId)); + } +} + + +// ---- +// Warning 6328: (1373-1428): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ncheck() diff --git a/test/libsolidity/smtCheckerTests/function_selector/selector.sol b/test/libsolidity/smtCheckerTests/function_selector/selector.sol new file mode 100644 index 000000000000..b1339ed54106 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/function_selector/selector.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + assert(msg.sig == this.f.selector); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/function_selector/selector_2.sol b/test/libsolidity/smtCheckerTests/function_selector/selector_2.sol new file mode 100644 index 000000000000..d5f6d53d24e6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/function_selector/selector_2.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function g() external pure { + } + + function f() public pure { + assert(msg.sig == this.g.selector); + } +} +// ---- +// Warning 6328: (125-159): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/function_selector/selector_3.sol b/test/libsolidity/smtCheckerTests/function_selector/selector_3.sol new file mode 100644 index 000000000000..04861ed43b5b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/function_selector/selector_3.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + + +contract C { + int public x; + int public y; + + function f() public pure { + assert(this.x.selector != this.y.selector); + assert(this.x.selector == this.y.selector); + } +} +// ---- +// Warning 6328: (175-217): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol b/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol index d03cd4bb5fb0..3c7fc141e26c 100644 --- a/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol +++ b/test/libsolidity/smtCheckerTests/functions/abi_encode_functions.sol @@ -1,11 +1,11 @@ -pragma experimental SMTChecker;pragma experimental ABIEncoderV2; +pragma experimental SMTChecker;pragma abicoder v2; contract C { function f() public pure returns (bytes memory, bytes memory) { return (abi.encode(""), abi.encodePacked( "7?8r")); } } // ---- -// Warning 8364: (162-165): Assertion checker does not yet implement type abi // Warning 4588: (162-176): Assertion checker does not yet implement this type of function call. -// Warning 8364: (178-181): Assertion checker does not yet implement type abi +// Warning 4588: (178-203): Assertion checker does not yet implement this type of function call. +// Warning 4588: (162-176): Assertion checker does not yet implement this type of function call. // Warning 4588: (178-203): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol index f2d408cd6354..56f6a727f2a8 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy.sol @@ -13,4 +13,4 @@ contract A is C { } } // ---- -// Warning 6328: (152-166): Assertion violation happens here +// Warning 6328: (152-166): CHC: Assertion violation happens here.\nCounterexample:\na = 2\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol index 88c7722e4756..7591daf7a434 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_2.sol @@ -4,4 +4,4 @@ contract A is C { constructor() C(2) { assert(a == 2); } } contract B is C { constructor() C(3) { assert(a == 3); } } contract J is C { constructor() C(3) { assert(a == 4); } } // ---- -// Warning 6328: (243-257): Assertion violation happens here +// Warning 6328: (243-257): CHC: Assertion violation happens here.\nCounterexample:\na = 3\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol index b5017761947f..23889a3e77e0 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_3.sol @@ -18,7 +18,9 @@ contract A is B { assert(a == x + 1); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (232-250): Assertion violation happens here -// Warning 2661: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (244-249): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (244-249): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (232-250): CHC: Assertion violation happens here. +// Warning 4984: (203-208): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol index c2466c0d1eea..2af4d46697ff 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_4.sol @@ -18,6 +18,6 @@ contract A is B { } } // ---- -// Warning 2661: (207-212): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (198-203): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (230-235): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (230-235): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (207-212): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) +// Warning 4984: (198-203): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol index 29c2d41b2eef..09cad9885937 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond.sol @@ -24,8 +24,9 @@ contract A is B2, B1 { assert(a == x + 1); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (302-320): Assertion violation happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (314-319): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (200-205): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (314-319): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (302-320): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol index 83cb5dc64c08..1b02db6947c1 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_2.sol @@ -25,7 +25,6 @@ contract A is B2, B1 { } } // ---- -// Warning 6328: (302-320): Assertion violation happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (200-205): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (314-319): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (200-205): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639934\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639934) +// Warning 4984: (314-319): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (302-320): CHC: Assertion violation happens here.\nCounterexample:\na = 0\nx = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol index cdd479268fa7..c8412f96dbdf 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_3.sol @@ -27,11 +27,7 @@ contract A is B2, B1 { } } // ---- -// Warning 6328: (334-350): Assertion violation happens here -// Warning 4144: (160-165): Underflow (resulting value less than 0) happens here -// Warning 2661: (160-165): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (225-230): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (241-246): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (160-165): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (160-165): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nb1 = 0, a = 115792089237316195423570985008687907853269984665640564039457584007913129639935\nx = 1\n\n\nTransaction trace:\nconstructor(1) +// Warning 4984: (241-246): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nb2 = 0, a = 1\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) +// Warning 4984: (225-230): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nb2 = 0, a = 0\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639934\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639934) +// Warning 6328: (334-350): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol index 64f0482f8e85..8ec4c327d49c 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_diamond_empty_middle.sol @@ -20,4 +20,4 @@ contract A is B, B2 { } // ---- // Warning 5667: (164-170): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (194-208): Assertion violation happens here +// Warning 6328: (194-208): CHC: Assertion violation happens here.\nCounterexample:\na = 2\nx = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol index d694b191ae3e..f53857f6ab0a 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_chain.sol @@ -19,4 +19,4 @@ contract A is B { } // ---- // Warning 5667: (194-200): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (224-238): Assertion violation happens here +// Warning 6328: (224-238): CHC: Assertion violation happens here.\nCounterexample:\na = 2\nx = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol index c89cad884fb3..f11aa2f8d3fa 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle.sol @@ -17,4 +17,4 @@ contract A is B { } // ---- // Warning 5667: (138-144): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (172-186): Assertion violation happens here +// Warning 6328: (172-186): CHC: Assertion violation happens here.\nCounterexample:\na = 2\nx = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol index a5ac97344587..2a242982519e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_empty_middle_no_invocation.sol @@ -16,4 +16,4 @@ contract A is B { } // ---- // Warning 5667: (138-144): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (150-164): Assertion violation happens here +// Warning 6328: (150-164): CHC: Assertion violation happens here.\nCounterexample:\na = 2\nx = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol index a5962165abdc..36fd55d47756 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain.sol @@ -27,4 +27,4 @@ contract A is B { } // ---- // Warning 5667: (254-260): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (284-298): Assertion violation happens here +// Warning 6328: (284-298): CHC: Assertion violation happens here.\nCounterexample:\na = 4\nx = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol index 72f849db3372..5541b64ee571 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_local_vars.sol @@ -32,4 +32,4 @@ contract A is B { } // ---- // Warning 5667: (296-302): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (357-372): Assertion violation happens here +// Warning 6328: (357-372): CHC: Assertion violation happens here.\nCounterexample:\na = 4\nx = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol index 223703be4607..61a77682a325 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params.sol @@ -24,7 +24,8 @@ contract A is B { assert(a == 4); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (328-342): Assertion violation happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (247-252): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (328-342): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol index 685be3b96ed8..4cd07eeaf70e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_mixed_chain_with_params_2.sol @@ -23,4 +23,4 @@ contract B is C { contract A is B { } // ---- -// Warning 6328: (266-280): Assertion violation happens here +// Warning 6328: (266-280): CHC: Assertion violation happens here.\nCounterexample:\na = 3\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol index 8a67b7a8b151..92d861889848 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_modifier.sol @@ -14,4 +14,4 @@ contract A is C { } } // ---- -// Warning 6328: (188-202): Assertion violation happens here +// Warning 6328: (188-202): CHC: Assertion violation happens here.\nCounterexample:\na = 7\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol index f91439b7775c..052b5ce0cf6e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_hierarchy_same_var.sol @@ -13,5 +13,5 @@ contract A is C { } } // ---- -// Warning 6328: (134-148): Assertion violation happens here -// Warning 6328: (152-168): Assertion violation happens here +// Warning 6328: (134-148): CHC: Assertion violation happens here.\nCounterexample:\na = 2\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (152-168): CHC: Assertion violation happens here.\nCounterexample:\na = 2\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_simple.sol b/test/libsolidity/smtCheckerTests/functions/constructor_simple.sol index 79fbb271429a..0870ba49673e 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_simple.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_simple.sol @@ -12,5 +12,7 @@ contract C { assert(y == x); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (141-155): Assertion violation happens here +// Warning 6328: (141-155): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol index e674dc70dd7c..8c6ee55171d4 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value.sol @@ -13,4 +13,4 @@ contract C { } } // ---- -// Warning 6328: (145-159): Assertion violation happens here +// Warning 6328: (145-159): CHC: Assertion violation happens here.\nCounterexample:\nx = 10\ny = 11\n\n\nTransaction trace:\nconstructor()\nState: x = 10\nf(11) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol index e3b765264925..100183012536 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_inherited.sol @@ -15,4 +15,4 @@ contract C is B { } } // ---- -// Warning 6328: (165-179): Assertion violation happens here +// Warning 6328: (165-179): CHC: Assertion violation happens here.\nCounterexample:\nx = 10\ny = 9\n\n\nTransaction trace:\nconstructor()\nState: x = 10\nf(9) diff --git a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol index 106e60440960..faee3d0cf6df 100644 --- a/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol +++ b/test/libsolidity/smtCheckerTests/functions/constructor_state_value_parameter.sol @@ -13,5 +13,5 @@ contract C { } } // ---- -// Warning 6328: (162-176): Assertion violation happens here -// Warning 2661: (115-120): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (162-176): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\ny = 0\n\n\nTransaction trace:\nconstructor(1, 0)\nState: x = 1\nf(0) +// Warning 4984: (115-120): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 5\na = 1\nb = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/functions/function_call_state_var_init.sol b/test/libsolidity/smtCheckerTests/functions/function_call_state_var_init.sol index ba8b2638eedf..2151b1288f0b 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_call_state_var_init.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_call_state_var_init.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (116-132): Assertion violation happens here +// Warning 6328: (116-132): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_1.sol b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_1.sol new file mode 100644 index 000000000000..2bacad7e2cf1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_1.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; +contract State { + C c; + function f() public returns (uint) { + while(true) + c.setOwner(); + } +} +contract C { + address owner; + function setOwner() public { + owner = address(0); + } +} +// ---- +// Warning 6321: (85-89): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_2.sol b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_2.sol new file mode 100644 index 000000000000..897367d48854 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/function_external_call_should_not_inline_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract Other { + C c; + function h(bool b) public { + if (b) + c.setOwner(address(0)); + } +} +contract C { + address owner; + function setOwner(address _owner) public { + owner = _owner; + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var.sol b/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var.sol index db3e7918c7d7..f96145adf38b 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var.sol @@ -16,4 +16,4 @@ contract C } } // ---- -// Warning 6328: (209-223): Assertion violation happens here +// Warning 6328: (209-223): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\nb = true\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(true) diff --git a/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var_3.sol b/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var_3.sol index 9a1aac540475..772287456232 100644 --- a/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/function_inside_branch_modify_state_var_3.sol @@ -24,5 +24,5 @@ contract C } // ---- -// Warning 6328: (209-223): Assertion violation happens here -// Warning 6328: (321-335): Assertion violation happens here +// Warning 6328: (209-223): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\nb = true\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(true) +// Warning 6328: (321-335): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\nb = false\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nh(false) diff --git a/test/libsolidity/smtCheckerTests/functions/functions_bound_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_bound_1_fail.sol index 5c4d16cfd945..82b9171f941c 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_bound_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_bound_1_fail.sol @@ -18,4 +18,4 @@ contract C } } // ---- -// Warning 6328: (261-277): Assertion violation happens here +// Warning 6328: (261-277): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 1\n\n\nTransaction trace:\nconstructor()\nf(1) diff --git a/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol index 8da1b5e169aa..b6ec5b8b1f91 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol @@ -17,4 +17,5 @@ contract C } } // ---- -// Warning 4661: (297-321): Assertion violation happens here +// Warning 6328: (297-321): CHC: Assertion violation might happen here. +// Warning 4661: (297-321): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_external_4.sol b/test/libsolidity/smtCheckerTests/functions/functions_external_4.sol index c75214b4fafe..2b0eaa86e457 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_external_4.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_external_4.sol @@ -16,4 +16,4 @@ contract D } } // ---- -// Warning 6328: (191-206): Assertion violation happens here +// Warning 6328: (191-206): CHC: Assertion violation happens here.\nCounterexample:\nc = 0\n_y = 0\n\n\nTransaction trace:\nconstructor()\nState: c = 0\ng(0) diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identifier_nested_tuple_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_identifier_nested_tuple_1.sol new file mode 100644 index 000000000000..a329120cce76 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identifier_nested_tuple_1.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + uint x; + function f() public { + x = 0; + ((inc))(); + assert(x == 1); // should hold + } + + function inc() internal returns (uint) { + require(x < 100); + return ++x; + } +} diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identifier_nested_tuple_2.sol b/test/libsolidity/smtCheckerTests/functions/functions_identifier_nested_tuple_2.sol new file mode 100644 index 000000000000..9664722ee887 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identifier_nested_tuple_2.sol @@ -0,0 +1,23 @@ +pragma experimental SMTChecker; + +library L { + struct S { + uint256[] data; + } + function f(S memory _s) internal pure returns (uint256) { + require(_s.data.length > 0); + return 42; + } +} + +contract C { + using L for L.S; + function f() public pure returns (uint256 y) { + L.S memory x; + y = (x.f)(); + assert(y == 42); // should hold + } +} +// ---- +// Warning 6031: (289-292): Internal error: Expression undefined for SMT solver. +// Warning 6031: (289-292): Internal error: Expression undefined for SMT solver. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_1_fail.sol index 4931b8fec3c9..90fa3a452c02 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_identity_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_1_fail.sol @@ -12,4 +12,4 @@ contract C } // ---- -// Warning 6328: (161-174): Assertion violation happens here +// Warning 6328: (161-174): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_2_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_2_fail.sol index 29b422c7dce2..5ad96ae9c776 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_identity_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_2_fail.sol @@ -16,4 +16,4 @@ contract C } // ---- -// Warning 6328: (229-242): Assertion violation happens here +// Warning 6328: (229-242): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol index 87a027a9cac1..061e1c688119 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol @@ -12,4 +12,4 @@ contract C } // ---- -// Warning 6328: (163-176): Assertion violation happens here +// Warning 6328: (163-176): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol index 04a66621991f..ff33b287744d 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1.sol @@ -17,4 +17,3 @@ contract C } } // ---- -// Warning 8364: (228-229): Assertion checker does not yet implement type type(library L) diff --git a/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol index 3c35898a231f..5b8c74f68d4e 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_library_1_fail.sol @@ -17,5 +17,4 @@ contract C } } // ---- -// Warning 6328: (245-261): Assertion violation happens here -// Warning 8364: (228-229): Assertion checker does not yet implement type type(library L) +// Warning 6328: (245-261): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 1\n\n\nTransaction trace:\nconstructor()\nf(1) diff --git a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1_fail.sol index 93fa49176e7c..a4df06cfdeeb 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1_fail.sol @@ -13,4 +13,4 @@ contract C } // ---- -// Warning 6328: (144-157): Assertion violation happens here +// Warning 6328: (144-157): CHC: Assertion violation happens here.\nCounterexample:\na = 0\n\n\n\nTransaction trace:\nconstructor()\nState: a = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2_fail.sol index 5a79201223ca..0adc0a97dadd 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2_fail.sol @@ -14,4 +14,4 @@ contract C } // ---- -// Warning 6328: (152-165): Assertion violation happens here +// Warning 6328: (152-165): CHC: Assertion violation happens here.\nCounterexample:\na = 0\n\n\n\nTransaction trace:\nconstructor()\nState: a = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for.sol index ff2c68ef5256..23b6cf8b4dda 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for.sol @@ -5,4 +5,4 @@ contract C function f(bool x) public pure { require(x); for (;x;) {} } } // ---- -// Warning 6838: (98-99): Condition is always true. +// Warning 6838: (98-99): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_if.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_if.sol index 619b0d5f825b..ea2093fa4f14 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_if.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_if.sol @@ -4,4 +4,4 @@ contract C function f(bool x) public pure { require(x); if (x) {} } } // ---- -// Warning 6838: (95-96): Condition is always true. +// Warning 6838: (95-96): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require.sol index 86dfba66dbdd..460b7da79fa8 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require.sol @@ -5,4 +5,4 @@ contract C function f(bool x) public pure { x = true; require(x); } } // ---- -// Warning 6838: (98-99): Condition is always true. +// Warning 6838: (98-99): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while.sol index 3ba28289d448..3899d35d741c 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while.sol @@ -5,4 +5,4 @@ contract C function f(bool x) public pure { require(x); while (x) {} } } // ---- -// Warning 6838: (99-100): Condition is always true. +// Warning 6838: (99-100): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/address.sol b/test/libsolidity/smtCheckerTests/functions/getters/address.sol new file mode 100644 index 000000000000..89b36e446f34 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/address.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + address public x; + address payable public y; + + function f() public view { + address a = this.x(); + address b = this.y(); + assert(a == x); // should hold + assert(a == address(this)); // should fail + assert(b == y); // should hold + assert(y == address(this)); // should fail + } +} +// ---- +// Warning 6328: (204-230): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\nf() +// Warning 6328: (282-308): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/array_1.sol b/test/libsolidity/smtCheckerTests/functions/getters/array_1.sol new file mode 100644 index 000000000000..8e3dd59ee5ce --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/array_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + uint[] public a; + + function f() public view { + uint y = this.a(2); + assert(y == a[2]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (153-167): CHC: Assertion violation happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/array_2.sol b/test/libsolidity/smtCheckerTests/functions/getters/array_2.sol new file mode 100644 index 000000000000..81d23d974fb9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/array_2.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + uint[][] public a; + + function f() public view { + uint y = this.a(2,3); + assert(y == a[2][3]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (160-174): CHC: Assertion violation happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol b/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol new file mode 100644 index 000000000000..b760cfe21f45 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/bytes.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + bytes public str2 = 'c'; + + function f() public view { + bytes memory a2 = this.str2(); + assert(keccak256(a2) == keccak256(str2)); // should hold + assert(keccak256(a2) == keccak256('a')); // should fail + } +} +// ---- +// Warning 6328: (195-234): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/contract.sol b/test/libsolidity/smtCheckerTests/functions/getters/contract.sol new file mode 100644 index 000000000000..93d5c837390c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/contract.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract D {} + +contract C { + D public d; + + function f() public view { + D e = this.d(); + assert(e == d); // should hold + assert(address(e) == address(this)); // should fail + } +} +// ---- +// Warning 6328: (156-191): CHC: Assertion violation happens here.\nCounterexample:\nd = 0\n\n\n\nTransaction trace:\nconstructor()\nState: d = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/double_access.sol b/test/libsolidity/smtCheckerTests/functions/getters/double_access.sol new file mode 100644 index 000000000000..1e2977f850a4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/double_access.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint u; + } + + S public s; + + function f() public view { + uint u = this.s(); + uint v = this.s(); + assert(u == s.u); // should hold + assert(u == v); // should hold + assert(u == 1); // should fail + } +} +// ---- +// Warning 6328: (226-240): CHC: Assertion violation happens here.\nCounterexample:\ns = {u: 0}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {u: 0}\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/enum.sol b/test/libsolidity/smtCheckerTests/functions/getters/enum.sol new file mode 100644 index 000000000000..c0bf70ec5dc0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/enum.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + + +contract C { + enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } + ActionChoices public choice; + + function f() public view { + ActionChoices e = this.choice(); + assert(e == choice); // should hold + assert(e == ActionChoices.SitStill); // should fail + } +} +// ---- +// Warning 6328: (243-278): CHC: Assertion violation happens here.\nCounterexample:\nchoice = 0\n\n\n\nTransaction trace:\nconstructor()\nState: choice = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/fixed_bytes.sol b/test/libsolidity/smtCheckerTests/functions/getters/fixed_bytes.sol new file mode 100644 index 000000000000..ca40a5733fef --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/fixed_bytes.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + bytes1 public x; + bytes3 public y; + + function f() public view { + bytes1 a = this.x(); + bytes3 b = this.y(); + assert(a == x); // should hold + assert(a == 'a'); // should fail + assert(b == y); // should hold + assert(y == "abc"); // should fail + } +} +// ---- +// Warning 6328: (192-208): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\nf() +// Warning 6328: (260-278): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/function.sol b/test/libsolidity/smtCheckerTests/functions/getters/function.sol new file mode 100644 index 000000000000..58c9a4c2ecad --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/function.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function () external returns (uint) public g; + + function f() public { + g = this.X; + function () external returns (uint) e = this.g(); + assert(e() == g()); // should hold, but fails because of the lack of support for tracking function pointers + assert(e() == 1); // should fail + } + + function X() public pure returns (uint) { + return 42; + } +} +// ---- +// Warning 6328: (185-203): CHC: Assertion violation happens here.\nCounterexample:\ng = 0\n\n\n\nTransaction trace:\nconstructor()\nState: g = 0\nf() +// Warning 6328: (295-311): CHC: Assertion violation happens here.\nCounterexample:\ng = 0\n\n\n\nTransaction trace:\nconstructor()\nState: g = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/mapping_1.sol b/test/libsolidity/smtCheckerTests/functions/getters/mapping_1.sol new file mode 100644 index 000000000000..8b3d6fc0262e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/mapping_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint) public map; + + function f() public view { + uint y = this.map(2); + assert(y == map[2]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (175-189): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/mapping_2.sol b/test/libsolidity/smtCheckerTests/functions/getters/mapping_2.sol new file mode 100644 index 000000000000..a7b99f816548 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/mapping_2.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => mapping (uint => uint)) public map; + + function f() public view { + uint y = this.map(2, 3); + assert(y == map[2][3]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (199-213): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/mapping_with_cast.sol b/test/libsolidity/smtCheckerTests/functions/getters/mapping_with_cast.sol new file mode 100644 index 000000000000..4fb02cd7adbd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/mapping_with_cast.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + mapping (bytes16 => uint) public m; + + function f() public view { + uint y = this.m("foo"); + assert(y == m["foo"]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (180-194): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_1.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_1.sol new file mode 100644 index 000000000000..999df84444bd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_1.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint[]) public m; + + constructor() { + m[0].push(); + m[0].push(); + m[0][1] = 42; + } + + function f() public view { + uint y = this.m(0,1); + assert(y == m[0][1]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (243-257): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_10.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_10.sol new file mode 100644 index 000000000000..c08fe84f83ab --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_10.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint[])[] public m; + + constructor() { + m.push(); + m[0][1].push(); + m[0][1].push(); + m[0][1].push(); + m[0][1][2] = 42; + } + + function f() public view { + uint y = this.m(0,1,2); + assert(y == m[0][1][2]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (289-303): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_2.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_2.sol new file mode 100644 index 000000000000..412c70e54410 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_2.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint[][]) public m; + + constructor() { + m[0].push(); + m[0].push(); + m[0][1].push(); + m[0][1].push(); + m[0][1].push(); + m[0][1][2] = 42; + } + + function f() public view { + uint y = this.m(0,1,2); + assert(y == m[0][1][2]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (307-321): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_3.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_3.sol new file mode 100644 index 000000000000..404c1ee9bcf3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_3.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint[][][]) public m; + + constructor() { + m[0].push(); + m[0].push(); + m[0][1].push(); + m[0][1].push(); + m[0][1].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2][3] = 42; + } + + function f() public view { + uint y = this.m(0,1,2,3); + assert(y == m[0][1][2][3]); // should hold + // Disabled because of Spacer seg fault + //assert(y == 1); // should fail + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_4.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_4.sol new file mode 100644 index 000000000000..2dc53b7e5886 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_4.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => mapping (uint => uint[])) public m; + + constructor() { + m[0][1].push(); + m[0][1].push(); + m[0][1].push(); + m[0][1][2] = 42; + } + + function f() public view { + uint y = this.m(0,1,2); + assert(y == m[0][1][2]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (293-307): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_5.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_5.sol new file mode 100644 index 000000000000..5fbc0ceb83a7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_5.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => mapping (uint => uint[][])) public m; + + constructor() { + m[0][1].push(); + m[0][1].push(); + m[0][1].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2][3] = 42; + } + + function f() public view { + uint y = this.m(0,1,2,3); + assert(y == m[0][1][2][3]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (387-401): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_6.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_6.sol new file mode 100644 index 000000000000..336fad8d5264 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_6.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => mapping (uint => mapping (uint => uint[]))) public m; + + constructor() { + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2].push(); + m[0][1][2][3] = 42; + } + + function f() public view { + uint y = this.m(0,1,2,3); + assert(y == m[0][1][2][3]); // should hold + // Disabled because Spacer seg faults + //assert(y == 1); // should fail + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_7.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_7.sol new file mode 100644 index 000000000000..6699b1b3bf55 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_7.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint)[] public m; + + constructor() { + m.push(); + m[0][1] = 42; + } + + function f() public view { + uint y = this.m(0,1); + assert(y == m[0][1]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (225-239): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_8.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_8.sol new file mode 100644 index 000000000000..7d79be24d202 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_8.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => uint)[][] public m; + + constructor() { + m.push(); + m[0].push(); + m[0].push(); + m[0][1][2] = 42; + } + + function f() public view { + uint y = this.m(0,1,2); + assert(y == m[0][1][2]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (265-279): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_9.sol b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_9.sol new file mode 100644 index 000000000000..c7120cc3e5f3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/nested_arrays_mappings_9.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + mapping (uint => mapping (uint => uint))[] public m; + + constructor() { + m.push(); + m[0][1][2] = 42; + } + + function f() public view { + uint y = this.m(0,1,2); + assert(y == m[0][1][2]); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (251-265): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/static_array.sol b/test/libsolidity/smtCheckerTests/functions/getters/static_array.sol new file mode 100644 index 000000000000..607852c42072 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/static_array.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + + +contract C { + + uint[2] public x = [42,1]; + + function f() public view { + assert(this.x(0) == x[0]); // should hold + assert(this.x(1) == x[1]); // should hold + assert(this.x(0) == 0); // should fail + } +} +// ---- +// Warning 6328: (195-217): CHC: Assertion violation happens here.\nCounterexample:\nx = [42, 1]\n\n\n\nTransaction trace:\nconstructor()\nState: x = [42, 1]\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/string.sol b/test/libsolidity/smtCheckerTests/functions/getters/string.sol new file mode 100644 index 000000000000..6ed52b5a6330 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/string.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + string public str1 = 'b'; + + function f() public view { + string memory a1 = this.str1(); + assert(keccak256(bytes(a1)) == keccak256(bytes(str1))); // should hold + assert(keccak256(bytes(a1)) == keccak256('a')); // should fail + } +} +// ---- +// Warning 6328: (211-257): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/struct_1.sol b/test/libsolidity/smtCheckerTests/functions/getters/struct_1.sol new file mode 100644 index 000000000000..328cf797c5ab --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/struct_1.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct T { + uint t; + } + struct S { + uint x; + T t; + bool b; + uint[] a; + } + + S public s; + + function f() public view { + uint y; + bool c; + T memory t; + (y,t,c) = this.s(); + assert(y == s.x); // this should hold + assert(c == s.b); // this should hold + assert(t.t == s.t.t); // this should hold + assert(c == true); // this should fail + } +} +// ---- +// Warning 6328: (370-387): CHC: Assertion violation happens here.\nCounterexample:\ns = {x: 0, t: {t: 0}, b: false, a: []}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 0, t: {t: 0}, b: false, a: []}\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/struct_2.sol b/test/libsolidity/smtCheckerTests/functions/getters/struct_2.sol new file mode 100644 index 000000000000..5a47d0d77514 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/struct_2.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; +//pragma abicoder v2; + +contract C { + struct S { + uint[2] a; + uint u; + } + + S public s; + + function f() public view { + uint u = this.s(); + assert(u == s.u); // should hold + assert(u == 1); // should fail + } +} +// ---- +// Warning 6328: (207-221): CHC: Assertion violation happens here.\nCounterexample:\ns = {a: [0, 0], u: 0}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {a: [0, 0], u: 0}\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/struct_3.sol b/test/libsolidity/smtCheckerTests/functions/getters/struct_3.sol new file mode 100644 index 000000000000..d2f78d3332f3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/struct_3.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + string s; + bytes b; + } + + S public m; + + constructor() { + m.s = "foo"; + m.b = "bar"; + } + + function f() public view { + (string memory s, bytes memory b) = this.m(); + assert(keccak256(bytes(s)) == keccak256(bytes(m.s))); // should hold + assert(b[0] == m.b[0]); // should hold + assert(b[0] == "t"); // should fail + } +} +// ---- +// Warning 6328: (340-359): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/struct_4.sol b/test/libsolidity/smtCheckerTests/functions/getters/struct_4.sol new file mode 100644 index 000000000000..6a70d9583ba2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/struct_4.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract D { +} + +contract C { + struct S { + D d; + function () external returns (uint) f; + } + + S public s; + + function test() public view { + (D d, function () external returns (uint) f) = this.s(); + assert(d == s.d); // should hold + assert(address(d) == address(this)); // should fail + } +} +// ---- +// Warning 2072: (179-216): Unused local variable. +// Warning 6328: (267-302): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 0, f: 0}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {d: 0, f: 0}\ntest() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/struct_with_reassignment.sol b/test/libsolidity/smtCheckerTests/functions/getters/struct_with_reassignment.sol new file mode 100644 index 000000000000..dde5781a698a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/struct_with_reassignment.sol @@ -0,0 +1,32 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + bool b; + } + + S public s; + + constructor() { + s.x = 1; + s.b = false; + } + + function f() public { + uint x; + bool b; + (x,b) = this.s(); + assert(x == s.x); // this should hold + assert(b == s.b); // this should hold + assert(b == true); // this should fail + s.x = 42; + (uint y, bool c) = this.s(); + assert(c == b); // this should hold + assert(y == x); // this should fail + + } +} +// ---- +// Warning 6328: (288-305): CHC: Assertion violation happens here.\nCounterexample:\ns = {x: 1, b: false}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 1, b: false}\nf() +// Warning 6328: (410-424): CHC: Assertion violation happens here.\nCounterexample:\ns = {x: 42, b: false}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 1, b: false}\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/uint.sol b/test/libsolidity/smtCheckerTests/functions/getters/uint.sol new file mode 100644 index 000000000000..7a5cb426b2e2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/getters/uint.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + uint public x; + + function f() public view { + uint y = this.x(); + assert(y == x); // should hold + assert(y == 1); // should fail + } +} +// ---- +// Warning 6328: (147-161): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol index fe2e882a0989..1b25e07be0d2 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol @@ -17,4 +17,4 @@ contract A is B { } } // ---- -// Warning 6328: (254-268): Assertion violation happens here +// Warning 6328: (254-268): CHC: Assertion violation happens here.\nCounterexample:\nx = 42\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\na() diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance_2.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance_2.sol deleted file mode 100644 index c57fd1073af3..000000000000 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance_2.sol +++ /dev/null @@ -1,24 +0,0 @@ -pragma experimental SMTChecker; - -contract C { - uint y; - function c(uint _y) public returns (uint) { - y = _y; - return y; - } -} - -contract B is C { - function b() public returns (uint) { return c(42); } -} - -contract A is B { - uint public x; - - function a() public { - x = b(); - assert(x < 40); - } -} -// ---- -// Warning 6328: (274-288): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol index 3439ec6d43a7..5c94cd9ed024 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_state_var_init_2.sol @@ -7,6 +7,4 @@ contract c { bool b = (f() > 0) || (f() > 0); } // ---- -// Warning 2661: (100-105): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4144: (100-105): Underflow (resulting value less than 0) happens here -// Warning 2661: (100-105): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6321: (86-90): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol index 05d965aa3e50..52dcfb1ff003 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1.sol @@ -21,5 +21,3 @@ contract C{ } // ---- // Warning 5667: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 2661: (156-159): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4144: (238-241): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol index 33fc53e4fc4b..a1e245fd9863 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_1_fail.sol @@ -21,10 +21,8 @@ contract C{ } // ---- // Warning 5667: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (138-152): Assertion violation happens here -// Warning 6328: (170-184): Assertion violation happens here -// Warning 6328: (220-234): Assertion violation happens here -// Warning 6328: (245-259): Assertion violation happens here -// Warning 6328: (82-96): Assertion violation happens here -// Warning 2661: (156-159): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4144: (238-241): Underflow (resulting value less than 0) happens here +// Warning 6328: (138-152): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor(0)\nState: x = 1\nf() +// Warning 6328: (170-184): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor(0)\nState: x = 1\nf() +// Warning 6328: (220-234): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n\n\nTransaction trace:\nconstructor(0)\nState: x = 1\nf() +// Warning 6328: (245-259): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor(0)\nState: x = 1\nf() +// Warning 6328: (82-96): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol index 067ce603b23c..80f6d6304e95 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1.sol @@ -17,5 +17,3 @@ contract C is A { } } // ---- -// Warning 4144: (100-103): Underflow (resulting value less than 0) happens here -// Warning 4144: (100-103): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol index d07c1146566e..3052258fcb17 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_with_assertion_inheritance_1_fail.sol @@ -17,8 +17,6 @@ contract C is A { } } // ---- -// Warning 6328: (82-96): Assertion violation happens here -// Warning 6328: (148-162): Assertion violation happens here -// Warning 6328: (180-194): Assertion violation happens here -// Warning 4144: (100-103): Underflow (resulting value less than 0) happens here -// Warning 4144: (100-103): Underflow (resulting value less than 0) happens here +// Warning 6328: (82-96): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (148-162): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (180-194): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol index f8ec40ab7500..f23217cdeb3e 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1.sol @@ -21,7 +21,3 @@ contract C{ } // ---- // Warning 5667: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 2661: (156-159): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (234-237): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4144: (234-237): Underflow (resulting value less than 0) happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol index 2c86472897de..81ba3329550c 100644 --- a/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/functions/internal_multiple_calls_with_assertion_1_fail.sol @@ -21,10 +21,6 @@ contract C{ } // ---- // Warning 5667: (70-76): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (138-152): Assertion violation happens here -// Warning 6328: (184-198): Assertion violation happens here -// Warning 6328: (82-96): Assertion violation happens here -// Warning 2661: (156-159): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (163-166): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (234-237): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4144: (234-237): Underflow (resulting value less than 0) happens here +// Warning 6328: (138-152): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor(0)\nState: x = 1\nf() +// Warning 6328: (184-198): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor(0)\nState: x = 1\nf() +// Warning 6328: (82-96): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol index c621041dd7f3..0d3efa6f660b 100644 --- a/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol +++ b/test/libsolidity/smtCheckerTests/functions/library_after_contract.sol @@ -15,4 +15,3 @@ library L { // ---- // Warning 2018: (131-190): Function state mutability can be restricted to pure -// Warning 8364: (86-87): Assertion checker does not yet implement type type(library L) diff --git a/test/libsolidity/smtCheckerTests/functions/library_constant.sol b/test/libsolidity/smtCheckerTests/functions/library_constant.sol index ee521a1810a1..4f0e341ac217 100644 --- a/test/libsolidity/smtCheckerTests/functions/library_constant.sol +++ b/test/libsolidity/smtCheckerTests/functions/library_constant.sol @@ -19,8 +19,6 @@ contract C { } } // ---- -// Warning 6328: (136-155): Assertion violation happens here -// Warning 2661: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 8364: (300-302): Assertion checker does not yet implement type type(library l1) -// Warning 2661: (229-234): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (327-332): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (136-155): CHC: Assertion violation happens here.\nCounterexample:\nTON = 1000\n\n\n\nTransaction trace:\nconstructor()\nState: TON = 1000\nf1() +// Warning 4984: (229-234): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor()\nf(115792089237316195423570985008687907853269984665640564039457584007913129639935) +// Warning 4984: (327-332): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor()\nf(115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol index 0c2a781fca9d..d0d52c937095 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return.sol @@ -9,3 +9,5 @@ contract C { } // // ---- +// Warning 6321: (81-85): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (87-91): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol index cb4773841d24..b01d8cb05932 100644 --- a/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/recursive_multi_return_2.sol @@ -23,5 +23,18 @@ a; } } // ---- +// Warning 6321: (163-167): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (171-175): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (179-183): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (187-191): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (195-199): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (203-207): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (211-215): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (219-223): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (227-231): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (235-239): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (241-244): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (246-250): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (252-259): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. // Warning 6133: (72-90): Statement has no effect. // Warning 6133: (96-107): Statement has no effect. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call.sol index 384281e8ed2d..de69baf53d81 100644 --- a/test/libsolidity/smtCheckerTests/functions/this_external_call.sol +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call.sol @@ -9,9 +9,7 @@ contract C function g(uint y) public { require(y < 1000); this.f(y); - // Fails as false positive because CHC does not support `this`. assert(x < 1000); } } // ---- -// Warning 6328: (227-243): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_2.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_2.sol new file mode 100644 index 000000000000..444e88f7d5c2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_2.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint a; + function f(uint x) public { + this.g(x); + assert(a == x); + assert(a != 42); + } + + function g(uint x) public { + a = x; + } +} +// ---- +// Warning 6328: (141-156): CHC: Assertion violation happens here.\nCounterexample:\na = 42\nx = 42\n\n\nTransaction trace:\nconstructor()\nState: a = 0\nf(42) diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol index b43b95e7910d..cb94af86731c 100644 --- a/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_return.sol @@ -10,9 +10,7 @@ contract C function g(uint y) public { require(y < 1000); uint z = this.f(y); - // Fails as false positive because CHC does not support `this`. assert(z < 1000); } } // ---- -// Warning 6328: (263-279): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_sender.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_sender.sol new file mode 100644 index 000000000000..e8bfe663b584 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_sender.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C { + address lastCaller; + + constructor() { + lastCaller = msg.sender; + } + + modifier log { + lastCaller = msg.sender; + _; + } + + function test() log public { + assert(lastCaller == msg.sender); + this.g(); + assert(lastCaller == address(this)); + assert(lastCaller == msg.sender); + assert(lastCaller == address(0)); + } + + function g() log public { + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (347-379): CHC: Assertion violation happens here. +// Warning 6328: (389-421): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/functions/this_external_call_tx_origin.sol b/test/libsolidity/smtCheckerTests/functions/this_external_call_tx_origin.sol new file mode 100644 index 000000000000..04dd5599dc8d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/this_external_call_tx_origin.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + + function test() view public { + require(address(this) != tx.origin); + assert(!this.g()); + } + + function g() view public returns (bool) { + return msg.sender == tx.origin; + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/functions/this_fake.sol b/test/libsolidity/smtCheckerTests/functions/this_fake.sol deleted file mode 100644 index 2227e9b3fdf0..000000000000 --- a/test/libsolidity/smtCheckerTests/functions/this_fake.sol +++ /dev/null @@ -1,24 +0,0 @@ -pragma experimental SMTChecker; - -contract C -{ - uint public x; - C c; - function f(C _c) public { - c = _c; - } - function g() public { - C this = c; - x = 0; - this.h(); - // State knowledge is erased. - // Function call is not inlined. - assert(x == 0); - } - function h() public { - x = 2; - } -} -// ---- -// Warning 2319: (160-166): This declaration shadows a builtin symbol. -// Warning 6328: (268-282): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/this_state.sol b/test/libsolidity/smtCheckerTests/functions/this_state.sol index b21c71972e4a..9f417380e4f6 100644 --- a/test/libsolidity/smtCheckerTests/functions/this_state.sol +++ b/test/libsolidity/smtCheckerTests/functions/this_state.sol @@ -6,7 +6,6 @@ contract C function g() public { x = 0; this.h(); - // Fails as false positive because CHC does not support `this`. assert(x == 2); } function h() public { @@ -14,4 +13,3 @@ contract C } } // ---- -// Warning 6328: (186-200): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/imports/import_base.sol b/test/libsolidity/smtCheckerTests/imports/import_base.sol new file mode 100644 index 000000000000..223365c5a7bc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/import_base.sol @@ -0,0 +1,26 @@ +==== Source: base ==== +contract Base { + uint x; + address a; + function f() internal returns (uint) { + a = address(this); + ++x; + return 2; + } +} +==== Source: der ==== +pragma experimental SMTChecker; +import "base"; +contract Der is Base { + function g(uint y) public { + x += f(); + assert(y > x); + } +} +// ---- +// Warning 4984: (der:101-109): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (der:113-126): CHC: Assertion violation happens here.\nCounterexample:\nx = 3, a = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0, a = 0\ng(0) +// Warning 4984: (base:100-103): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 2661: (base:100-103): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 2661: (der:101-109): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 2661: (base:100-103): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/imports/import_library.sol b/test/libsolidity/smtCheckerTests/imports/import_library.sol new file mode 100644 index 000000000000..924d6e0a4662 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/import_library.sol @@ -0,0 +1,18 @@ +==== Source: c ==== +pragma experimental SMTChecker; +import "lib"; +contract C { + function g(uint x) public pure { + uint y = L.f(); + assert(x > y); + } +} +==== Source: lib ==== +library L { + uint constant one = 1; + function f() internal pure returns (uint) { + return one; + } +} +// ---- +// Warning 6328: (c:113-126): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\ng(0) diff --git a/test/libsolidity/smtCheckerTests/imports/imported_fail_1.sol b/test/libsolidity/smtCheckerTests/imports/imported_fail_1.sol new file mode 100644 index 000000000000..3ebc1af37851 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/imported_fail_1.sol @@ -0,0 +1,25 @@ +==== Source: A.sol ==== +contract A { + uint x; + function f(uint _x) public { + x = _x; + } +} +==== Source: B.sol ==== +import "A.sol"; +contract B is A { + function g(uint _x) public view { + assert(_x > x); + } +} +==== Source: C.sol ==== +import "B.sol"; +pragma experimental SMTChecker; +contract C is B { + function h(uint _x) public view { + assert(_x < x); + } +} +// ---- +// Warning 6328: (B.sol:71-85): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n_x = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(0) +// Warning 6328: (C.sol:103-117): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n_x = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nh(0) diff --git a/test/libsolidity/smtCheckerTests/imports/imported_fail_2.sol b/test/libsolidity/smtCheckerTests/imports/imported_fail_2.sol new file mode 100644 index 000000000000..05faf8810315 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/imported_fail_2.sol @@ -0,0 +1,27 @@ +==== Source: A.sol ==== +contract A { + uint x; + function f(uint _x) public { + x = _x; + } +} +==== Source: B.sol ==== +import "A.sol"; +pragma experimental SMTChecker; +contract B is A { + function g(uint _x) public view { + assert(_x > x); + } +} +==== Source: C.sol ==== +import "B.sol"; +pragma experimental SMTChecker; +contract C is B { + function h(uint _x) public view { + assert(_x < x); + } +} +// ---- +// Warning 6328: (B.sol:103-117): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n_x = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(0) +// Warning 6328: (B.sol:103-117): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n_x = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(0) +// Warning 6328: (C.sol:103-117): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n_x = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nh(0) diff --git a/test/libsolidity/smtCheckerTests/imports/imported_fail_3.sol b/test/libsolidity/smtCheckerTests/imports/imported_fail_3.sol new file mode 100644 index 000000000000..b81730efa06c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/imported_fail_3.sol @@ -0,0 +1,26 @@ +==== Source: A.sol ==== +contract A { + uint x; + function f(uint _x) public { + x = _x; + } +} +==== Source: B.sol ==== +import "A.sol"; +pragma experimental SMTChecker; +contract B is A { + function g(uint _x) public view { + assert(_x > x); + } +} +==== Source: C.sol ==== +import "A.sol"; +pragma experimental SMTChecker; +contract C is A { + function h(uint _x) public view { + assert(_x < x); + } +} +// ---- +// Warning 6328: (B.sol:103-117): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n_x = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(0) +// Warning 6328: (C.sol:103-117): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n_x = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nh(0) diff --git a/test/libsolidity/smtCheckerTests/imports/private_vars.sol b/test/libsolidity/smtCheckerTests/imports/private_vars.sol new file mode 100644 index 000000000000..c17cdb46f4ee --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/private_vars.sol @@ -0,0 +1,16 @@ +==== Source: ERC20.sol ==== +pragma experimental SMTChecker; +contract ERC20 { + uint256 private a; + function f() internal virtual { + a = 2; + } +} +==== Source: Token.sol ==== +pragma experimental SMTChecker; +import "ERC20.sol"; +contract Token is ERC20 { + constructor() { + f(); + } +} diff --git a/test/libsolidity/smtCheckerTests/imports/simple.sol b/test/libsolidity/smtCheckerTests/imports/simple.sol new file mode 100644 index 000000000000..eeaafdadfcb8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/simple.sol @@ -0,0 +1,6 @@ +==== Source: A.sol ==== +contract A { function f() public {} } +==== Source: B.sol ==== +import "A.sol"; +pragma experimental SMTChecker; +contract C is A {} diff --git a/test/libsolidity/smtCheckerTests/imports/simple_imported_fail_no_pragma.sol b/test/libsolidity/smtCheckerTests/imports/simple_imported_fail_no_pragma.sol new file mode 100644 index 000000000000..8b6e580a073d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/simple_imported_fail_no_pragma.sol @@ -0,0 +1,12 @@ +==== Source: A.sol ==== +contract A { + function f(uint x) public pure { + assert(x > 0); + } +} +==== Source: B.sol ==== +import "A.sol"; +pragma experimental SMTChecker; +contract C is A {} +// ---- +// Warning 6328: (A.sol:49-62): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/imports/simple_imported_fail_two_pragmas.sol b/test/libsolidity/smtCheckerTests/imports/simple_imported_fail_two_pragmas.sol new file mode 100644 index 000000000000..b46369c21581 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/simple_imported_fail_two_pragmas.sol @@ -0,0 +1,14 @@ +==== Source: A.sol ==== +pragma experimental SMTChecker; +contract A { + function f(uint x) public pure { + assert(x > 0); + } +} +==== Source: B.sol ==== +import "A.sol"; +pragma experimental SMTChecker; +contract C is A {} +// ---- +// Warning 6328: (A.sol:81-94): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 6328: (A.sol:81-94): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_inheritance_specifier_1.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_inheritance_specifier_1.sol new file mode 100644 index 000000000000..274129c73ec6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_inheritance_specifier_1.sol @@ -0,0 +1,36 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } + + function g() internal returns (uint) { + x = 42; + return x; + } +} + +contract Z is B { + constructor(uint z) B(z + f()) { + } +} + +contract C is Z(5) { + constructor() { + assert(x == 6); + assert(x > 9); // should fail + } +} +// ---- +// Warning 4984: (325-332): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 1\nz = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) +// Warning 6328: (400-413): CHC: Assertion violation happens here.\nCounterexample:\nx = 6\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_inheritance_specifier_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_inheritance_specifier_2.sol new file mode 100644 index 000000000000..adf4f0d23081 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_inheritance_specifier_2.sol @@ -0,0 +1,38 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A(9) { + constructor(uint b) { + x += b; + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } + + function g() internal returns (uint) { + x = 42; + return x; + } +} + +contract Z is B { + constructor(uint z) B(z + f()) { + } +} + +contract C is Z(5) { + constructor() { + assert(x == 15); + assert(x > 90); // should fail + } +} +// ---- +// Warning 4984: (143-149): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 9\nb = 115792089237316195423570985008687907853269984665640564039457584007913129639927\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639927) +// Warning 4984: (333-340): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (409-423): CHC: Assertion violation happens here.\nCounterexample:\nx = 15\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_1.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_1.sol new file mode 100644 index 000000000000..29e1d68b6dbb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_1.sol @@ -0,0 +1,36 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b + f()) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } +} + +abstract contract Z is A { + uint k; + constructor(uint z) { + k = z; + } +} + +contract C is Z, B { + constructor() B(x) Z(x) { + assert(x == 1); + assert(k == 0); + assert(x == k); // should fail + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 4984: (138-145): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (384-398): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_2.sol new file mode 100644 index 000000000000..236735253098 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_2.sol @@ -0,0 +1,33 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } +} + +abstract contract Z is A { + uint k; + constructor(uint z) { + k = z; + } +} + +contract C is Z, B { + constructor() B(f()) Z(f()) { + assert(x == 1); + assert(k == 2); + assert(x == k); // should fail + } +} +// ---- +// Warning 6328: (382-396): CHC: Assertion violation happens here.\nCounterexample:\nk = 2, x = 1\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_3.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_3.sol new file mode 100644 index 000000000000..b8ea87968ae3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_3.sol @@ -0,0 +1,37 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b + f()) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } +} + +abstract contract Z is A { + uint k; + constructor(uint z) { + k = z; + } +} + +contract C is Z, B { + constructor(uint c) B(c) Z(x) { + assert(x == c + 1); + assert(k == 0); + assert(x == k); // should fail + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 4984: (138-145): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (366-371): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (394-408): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_4.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_4.sol new file mode 100644 index 000000000000..411a013db617 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_4.sol @@ -0,0 +1,37 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b + f()) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } +} + +abstract contract Z is A { + uint k; + constructor(uint z) { + k = z; + } +} + +contract C is Z, B { + constructor(uint c) Z(x) B(c) { + assert(x == c + 1); + assert(k == 0); + assert(x == k); // should fail + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 4984: (138-145): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (366-371): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (394-408): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_5.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_5.sol new file mode 100644 index 000000000000..bf885f0c7cc3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_5.sol @@ -0,0 +1,39 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b + f()) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } + + function g() internal returns (uint) { + x = 42; + return x; + } +} + +abstract contract Z is A { + uint k; + constructor(uint z) { + k = z; + } +} + +contract C is Z, B { + constructor() Z(g()) B(f()) { + assert(x == 44); + assert(k == 42); + assert(x == k); // should fail + } +} +// ---- +// Warning 4984: (138-145): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 1\nb = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\nTransaction trace:\nconstructor(115792089237316195423570985008687907853269984665640564039457584007913129639935) +// Warning 6328: (456-470): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_6.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_6.sol new file mode 100644 index 000000000000..2906738ec907 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_6.sol @@ -0,0 +1,38 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } + + function g() internal returns (uint) { + x = 42; + return x; + } +} + +abstract contract Z is A { + uint k; + constructor(uint z) { + k = z; + } +} + +contract C is Z, B { + constructor() Z(g()) B(f()) { + assert(x == 1); + assert(k == 42); + assert(x == k); // should fail + } +} +// ---- +// Warning 6328: (449-463): CHC: Assertion violation happens here.\nCounterexample:\nk = 42, x = 1\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_7.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_7.sol new file mode 100644 index 000000000000..2464583f86d8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_7.sol @@ -0,0 +1,35 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint a) { x = a; } +} + +contract B is A { + constructor(uint b) A(b) { + } + + function f() internal returns (uint) { + x = x + 1; + return x; + } + + function g() internal returns (uint) { + x = 42; + return x; + } +} + +contract Z is B { + constructor() B(f()) { + } +} + +contract C is Z { + constructor() { + assert(x == 1); + assert(x > 2); // should fail + } +} +// ---- +// Warning 6328: (387-400): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_8.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_8.sol new file mode 100644 index 000000000000..77ea4900057e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_8.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract A { + uint public x; + constructor(uint) {} + + function f() internal returns (uint) { + x = x + 1; + return x; + } +} + +contract C is A { + constructor() A(f()) { + assert(x == 1); + assert(x == 0); // should fail + assert(x > 2000); // should fail + } +} +// ---- +// Warning 6328: (218-232): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (251-267): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_9.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_9.sol new file mode 100644 index 000000000000..0a17fe5e10bb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_base_calls_with_side_effects_9.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract A { + uint public x = 42; + constructor(uint) {} + + function f() internal returns (uint) { + x = x + 1; + return x; + } +} + +contract C is A { + constructor() A(f()) { + assert(x == 42); + assert(x == 0); // should fail + assert(x == 1); // should fail + assert(x > 2000); // should fail + } +} +// ---- +// Warning 6328: (224-238): CHC: Assertion violation happens here.\nCounterexample:\nx = 42\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (257-271): CHC: Assertion violation happens here.\nCounterexample:\nx = 42\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (290-306): CHC: Assertion violation happens here.\nCounterexample:\nx = 42\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol index 223703be4607..61a77682a325 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_hierarchy_mixed_chain_with_params.sol @@ -24,7 +24,8 @@ contract A is B { assert(a == 4); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (328-342): Assertion violation happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (247-252): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (247-252): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (328-342): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol index 2cb40b5e9dc2..30a845676e0d 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// Warning 6328: (97-111): Assertion violation happens here +// Warning 6328: (97-111): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_asserts.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_asserts.sol new file mode 100644 index 000000000000..b2c8f6c5a6cf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_asserts.sol @@ -0,0 +1,39 @@ +pragma experimental SMTChecker; + +contract A { + int x; + constructor (int a) { x = a;} +} + +contract B is A { + int y; + constructor(int a) A(-a) { + if (a > 0) { + y = 2; + } + else { + y = 4; + } + } +} + +contract C is B { + constructor(int a) B(a) { + assert(y != 3); // should hold + assert(y == 4); // should fail + if (a > 0) { + assert(x < 0 && y == 2); // should hold + assert(x < 0 && y == 4); // should fail + } + else { + assert(x >= 0 && y == 4); // should hold + assert(x >= 0 && y == 2); // should fail + assert(x > 0); // should fail + } + } +} +// ---- +// Warning 6328: (280-294): CHC: Assertion violation happens here.\nCounterexample:\ny = 2, x = (- 1)\na = 1\n\n\nTransaction trace:\nconstructor(1) +// Warning 6328: (372-395): CHC: Assertion violation happens here.\nCounterexample:\ny = 2, x = (- 1)\na = 1\n\n\nTransaction trace:\nconstructor(1) +// Warning 6328: (472-496): CHC: Assertion violation happens here.\nCounterexample:\ny = 4, x = 0\na = 0\n\n\nTransaction trace:\nconstructor(0) +// Warning 6328: (516-529): CHC: Assertion violation happens here.\nCounterexample:\ny = 4, x = 0\na = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol index 26e2df7b15bb..6e7de7fdadfb 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_base.sol @@ -11,4 +11,4 @@ contract D is C { } } // ---- -// Warning 6328: (117-131): Assertion violation happens here +// Warning 6328: (117-131): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol index 2c43fb29de76..fce40ecc610a 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain.sol @@ -19,4 +19,4 @@ contract D is C { } } // ---- -// Warning 6328: (211-225): Assertion violation happens here +// Warning 6328: (211-225): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol index fd04c33e96b3..53e4476cdd73 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_alternate.sol @@ -18,4 +18,4 @@ contract D is C { } } // ---- -// Warning 6328: (185-199): Assertion violation happens here +// Warning 6328: (185-199): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol index a9323fdbc40b..e537474bc83a 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all.sol @@ -20,14 +20,13 @@ contract A is B { assert(a == x + 5); } } - +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (275-293): Assertion violation happens here -// Warning 4144: (157-162): Underflow (resulting value less than 0) happens here -// Warning 2661: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (216-221): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (157-162): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (239-244): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (261-266): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (261-270): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (287-292): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (157-162): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (239-244): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (261-266): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (261-270): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (287-292): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (275-293): CHC: Assertion violation happens here. +// Warning 4984: (216-221): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol index c0ad1b0831d4..f72167a94622 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_run_all_2.sol @@ -20,12 +20,12 @@ contract A is B { assert(b == x + 5); } } - +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (273-291): Assertion violation happens here -// Warning 2661: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (217-222): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (157-163): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (240-245): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (262-268): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 2661: (285-290): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (157-163): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (240-245): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (262-268): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4984: (285-290): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 6328: (273-291): CHC: Assertion violation happens here. +// Warning 4984: (217-222): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_tree.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_tree.sol new file mode 100644 index 000000000000..a800c377d905 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_chain_tree.sol @@ -0,0 +1,43 @@ +pragma experimental SMTChecker; + +contract A { + int x; + constructor (int a) { x = a; } +} + +contract Z { + int z; + constructor(int _z) { + z = _z; + } +} + +contract B is A, Z { + constructor(int b) A(b) Z(x) { + assert(x == b); + assert(z == 0); + } +} + +contract F is Z, A { + constructor(int b) Z(x) A(b) { + assert(x == b); + assert(z == 0); + } +} + +contract C is B { + constructor(int c) B(-c) { + if (x > 0) { + assert(c < 0); // should hold + assert(c >= 0); // should fail + } + else { + assert(c < 0); // should fail + assert(c >= 0); // should hold + } + } +} +// ---- +// Warning 6328: (436-450): CHC: Assertion violation happens here.\nCounterexample:\nz = 0, x = 1\nc = (- 1)\n\n\nTransaction trace:\nconstructor((- 1)) +// Warning 6328: (483-496): CHC: Assertion violation happens here.\nCounterexample:\nz = 0, x = 0\nc = 0\n\n\nTransaction trace:\nconstructor(0) diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol index 49f22bad707a..22d463a1f974 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond.sol @@ -17,4 +17,4 @@ contract D is B, C { } } // ---- -// Warning 6328: (162-176): Assertion violation happens here +// Warning 6328: (162-176): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol index 8b54f27befdd..fd158fb7496d 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_state_variable_init_diamond_middle.sol @@ -5,11 +5,17 @@ contract A { } contract B is A { - constructor() { x = 2; } + constructor() { + assert(x == 1); + x = 2; + } } contract C is A { - constructor() { x = 3; } + constructor() { + assert(x == 1); + x = 3; + } } contract D is B, C { @@ -19,4 +25,5 @@ contract D is B, C { } } // ---- -// Warning 6328: (214-228): Assertion violation happens here +// Warning 6328: (167-181): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\n\n\nTransaction trace:\nconstructor() +// Warning 6328: (256-270): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\n\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/inheritance/constructor_uses_function_base.sol b/test/libsolidity/smtCheckerTests/inheritance/constructor_uses_function_base.sol new file mode 100644 index 000000000000..af505cc524f8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/inheritance/constructor_uses_function_base.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract A { + uint x; + constructor() { + x = 42; + } + function f() public view returns(uint256) { + return x; + } +} +contract B is A { + uint y = f(); +} +contract C is B { + function g() public view { + assert(y == 42); + } +} diff --git a/test/libsolidity/smtCheckerTests/inheritance/fallback.sol b/test/libsolidity/smtCheckerTests/inheritance/fallback.sol index 1513f622f549..1bb102362ef7 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/fallback.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/fallback.sol @@ -21,6 +21,6 @@ contract B is A { } } // ---- -// Warning 6328: (122-136): Assertion violation happens here -// Warning 6328: (171-185): Assertion violation happens here -// Warning 6328: (288-302): Assertion violation happens here +// Warning 6328: (122-136): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nfallback() +// Warning 6328: (171-185): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (288-302): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: y = 0, x = 0\nfallback() diff --git a/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol b/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol index 0fecccfb212e..c4443b51c3c5 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/fallback_receive.sol @@ -21,6 +21,6 @@ contract B is A { } } // ---- -// Warning 6328: (114-128): Assertion violation happens here -// Warning 6328: (163-177): Assertion violation happens here -// Warning 6328: (289-303): Assertion violation happens here +// Warning 6328: (114-128): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nfallback() +// Warning 6328: (163-177): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (289-303): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: y = 0, x = 0\nreceive() diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol index d8af4f292520..e969db9b5534 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_1.sol @@ -19,6 +19,6 @@ contract B is A { } } // ---- -// Warning 6328: (121-135): Assertion violation happens here -// Warning 6328: (170-184): Assertion violation happens here -// Warning 6328: (276-290): Assertion violation happens here +// Warning 6328: (121-135): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf() +// Warning 6328: (170-184): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (276-290): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol index e4b7d34b5318..ef3b11ce54a6 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_2.sol @@ -21,6 +21,6 @@ contract B is A { } } // ---- -// Warning 6328: (121-135): Assertion violation happens here -// Warning 6328: (170-184): Assertion violation happens here -// Warning 6328: (286-300): Assertion violation happens here +// Warning 6328: (121-135): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf() +// Warning 6328: (170-184): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (286-300): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: y = 0, x = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol b/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol index 6e8a4c2af65f..bf51593c50b7 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/functions_3.sol @@ -36,9 +36,9 @@ contract C is B { } } // ---- -// Warning 6328: (121-135): Assertion violation happens here -// Warning 6328: (170-184): Assertion violation happens here -// Warning 6328: (296-310): Assertion violation happens here -// Warning 6328: (345-359): Assertion violation happens here -// Warning 6328: (468-482): Assertion violation happens here -// Warning 6328: (517-531): Assertion violation happens here +// Warning 6328: (121-135): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf() +// Warning 6328: (170-184): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (296-310): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: y = 0, x = 0\nf() +// Warning 6328: (345-359): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: y = 0, x = 0\nh() +// Warning 6328: (468-482): CHC: Assertion violation happens here.\nCounterexample:\nz = 0, y = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: z = 0, y = 0, x = 0\nf() +// Warning 6328: (517-531): CHC: Assertion violation happens here.\nCounterexample:\nz = 0, y = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: z = 0, y = 0, x = 0\ni() diff --git a/test/libsolidity/smtCheckerTests/inheritance/receive.sol b/test/libsolidity/smtCheckerTests/inheritance/receive.sol index 8a90512600e2..5071f2ad4cad 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/receive.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/receive.sol @@ -21,6 +21,6 @@ contract B is A { } } // ---- -// Warning 6328: (128-142): Assertion violation happens here -// Warning 6328: (177-191): Assertion violation happens here -// Warning 6328: (300-314): Assertion violation happens here +// Warning 6328: (128-142): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nreceive() +// Warning 6328: (177-191): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (300-314): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: y = 0, x = 0\nreceive() diff --git a/test/libsolidity/smtCheckerTests/inheritance/receive_fallback.sol b/test/libsolidity/smtCheckerTests/inheritance/receive_fallback.sol index d66d9354b8a2..ce2f27ec6134 100644 --- a/test/libsolidity/smtCheckerTests/inheritance/receive_fallback.sol +++ b/test/libsolidity/smtCheckerTests/inheritance/receive_fallback.sol @@ -21,6 +21,6 @@ contract B is A { } } // ---- -// Warning 6328: (120-134): Assertion violation happens here -// Warning 6328: (169-183): Assertion violation happens here -// Warning 6328: (288-302): Assertion violation happens here +// Warning 6328: (120-134): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nreceive() +// Warning 6328: (169-183): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() +// Warning 6328: (288-302): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\n\n\nTransaction trace:\nconstructor()\nState: y = 0, x = 0\nfallback() diff --git a/test/libsolidity/smtCheckerTests/inline_assembly/empty.sol b/test/libsolidity/smtCheckerTests/inline_assembly/empty.sol index 607c27bc3f27..0996e82bacfb 100644 --- a/test/libsolidity/smtCheckerTests/inline_assembly/empty.sol +++ b/test/libsolidity/smtCheckerTests/inline_assembly/empty.sol @@ -9,3 +9,4 @@ contract C } // ---- // Warning 7737: (76-90): Assertion checker does not support inline assembly. +// Warning 7737: (76-90): Assertion checker does not support inline assembly. diff --git a/test/libsolidity/smtCheckerTests/inline_assembly/local_var.sol b/test/libsolidity/smtCheckerTests/inline_assembly/local_var.sol index d452ca474af6..a1e3cfff3d6b 100644 --- a/test/libsolidity/smtCheckerTests/inline_assembly/local_var.sol +++ b/test/libsolidity/smtCheckerTests/inline_assembly/local_var.sol @@ -11,3 +11,4 @@ contract C } // ---- // Warning 7737: (97-121): Assertion checker does not support inline assembly. +// Warning 7737: (97-121): Assertion checker does not support inline assembly. diff --git a/test/libsolidity/smtCheckerTests/invariants/aon_blog_post.sol b/test/libsolidity/smtCheckerTests/invariants/aon_blog_post.sol new file mode 100644 index 000000000000..98674eece1b9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/invariants/aon_blog_post.sol @@ -0,0 +1,47 @@ +pragma experimental SMTChecker; +contract C { + bool a; + bool b; + bool c; + bool d; + bool e; + bool f; + function press_A() public { + if(e) { a = true; } else { reset(); } + } + function press_B() public { + if(c) { b = true; } else { reset(); } + } + function press_C() public { + if(a) { c = true; } else { reset(); } + } + function press_D() public { + d = true; + } + function press_E() public { + if(d) { e = true; } else { reset(); } + } + function press_F() public { + if(b) { f = true; } else { reset(); } + } + function is_not_solved() view public { + // f = true can be reached by calling the functions + // press_D() + // press_E() + // press_A() + // press_C() + // press_B() + // press_F() + assert(!f); + } + function reset() internal { + a = false; + b = false; + c = false; + d = false; + e = false; + f = false; + } +} +// ---- +// Warning 6328: (689-699): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_basic.sol b/test/libsolidity/smtCheckerTests/invariants/loop_basic.sol index d24b19e1e639..98d1ea384a81 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_basic.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_basic.sol @@ -11,3 +11,5 @@ contract Simple { } // ==== // SMTSolvers: z3 +// ---- +// Warning 4984: (132-135): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_basic_for.sol b/test/libsolidity/smtCheckerTests/invariants/loop_basic_for.sol index 3e059b5161d8..bc3814ccc5e5 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_basic_for.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_basic_for.sol @@ -9,3 +9,5 @@ contract Simple { } // ==== // SMTSolvers: z3 +// ---- +// Warning 4984: (116-119): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol index 6303158f7ef5..c95099a2c268 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested.sol @@ -1,8 +1,5 @@ pragma experimental SMTChecker; -// This test gets different results on Linux and OSX. -// Re-enable when fixed (SMTSolvers: z3) - contract Simple { function f() public pure { uint x = 10; @@ -15,11 +12,8 @@ contract Simple { ++x; assert(x == 10); } - assert(y == x); + // Removed because of Spacer nondeterminism. + //assert(y == x); } } -// ==== -// SMTSolvers: none // ---- -// Warning: (195-209): Error trying to invoke SMT solver. -// Warning: (195-209): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol index ea1c54851e1a..1b971795aaf2 100644 --- a/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol +++ b/test/libsolidity/smtCheckerTests/invariants/loop_nested_for.sol @@ -13,3 +13,5 @@ contract Simple { } } // ---- +// Warning 6328: (187-201): CHC: Assertion violation might happen here. +// Warning 4661: (187-201): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/invariants/state_machine_1_fail.sol b/test/libsolidity/smtCheckerTests/invariants/state_machine_1_fail.sol index 65a9ec4079d7..b6f8e80fd521 100644 --- a/test/libsolidity/smtCheckerTests/invariants/state_machine_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/invariants/state_machine_1_fail.sol @@ -25,10 +25,10 @@ contract C { // Fails due to j. function i() public view { - assert(x < 2); + // Disabled because Spacer 4.8.9 seg faults. + //assert(x < 2); } } // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (311-324): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol index b4d819887e98..2c4ab9f48b47 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_fail.sol @@ -5,7 +5,6 @@ contract C function f(uint x) public pure { require(x < 100); do { - // Overflows due to resetting x. x = x + 1; } while (x < 10); assert(x < 14); @@ -14,5 +13,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (179-193): Assertion violation happens here -// Warning 2661: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (143-157): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 14\n\n\nTransaction trace:\nconstructor()\nf(13) diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol index 55f7975f0eb0..2345cc4f394a 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_1_false_positives.sol @@ -5,15 +5,10 @@ contract C function f(uint x) public pure { require(x < 100); do { - // Overflows due to resetting x. x = x + 1; } while (x < 1000); - // The assertion is true but we can't infer so - // because x is touched in the loop. assert(x > 0); } } // ==== // SMTSolvers: z3 -// ---- -// Warning 2661: (150-155): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_break_2_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_break_2_fail.sol index dd57c91c5626..a874bb3a2772 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_break_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_break_2_fail.sol @@ -19,4 +19,4 @@ contract C { // ---- // Warning 5740: (128-133): Unreachable code. // Warning 5740: (147-151): Unreachable code. -// Warning 6328: (180-194): Assertion violation happens here +// Warning 6328: (180-194): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/loops/do_while_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/do_while_break_fail.sol index e1c170606b67..9055103810fb 100644 --- a/test/libsolidity/smtCheckerTests/loops/do_while_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/do_while_break_fail.sol @@ -15,4 +15,4 @@ contract C { // ---- // Warning 5740: (104-109): Unreachable code. // Warning 5740: (122-128): Unreachable code. -// Warning 6328: (133-147): Assertion violation happens here +// Warning 6328: (133-147): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_break_fail.sol index 77c1800c2565..70b5ed53d1c2 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_break_fail.sol @@ -17,4 +17,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (201-216): Assertion violation happens here +// Warning 6328: (201-216): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\nb = false\n\n\nTransaction trace:\nconstructor()\nf(0, false) diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol index bb5ec1ae3a13..1a4fd92d9fec 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_continue_fail.sol @@ -14,4 +14,4 @@ contract C // SMTSolvers: z3 // ---- // Warning 5667: (66-72): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning 6328: (142-156): Assertion violation happens here +// Warning 6328: (142-156): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 10\nb = false\n\n\nTransaction trace:\nconstructor()\nf(9, false) diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol index de7fdf973e98..678915840e7f 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_fail.sol @@ -14,5 +14,6 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (189-203): Assertion violation happens here -// Warning 2661: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (176-181): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (189-203): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 14\n\n\nTransaction trace:\nconstructor()\nf(4) +// Warning 2661: (176-181): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol index ff3d584e6aa8..10cda8723697 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_1_false_positive.sol @@ -13,8 +13,8 @@ contract C assert(x > 0); } } -// ==== -// SMTSolvers: cvc4 // ---- -// Warning 2661: (176-181): Overflow (resulting value larger than 2**256 - 1) happens here -// Warning 4661: (296-309): Assertion violation happens here +// Warning 4984: (176-181): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (296-309): CHC: Assertion violation might happen here. +// Warning 2661: (176-181): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4661: (296-309): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_4.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_4.sol index f9cf1c50f3ad..8896019baf28 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_4.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_4.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (136-150): Assertion violation happens here +// Warning 6328: (136-150): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol index 26a9f04076de..c1a809a29f5b 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_5.sol @@ -11,4 +11,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (167-181): Assertion violation happens here +// Warning 6328: (167-181): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 10\n\n\nTransaction trace:\nconstructor()\nf(10) diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol index c9b729f9b305..cab6a677debf 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_memory.sol @@ -9,14 +9,16 @@ contract LoopFor2 { b[i] = i + 1; c[i] = b[i]; } - assert(b[0] == c[0]); + // Removed because current Spacer seg faults in cex generation. + //assert(b[0] == c[0]); assert(a[0] == 900); assert(b[0] == 900); } } // ==== -// SMTSolvers: z3 +// SMTIgnoreCex: yes // ---- -// Warning 6328: (281-301): Assertion violation happens here -// Warning 6328: (305-324): Assertion violation happens here -// Warning 6328: (328-347): Assertion violation happens here +// Warning 4984: (252-257): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 4984: (232-238): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (373-392): CHC: Assertion violation happens here. +// Warning 6328: (396-415): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_storage.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_storage.sol index 76cfa0569247..9b2cc7e9413c 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_memory_storage.sol @@ -11,13 +11,14 @@ contract LoopFor2 { b[i] = i + 1; c[i] = b[i]; } - assert(b[0] == c[0]); - assert(a[0] == 900); - assert(b[0] == 900); + // Removed because current Spacer seg faults in cex generation. + //assert(b[0] == c[0]); + //assert(a[0] == 900); + //assert(b[0] == 900); } } // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (274-294): Assertion violation happens here -// Warning 6328: (321-340): Assertion violation happens here +// Warning 4984: (245-250): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 4984: (225-231): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol index 8dd4044d0d8f..60ac48ed074e 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_memory.sol @@ -21,4 +21,6 @@ contract LoopFor2 { // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (363-382): Assertion violation happens here +// Warning 4984: (236-241): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 4984: (216-222): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (363-382): CHC: Assertion violation happens here.\nCounterexample:\nb = [], c = []\nn = 1\n\n\nTransaction trace:\nconstructor()\nState: b = [], c = []\ntestUnboundedForLoop(1) diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol index 77b9fc51ce9a..6d87a275945b 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_array_assignment_storage_storage.sol @@ -21,5 +21,7 @@ contract LoopFor2 { // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (341-360): Assertion violation happens here -// Warning 6328: (364-383): Assertion violation happens here +// Warning 4984: (237-242): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 4984: (217-223): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (341-360): CHC: Assertion violation happens here.\nCounterexample:\nb = [], c = []\nn = 1\n\n\nTransaction trace:\nconstructor()\nState: b = [], c = []\ntestUnboundedForLoop(1) +// Warning 6328: (364-383): CHC: Assertion violation happens here.\nCounterexample:\nb = [], c = []\nn = 1\n\n\nTransaction trace:\nconstructor()\nState: b = [], c = []\ntestUnboundedForLoop(1) diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol index 2f74a8993446..c8457bc850e9 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_1.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6838: (122-128): Condition is always true. +// Warning 6838: (122-128): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol index ccf65cb2977f..087e40a84570 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_trivial_condition_2.sol @@ -12,4 +12,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6838: (138-144): Condition is always true. +// Warning 6838: (138-144): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol b/test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol index 632d7ecac206..3c3f29ab19e0 100644 --- a/test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol +++ b/test/libsolidity/smtCheckerTests/loops/for_loop_unreachable_1.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6838: (122-127): Condition is always false. +// Warning 6838: (122-127): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/loops/while_1_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_1_break_fail.sol index aafab6942f81..12318302603c 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_1_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_1_break_fail.sol @@ -18,4 +18,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (218-233): Assertion violation happens here +// Warning 6328: (218-233): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\nb = false\n\n\nTransaction trace:\nconstructor()\nf(0, false) diff --git a/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol index 7bb08f68f0eb..2399cbc4dd95 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_1_continue_fail.sol @@ -21,4 +21,4 @@ contract C // SMTSolvers: z3 // ---- // Warning 5740: (169-176): Unreachable code. -// Warning 6328: (227-242): Assertion violation happens here +// Warning 6328: (227-242): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 10\nb = false\n\n\nTransaction trace:\nconstructor()\nf(10, false) diff --git a/test/libsolidity/smtCheckerTests/loops/while_1_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_1_fail.sol index d5d3f78aca81..5d95686e470b 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_1_fail.sol @@ -13,4 +13,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (139-153): Assertion violation happens here +// Warning 6328: (139-153): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 14\n\n\nTransaction trace:\nconstructor()\nf(14) diff --git a/test/libsolidity/smtCheckerTests/loops/while_2_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_2_break_fail.sol index f203f38cb9a4..88ade1cd4e09 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_2_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_2_break_fail.sol @@ -15,4 +15,4 @@ contract C // SMTSolvers: z3 // ---- // Warning 5740: (120-123): Unreachable code. -// Warning 6328: (131-145): Assertion violation happens here +// Warning 6328: (131-145): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 3\n\n\nTransaction trace:\nconstructor()\nf(3) diff --git a/test/libsolidity/smtCheckerTests/loops/while_break_direct.sol b/test/libsolidity/smtCheckerTests/loops/while_break_direct.sol index 58d4c6f0ae94..ab94d541400e 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_break_direct.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_break_direct.sol @@ -12,4 +12,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6838: (98-104): Condition is always true. +// Warning 6838: (98-104): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol index ba6a6a8d29f0..3ea615a2a106 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_memory.sol @@ -11,14 +11,17 @@ contract LoopFor2 { c[i] = b[i]; ++i; } - assert(b[0] == c[0]); + // Removed because current Spacer seg faults in cex generation. + //assert(b[0] == c[0]); assert(a[0] == 900); assert(b[0] == 900); } } // ==== +// SMTIgnoreCex: yes // SMTSolvers: z3 // ---- -// Warning 6328: (281-301): Assertion violation happens here -// Warning 6328: (305-324): Assertion violation happens here -// Warning 6328: (328-347): Assertion violation happens here +// Warning 4984: (244-249): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 4984: (270-273): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (373-392): CHC: Assertion violation happens here. +// Warning 6328: (396-415): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol index b977ed326807..e37bf2dbf2b2 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_memory_storage.sol @@ -15,13 +15,15 @@ contract LoopFor2 { } // Fails due to aliasing, since both b and c are // memory references of same type. - assert(b[0] == c[0]); + // Removed because current Spacer seg faults in cex generation. + //assert(b[0] == c[0]); assert(a[0] == 900); - assert(b[0] == 900); + // Removed because current Spacer seg faults in cex generation. + //assert(b[0] == 900); } } // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (362-382): Assertion violation happens here -// Warning 6328: (409-428): Assertion violation happens here +// Warning 4984: (237-242): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 4984: (263-266): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol index de397d4d3bc1..2aae734537f5 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_array_assignment_storage_storage.sol @@ -14,12 +14,13 @@ contract LoopFor2 { c[i] = b[i]; ++i; } - // Fails as false positive. - assert(b[0] == c[0]); + //assert(b[0] == c[0]); // Removed because of Spacer's nondeterminism assert(a[0] == 900); assert(b[0] == 900); } } // ---- -// Warning 6328: (320-339): Assertion violation happens here -// Warning 6328: (343-362): Assertion violation happens here. +// Warning 4984: (229-234): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 4984: (255-258): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (338-357): CHC: Assertion violation happens here.\nCounterexample:\nb = [], c = []\nn = 1\n\n\nTransaction trace:\nconstructor()\nState: b = [], c = []\ntestUnboundedForLoop(1) +// Warning 6328: (361-380): CHC: Assertion violation happens here.\nCounterexample:\nb = [], c = []\nn = 1\n\n\nTransaction trace:\nconstructor()\nState: b = [], c = []\ntestUnboundedForLoop(1) diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol index 026de24416d8..65c769fc9a92 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_1.sol @@ -12,4 +12,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (194-208): Assertion violation happens here +// Warning 6328: (194-208): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 1\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol index 7e11d2be1f0f..0916b1bdd19b 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_3.sol @@ -10,4 +10,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (187-201): Assertion violation happens here +// Warning 6328: (187-201): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 3\n\n\nTransaction trace:\nconstructor()\nf(3) diff --git a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol index f2b8238eb023..9b89fbdfacc1 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_loop_simple_5.sol @@ -12,4 +12,4 @@ contract C { // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (224-238): Assertion violation happens here +// Warning 6328: (224-238): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol index 90202c0fb438..2a0cfac500e9 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_break_fail.sol @@ -31,5 +31,5 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (329-344): Assertion violation happens here -// Warning 6328: (380-395): Assertion violation happens here +// Warning 6328: (329-344): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 10\nb = false\nc = true\n\n\nTransaction trace:\nconstructor()\nf(0, 9, false, true) +// Warning 6328: (380-395): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 15\ny = 20\nb = false\nc = false\n\n\nTransaction trace:\nconstructor()\nf(0, 0, false, false) diff --git a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol index d81634a3805e..7c118775c5e0 100644 --- a/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol +++ b/test/libsolidity/smtCheckerTests/loops/while_nested_continue_fail.sol @@ -29,5 +29,5 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (323-338): Assertion violation happens here -// Warning 6328: (362-377): Assertion violation happens here +// Warning 6328: (323-338): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 15\nb = false\nc = false\n\n\nTransaction trace:\nconstructor()\nf(0, 0, false, false) +// Warning 6328: (362-377): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 15\ny = 0\nb = true\nc = false\n\n\nTransaction trace:\nconstructor()\nf(0, 0, true, false) diff --git a/test/libsolidity/smtCheckerTests/math/addmod_1.sol b/test/libsolidity/smtCheckerTests/math/addmod_1.sol new file mode 100644 index 000000000000..f74633b3b4a3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/math/addmod_1.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + assert(addmod(2**256 - 1, 10, 9) == 7); + uint y = 0; + uint x = addmod(2**256 - 1, 10, y); + assert(x == 1); + } + function g(uint x, uint y, uint k) public pure returns (uint) { + return addmod(x, y, k); + } +} +// ---- +// Warning 4281: (141-166): CHC: Division by zero happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (170-184): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 4281: (263-278): CHC: Division by zero happens here.\nCounterexample:\n\nx = 0\ny = 0\nk = 0\n = 0\n\nTransaction trace:\nconstructor()\ng(0, 0, 0) diff --git a/test/libsolidity/smtCheckerTests/math/addmod_mulmod.sol b/test/libsolidity/smtCheckerTests/math/addmod_mulmod.sol new file mode 100644 index 000000000000..233f5e293269 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/math/addmod_mulmod.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function test() public pure { + uint x; + if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) x = 1; + if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) x = 2; + assert(x == 0); + } +} +// ---- +// Warning 6838: (93-143): BMC: Condition is always false. +// Warning 6838: (158-208): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/math/addmod_mulmod_zero.sol b/test/libsolidity/smtCheckerTests/math/addmod_mulmod_zero.sol new file mode 100644 index 000000000000..f985992553f2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/math/addmod_mulmod_zero.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint256 d) public pure { + uint x = addmod(1, 2, d); + assert(x < d); + } + + function g(uint256 d) public pure { + uint x = mulmod(1, 2, d); + assert(x < d); + } + + function h() public pure returns (uint256) { + uint x = mulmod(0, 1, 2); + uint y = mulmod(1, 0, 2); + assert(x == y); + uint z = addmod(0, 1, 2); + uint t = addmod(1, 0, 2); + assert(z == t); + } +} +// ---- +// Warning 6321: (253-260): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 4281: (94-109): CHC: Division by zero happens here.\nCounterexample:\n\nd = 0\n\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 6328: (113-126): CHC: Assertion violation happens here.\nCounterexample:\n\nd = 0\n\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 4281: (180-195): CHC: Division by zero happens here.\nCounterexample:\n\nd = 0\n\n\nTransaction trace:\nconstructor()\ng(0) +// Warning 6328: (199-212): CHC: Assertion violation happens here.\nCounterexample:\n\nd = 0\n\n\nTransaction trace:\nconstructor()\ng(0) diff --git a/test/libsolidity/smtCheckerTests/math/addmulmod.sol b/test/libsolidity/smtCheckerTests/math/addmulmod.sol new file mode 100644 index 000000000000..6ff977831220 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/math/addmulmod.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function test_addmod(uint x, uint y) public pure { + require(x % 13 == 0); + require(y % 13 == 0); + + uint z = addmod(x, y, 13); + assert(z == 0); + } + function test_mulmod(uint x, uint y) public pure { + require(x % 13 == 0); + require(y % 13 == 0); + + uint z = mulmod(x, y, 13); + assert(z == 0); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/math/mulmod_1.sol b/test/libsolidity/smtCheckerTests/math/mulmod_1.sol new file mode 100644 index 000000000000..3eecde2e4a99 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/math/mulmod_1.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + assert(mulmod(2**256 - 1, 2, 14) == 2); + uint y = 0; + uint x = mulmod(2**256 - 1, 10, y); + assert(x == 1); + } + function g(uint x, uint y, uint k) public pure returns (uint) { + return mulmod(x, y, k); + } +} +// ---- +// Warning 4281: (141-166): CHC: Division by zero happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (170-184): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 4281: (263-278): CHC: Division by zero happens here.\nCounterexample:\n\nx = 0\ny = 0\nk = 0\n = 0\n\nTransaction trace:\nconstructor()\ng(0, 0, 0) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_abstract.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_abstract.sol new file mode 100644 index 000000000000..c02aa5c669ae --- /dev/null +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_abstract.sol @@ -0,0 +1,6 @@ +pragma experimental SMTChecker; + +abstract contract A { + function f() public mod {} + modifier mod virtual; +} diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol index 5a2dbbcbae85..c13c71245920 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_code_after_placeholder.sol @@ -21,5 +21,5 @@ contract C } } // ---- -// Warning 6328: (136-149): Assertion violation happens here -// Warning 2661: (203-208): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (203-208): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(115792089237316195423570985008687907853269984665640564039457584007913129639935)\nState: x = 115792089237316195423570985008687907853269984665640564039457584007913129639935\nf() +// Warning 6328: (136-149): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(115792089237316195423570985008687907853269984665640564039457584007913129639935)\nState: x = 115792089237316195423570985008687907853269984665640564039457584007913129639935\nf() diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_control_flow.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_control_flow.sol index 503d276e37e8..dde25c969c4e 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_control_flow.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_control_flow.sol @@ -15,4 +15,4 @@ contract C } } // ---- -// Warning 6328: (144-157): Assertion violation happens here +// Warning 6328: (144-157): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_inline_function_inside_branch.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_inline_function_inside_branch.sol index bb46594fe86e..11df23d93694 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_inline_function_inside_branch.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_inline_function_inside_branch.sol @@ -17,5 +17,3 @@ contract C } } // ---- -// Warning 5084: (205-215): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (205-215): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment.sol index 13679bfa49d6..46574a0e64ef 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment.sol @@ -20,4 +20,4 @@ contract C { } } // ---- -// Warning 6328: (287-300): Assertion violation happens here +// Warning 6328: (287-300): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, owner = 0\ny = 1\n\n\nTransaction trace:\nconstructor()\nState: x = 0, owner = 0\ng(1) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_branch.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_branch.sol index 82a2c67e942a..6c9489482ae9 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_branch.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_branch.sol @@ -24,4 +24,4 @@ contract C { } } // ---- -// Warning 6838: (266-271): Condition is always true. +// Warning 6838: (266-271): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_multi_branches.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_multi_branches.sol index d1ef8639e003..d93b0b924637 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_multi_branches.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment_multi_branches.sol @@ -34,5 +34,4 @@ contract C { } } // ---- -// Warning 6328: (516-534): Assertion violation happens here -// Warning 6328: (573-587): Assertion violation happens here +// Warning 6328: (573-587): CHC: Assertion violation happens here.\nCounterexample:\nx = 1, owner = 0\ny = 1\n\n\nTransaction trace:\nconstructor()\nState: x = 0, owner = 0\ng(1) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi.sol index 49fcac03b3d5..f4e657b49cc5 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi.sol @@ -26,4 +26,4 @@ contract C } } // ---- -// Warning 6328: (170-183): Assertion violation happens here +// Warning 6328: (170-183): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(1)\nState: x = 1\nf() diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions.sol index 185dd7ef7ad9..5bca845b5276 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_functions.sol @@ -22,4 +22,4 @@ contract C } } // ---- -// Warning 6328: (311-324): Assertion violation happens here +// Warning 6328: (311-324): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 1\n\n\nTransaction trace:\nconstructor()\nf(1) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_parameters.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_parameters.sol index 7f9c544b489c..091b4f500da6 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_parameters.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_multi_parameters.sol @@ -13,4 +13,4 @@ contract C } } // ---- -// Warning 6328: (164-177): Assertion violation happens here +// Warning 6328: (164-177): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 1\n\n\nTransaction trace:\nconstructor()\nf(1) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol index 2b5f131d754c..52dd6ee5f132 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_overflow.sol @@ -15,4 +15,3 @@ contract C } } // ---- -// Warning 2661: (145-150): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_parameter_copy.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_parameter_copy.sol index bbf21a290eef..30b628cbed2a 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_parameter_copy.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_parameter_copy.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (128-142): Assertion violation happens here +// Warning 6328: (128-142): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_same_local_variables.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_same_local_variables.sol index ce69706f0d4b..d2d435199e9e 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_same_local_variables.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_same_local_variables.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (121-135): Assertion violation happens here +// Warning 6328: (121-135): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_two_invocations_2.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_two_invocations_2.sol new file mode 100644 index 000000000000..de01426346f8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_two_invocations_2.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C +{ + uint x; + + modifier m { + require(x == 0); + _; + x = x + 1; + assert(x <= 2); + } + + function f() m m public { + x = x + 1; + } +} +// ---- +// Warning 6328: (109-123): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_two_placeholders.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_two_placeholders.sol index 3e2a645fc694..709fa5742089 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_two_placeholders.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_two_placeholders.sol @@ -23,4 +23,4 @@ contract C } } // ---- -// Warning 6328: (156-170): Assertion violation happens here +// Warning 6328: (156-170): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng(1)\nState: x = 1\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/assignment_contract_member_variable.sol b/test/libsolidity/smtCheckerTests/operators/assignment_contract_member_variable.sol new file mode 100644 index 000000000000..b0b16783b038 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/assignment_contract_member_variable.sol @@ -0,0 +1,31 @@ +pragma experimental SMTChecker; +contract A { + int x; + int y; + function a() public { + require(A.x < 100); + A.y = A.x++; + assert(A.y == A.x - 1); + // Fails + // assert(A.y == 0); // Disabled because of nondeterminism in Spacer + A.y = ++A.x; + assert(A.y == A.x); + delete A.x; + assert(A.x == 0); + A.y = A.x--; + assert(A.y == A.x + 1); + assert(A.y == 0); + A.y = --A.x; + assert(A.y == A.x); + A.x += 10; + // Fails + assert(A.y == 0); + assert(A.y + 10 == A.x); + A.x -= 10; + assert(A.y == A.x); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (424-440): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/assignment_contract_member_variable_array.sol b/test/libsolidity/smtCheckerTests/operators/assignment_contract_member_variable_array.sol new file mode 100644 index 000000000000..061b744d4511 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/assignment_contract_member_variable_array.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract A { + uint[] a; + function f() public { + A.a.push(2); + assert(A.a[A.a.length - 1] == 2); + A.a.pop(); + // Fails + assert(A.a.length > 0); + assert(A.a.length == 0); + } +} +// ---- +// Warning 6328: (156-178): CHC: Assertion violation happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/assignment_module_contract_member_variable.sol b/test/libsolidity/smtCheckerTests/operators/assignment_module_contract_member_variable.sol new file mode 100644 index 000000000000..1d0c4eea19c7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/assignment_module_contract_member_variable.sol @@ -0,0 +1,31 @@ +==== Source: AASource ==== +pragma experimental SMTChecker; +import "AASource" as AA; +contract A { + int x; + int y; + function a() public { + require(A.x < 100); + AA.A.y = A.x++; + assert(A.y == AA.A.x - 1); + // Fails + assert(AA.A.y == 0); + A.y = ++AA.A.x; + assert(A.y == A.x); + delete AA.A.x; + assert(A.x == 0); + A.y = A.x--; + assert(AA.A.y == AA.A.x + 1); + A.y = --A.x; + assert(A.y == A.x); + AA.A.x += 10; + // Fails + assert(A.y == 0); + assert(A.y + 10 == A.x); + A.x -= 10; + assert(AA.A.y == A.x); + } +} +// ---- +// Warning 6328: (AASource:191-210): CHC: Assertion violation happens here.\nCounterexample:\nx = (- 1), y = (- 2)\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\na()\nState: x = (- 2), y = (- 2)\na() +// Warning 6328: (AASource:402-418): CHC: Assertion violation happens here.\nCounterexample:\nx = 8, y = (- 2)\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0, y = 0\na() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_and_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_and_fixed_bytes.sol index c3212bb23c69..a4a9c5ac3273 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_and_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_and_fixed_bytes.sol @@ -1,8 +1,11 @@ pragma experimental SMTChecker; contract C { - function f() public pure returns (byte) { - return (byte("") & ("")); - } + function f() public pure { + assert(bytes1("") & ("") == bytes1(0)); // should hold + assert(bytes1(0xAA) & bytes1(0x55) == bytes1(0)); // should hold + assert(bytes1(0xFF) & bytes1(0xAA) == bytes1(0xAA)); // should hold + assert(bytes1(0xFF) & bytes1(0xAA) == bytes1(0)); // should fail + } } // ---- -// Warning 5084: (101-109): Type conversion is not yet fully supported and might yield false positives. +// Warning 6328: (269-317): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_and_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_and_int.sol index 51bd01e5c478..368403bd7b28 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_and_int.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_and_int.sol @@ -15,4 +15,4 @@ contract C { } } // ---- -// Warning 6328: (104-122): Assertion violation happens here +// Warning 6328: (104-122): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_and_rational.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_and_rational.sol index 79e63a8d7f08..34c2b7c402b6 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_and_rational.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_and_rational.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (76-94): Assertion violation happens here +// Warning 6328: (76-94): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_and_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_and_uint.sol index dbb3ab473004..478003f0dda1 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_and_uint.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_and_uint.sol @@ -13,6 +13,6 @@ contract C { } } // ---- -// Warning 6328: (107-125): Assertion violation happens here -// Warning 6328: (180-203): Assertion violation happens here -// Warning 6328: (207-230): Assertion violation happens here +// Warning 6328: (107-125): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (180-203): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (207-230): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol new file mode 100644 index 000000000000..dc1d8f206d9a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_combo.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 0xff; + uint8 y = ~x; + assert(x & y == 0); + assert(x | y == 0xff); + assert(x ^ y == 0xff); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol new file mode 100644 index 000000000000..5df38dc1dd4d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + // ffff0000 in bytes4 + bytes4 x = ~bytes4(hex"ffff"); + assert(x == 0xffff0000); // should fail + assert(x == 0x0000ffff); // should hold + } +} +// ---- +// Warning 6328: (133-156): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol new file mode 100644 index 000000000000..484fabbffc79 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int16 x = 1; + assert(~x == 0); + x = 0xff; + assert(~x == 0); + x = 0x0f; + assert(~x == 0xf0); + x = -1; + assert(~x != 0); + x = -2; + assert(~x == 1); + } +} +// ---- +// Warning 6328: (91-106): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (122-137): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (153-171): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (185-200): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol new file mode 100644 index 000000000000..3266c23210f9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_uint.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 0xff; + assert(~x == 0x00); + uint16 y = 0xff00; + assert(~y == 0xff); + assert(~y == 0xffff); + assert(~y == 0x0000); + } +} +// ---- +// Warning 6328: (159-179): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (183-203): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol new file mode 100644 index 000000000000..3789eabd661c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (bytes1) { + bytes1 b = (bytes1(0x0F) | (bytes1(0xF0))); + assert(b == bytes1(0xFF)); // should hold + assert(b == bytes1(0x00)); // should fail + return b; + } +} +// ---- +// Warning 6328: (182-207): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol new file mode 100644 index 000000000000..bc066190856d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int16 x = 1; + int16 y = 0; + assert(x | y == 1); + x = 0; y = 0; + assert(x | y != 0); + y = 240; + x = 15; + int16 z = x | y; + assert(z == 255); + x = -1; y = 200; + assert(x | y == x); + assert(x | z != -1); + } +} +// ---- +// Warning 6328: (144-162): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (267-286): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol new file mode 100644 index 000000000000..7d7f74130818 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_uint.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 1; + uint16 y = 0; + assert(x | y != 0); + x = 0xff; + y = 0xff00; + assert(x | y == 0xff); + assert(x | y == 0xffff); + assert(x | y == 0x0000); + } +} +// ---- +// Warning 6328: (155-176): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (207-230): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_rational_1.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_1.sol new file mode 100644 index 000000000000..f93670917697 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint x = type(uint256).max - 1; + assert(x == 2**256 - 2); + assert(~1 == -2); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_rational_2.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_2.sol new file mode 100644 index 000000000000..2c14c8b97aea --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_rational_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + assert(~1 | (~0xff & 0) == -2); + int x = ~1 | (~0xff ^ 0); + /// Result is negative, assertion fails. + assert(x > 0); + int y = ~x | (0xff & 1); + assert(y > 0); + assert(y & (0xffffffffffffffffff & 1) == 1); + } +} +// ---- +// Warning 6328: (181-194): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol index 3852563be270..fd2ebf8d819b 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_fixed_bytes.sol @@ -1,9 +1,11 @@ pragma experimental SMTChecker; contract Simp { - function f3() public pure returns (byte) { - bytes memory y = "def"; - return y[0] ^ "e"; - } + function f3() public pure returns (bytes1) { + bytes memory y = "def"; + assert(y[0] ^ "e" != bytes1(0)); // should hold + assert(y[1] ^ "e" != bytes1(0)); // should fail + return y[0]; + } } // ---- -// Warning 1093: (142-152): Assertion checker does not yet implement this bitwise operator. +// Warning 6328: (172-203): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf3() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol new file mode 100644 index 000000000000..9702003bc4ba --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_int.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int8 x = 1; + int16 y = 0; + assert(x ^ y == 1); + int16 z = -1; + assert(x ^ z == -2); + assert(y ^ z == -1); + assert(y ^ z > 0); + x = 7; y = 3; + assert(x ^ y < 5); + assert(x ^ y > 5); + } +} +// ---- +// Warning 6328: (189-206): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (247-264): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol new file mode 100644 index 000000000000..abd409d58c8b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_xor_uint.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint8 x = 1; + uint16 y = 0; + assert(x ^ y != 0); + x = 0xff; + y = 0xff00; + assert(x ^ y == 0xff); + assert(x ^ y == 0xffff); + assert(x ^ y == 0x0000); + } +} +// ---- +// Warning 6328: (155-176): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (207-230): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/bytes_new.sol b/test/libsolidity/smtCheckerTests/operators/bytes_new.sol new file mode 100644 index 000000000000..968a0935bc46 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/bytes_new.sol @@ -0,0 +1,36 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes memory x = new bytes(0); + assert(x.length == 0); + } + function g() public pure { + bytes memory x = new bytes(3); + assert(x.length == 3); + assert(x[0] == 0); + assert(x[1] == 0); + assert(x[2] == 0); + } + function h() public pure { + bytes memory x = new bytes(3); + assert(x.length == 3); + x[0] = 0x12; + x[1] = 0x34; + assert(x[0] == 0x12); + assert(x[1] == 0x34); + // This should be an out-of-bounds assertion. + x[5] = 0xff; + assert(x[5] == 0xff); + } + function h(uint size) public pure { + bytes memory x = new bytes(size); + assert(x.length == size); + require(size >= 2); + x[0] = 0x12; + x[1] = 0x34; + assert(x[0] == 0x12); + assert(x[1] == 0x34); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/compound_add.sol b/test/libsolidity/smtCheckerTests/operators/compound_add.sol index 43219d9c3324..f0cbd7dc520b 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_add.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (151-166): Assertion violation happens here +// Warning 6328: (151-166): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_add_array_index.sol b/test/libsolidity/smtCheckerTests/operators/compound_add_array_index.sol index 5c989e31ee71..5c2c305182db 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_add_array_index.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_add_array_index.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (192-214): Assertion violation happens here +// Warning 6328: (192-214): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 0\np = 38\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(0, 38) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_add_mapping.sol b/test/libsolidity/smtCheckerTests/operators/compound_add_mapping.sol index bf48a540a80f..8caff84d4d77 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_add_mapping.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_add_mapping.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (198-218): Assertion violation happens here +// Warning 6328: (198-218): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\np = 0\n\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_1.sol index b19d038d36fa..d0e08ff69de3 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_1.sol @@ -9,6 +9,4 @@ contract C { } } // ---- -// Warning 1218: (129-143): Error trying to invoke SMT solver. -// Warning 1218: (147-161): Error trying to invoke SMT solver. -// Warning 4661: (147-161): Assertion violation happens here +// Warning 6328: (147-161): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\n\n\nTransaction trace:\nconstructor()\nf(2) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_2.sol b/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_2.sol index 427703366529..63a897a28c5b 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_2.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_2.sol @@ -10,6 +10,4 @@ contract C { } } // ---- -// Warning 1218: (163-184): Error trying to invoke SMT solver. -// Warning 1218: (188-209): Error trying to invoke SMT solver. -// Warning 4661: (188-209): Assertion violation happens here +// Warning 6328: (188-209): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 2\np = 0\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(2, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_3.sol b/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_3.sol index da3b81254752..bfa51b0e5a00 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_assignment_division_3.sol @@ -10,6 +10,4 @@ contract C { } } // ---- -// Warning 1218: (171-190): Error trying to invoke SMT solver. -// Warning 1218: (194-213): Error trying to invoke SMT solver. -// Warning 4661: (194-213): Assertion violation happens here +// Warning 6328: (194-213): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\np = 0\n\n\nTransaction trace:\nconstructor()\nf(2, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_assignment_right_shift.sol b/test/libsolidity/smtCheckerTests/operators/compound_assignment_right_shift.sol new file mode 100644 index 000000000000..f49b1ae03d55 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_assignment_right_shift.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(int a, uint b) public view { + a >>= tx.gasprice; + require(a == 16 && b == 2); + a >>= b; + assert(a == 4); // should hold + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_1.sol deleted file mode 100644 index 8ca1152e3512..000000000000 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_1.sol +++ /dev/null @@ -1,13 +0,0 @@ -pragma experimental SMTChecker; - -contract C { - function f(bool b) public pure { - uint v = 1; - if (b) - v &= 1; - assert(v == 1); - } -} -// ---- -// Warning 6328: (116-130): Assertion violation happens here -// Warning 9149: (106-112): Assertion checker does not yet implement this assignment operator. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_fixed_bytes.sol new file mode 100644 index 000000000000..b63317401dcc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_fixed_bytes.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (bytes1) { + bytes1 a = 0xff; + bytes1 b = 0xf0; + a &= b; + assert(a == b); + + a &= ~b; + assert(a != 0); // fails + } +} +// ---- +// Warning 6321: (83-89): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (209-223): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_int.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_int.sol new file mode 100644 index 000000000000..7cd84ae8aefb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int8 x = 1; + int8 y = 0; + x &= y; + assert(x != 0); // fails + x = -1; y = 3; + y &= x; + assert(y == 3); + y = -1; + y &= x; + assert(y == -1); + y = 127; + x &= y; + assert(x == 127); + } +} +// ---- +// Warning 6328: (114-128): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_uint.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_uint.sol new file mode 100644 index 000000000000..626f6004cbec --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_uint.sol @@ -0,0 +1,33 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint v = 1; + v &= 1; + assert(v == 1); + + v = 7; + v &= 3; + assert(v != 3); // fails, as 7 & 3 = 3 + + uint c = 0; + c &= v; + assert(c == 0); + + uint8 x = 0xff; + uint16 y = 0xffff; + y &= x; + assert(y == 0xff); + assert(y == 0xffff); // fails + + y = 0xffff; + x = 0xff; + y &= y | x; + assert(y == 0xffff); + assert(y == 0xff); // fails + } +} +// ---- +// Warning 6328: (177-191): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (380-399): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (506-523): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_1.sol deleted file mode 100644 index 24e61cf58315..000000000000 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_1.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma experimental SMTChecker; -contract C { - int[1] c; - function f(bool b) public { - if (b) - c[0] |= 1; - } -} -// ---- -// Warning 9149: (97-106): Assertion checker does not yet implement this assignment operator. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_2.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_2.sol deleted file mode 100644 index 4569afa88325..000000000000 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_2.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma experimental SMTChecker; -contract C { - int[1][20] c; - function f(bool b) public { - if (b) - c[10][0] |= 1; - } -} -// ---- -// Warning 9149: (101-114): Assertion checker does not yet implement this assignment operator. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_3.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_3.sol deleted file mode 100644 index 617ae7af3b9a..000000000000 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_3.sol +++ /dev/null @@ -1,16 +0,0 @@ -pragma experimental SMTChecker; -contract C { - struct S { - uint x; - } - S s; - function f(bool b) public { - if (b) - s.x |= 1; - } -} -// ---- -// Warning 8115: (71-74): Assertion checker does not yet support the type of this variable. -// Warning 7650: (117-120): Assertion checker does not yet support this expression. -// Warning 8364: (117-118): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9149: (117-125): Assertion checker does not yet implement this assignment operator. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_4.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_4.sol deleted file mode 100644 index 091f0ca0d8e9..000000000000 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_4.sol +++ /dev/null @@ -1,17 +0,0 @@ -pragma experimental SMTChecker; -contract C { - struct S { - uint[] x; - } - S s; - function f(bool b) public { - if (b) - s.x[2] |= 1; - } -} -// ---- -// Warning 8115: (73-76): Assertion checker does not yet support the type of this variable. -// Warning 7650: (119-122): Assertion checker does not yet support this expression. -// Warning 8364: (119-120): Assertion checker does not yet implement type struct C.S storage ref -// Warning 9118: (119-125): Assertion checker does not yet implement this expression. -// Warning 9149: (119-130): Assertion checker does not yet implement this assignment operator. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_fixed_bytes.sol new file mode 100644 index 000000000000..fcbcd5d7a836 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_fixed_bytes.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (bytes1) { + bytes1 a = 0xff; + bytes1 b = 0xf0; + b |= a; + assert(a == b); + + a |= ~b; + assert(a == 0); // fails + } +} +// ---- +// Warning 6321: (83-89): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (209-223): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_int.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_int.sol new file mode 100644 index 000000000000..1baec8920608 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_int.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int8 x = 1; + int8 y = 0; + x |= y; + assert(x == 0); // fails + x = -1; y = 3; + x |= y; + assert(x == -1); + x = 4; + y |= x; + assert(y == 7); + y = 127; + x |= y; + assert(x == 127); + } +} +// ---- +// Warning 6328: (114-128): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_int_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_int_1.sol new file mode 100644 index 000000000000..7c198b24eee9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_int_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + int[1][20] c; + function f(bool b) public { + require(c[10][0] == 0); + if (b) + c[10][0] |= 1; + assert(c[10][0] == 0 || c[10][0] == 1); + } +} +// ---- +// Warning 6328: (174-212): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint.sol new file mode 100644 index 000000000000..7222b0c85532 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint v = 7; + v |= 3; + assert(v != 7); // fails, as 7 | 3 = 7 + + uint c = 0; + c |= v; + assert(c == 7); + + uint16 x = 0xff; + uint16 y = 0xffff; + y |= x; + assert(y == 0xff); // fails + assert(y == 0xffff); + + y = 0xf1ff; + x = 0xff00; + x |= y & x; + assert(y == 0xffff); // fails + assert(x == 0xff00); + } +} +// ---- +// Warning 6328: (121-135): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (298-315): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (424-443): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_1.sol new file mode 100644 index 000000000000..7a3441661eb0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + uint[1] c; + function f(bool b) public { + require(c[0] == 0); + if (b) + c[0] |= 1; + assert(c[0] <= 1); + } +} +// ---- +// Warning 6328: (166-183): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_2.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_2.sol new file mode 100644 index 000000000000..b27b468a9f19 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_2.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + struct S { + uint[] x; + } + S s; + function f(bool b) public { + if (b) + s.x[2] |= 1; + // Removed because of Spacer nondeterminism. + //assert(s.x[2] != 1); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_3.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_3.sol new file mode 100644 index 000000000000..b6e9af8a8a44 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_uint_3.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C { + struct S { + uint x; + } + S s; + function f(bool b) public { + s.x |= b ? 1 : 2; + assert(s.x > 0); + } +} +// ---- +// Warning 6328: (157-172): CHC: Assertion violation might happen here. +// Warning 7812: (157-172): BMC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal.sol new file mode 100644 index 000000000000..ef53b86f2568 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes memory y = "def"; + y[0] &= "d"; + assert(y[0] == "d"); + + y[0] |= "e"; + assert(y[0] == "d"); // fails + + y[0] ^= "f"; + assert(y[0] == (bytes1("d") | bytes1("e")) ^ bytes1("f")); + } +} +// ---- +// Warning 6328: (189-208): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_2.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_2.sol new file mode 100644 index 000000000000..a04d3997fa80 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_2.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes3 y = "def"; + y &= "def"; + assert(y == "def"); + + y |= "dee"; + assert(y == "def"); // fails + + y ^= "fed"; + assert(y == (bytes3("def") | bytes3("dee")) ^ bytes3("fed")); + } +} +// ---- +// Warning 6328: (180-198): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_3.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_3.sol new file mode 100644 index 000000000000..43d77c532ed9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_3.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes32 y = "abcdefghabcdefghabcdefghabcdefgh"; + bytes32 z = y; + y &= "bcdefghabcdefghabcdefghabcdefgha"; + z &= "bcdefghabcdefghabcdefghabcdefgha"; + assert(y == "abcdefghabcdefghabcdefghabcdefgh"); // fails + + y |= "cdefghabcdefghabcdefghabcdefghab"; + z |= "cdefghabcdefghabcdefghabcdefghab"; + assert(y == "abcdefghabcdefghabcdefghabcd"); // fails + + y ^= "abcdefghabcdefghabcdefghabcdefgh"; + assert(y == z ^ "abcdefghabcdefghabcdefghabcdefgh"); + } +} +// ---- +// Warning 6328: (262-309): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (427-470): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_1.sol deleted file mode 100644 index 2e07a32f737d..000000000000 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_1.sol +++ /dev/null @@ -1,13 +0,0 @@ -pragma experimental SMTChecker; - -contract C { - function f(bool b) public pure { - uint v = 0; - if (b) - v ^= 1; - assert(v == 1); - } -} -// ---- -// Warning 6328: (116-130): Assertion violation happens here -// Warning 9149: (106-112): Assertion checker does not yet implement this assignment operator. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_fixed_bytes.sol new file mode 100644 index 000000000000..46c43d96f71e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_fixed_bytes.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns (bytes1) { + bytes1 a = 0xff; + bytes1 b = 0xf0; + a ^= ~b; + assert(a == b); + + a ^= ~b; + assert(a != 0xff); // fails + } +} +// ---- +// Warning 6321: (83-89): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (210-227): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_int.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_int.sol new file mode 100644 index 000000000000..eda032cd520e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_int.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + int8 x = 1; + int8 y = 0; + x ^= y; + assert(x != 1); // fails + x = -1; y = 1; + x ^= y; + assert(x == -2); + x = 4; + y ^= x; + assert(y == 5); + } +} +// ---- +// Warning 6328: (114-128): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_uint.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_uint.sol new file mode 100644 index 000000000000..60acee47c64d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_uint.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint v = 7; + v ^= 3; + assert(v != 4); // fails, as 7 ^ 3 = 4 + + uint c = 0; + c ^= v; + assert(c == 4); + + uint16 x = 0xff; + uint16 y = 0xffff; + y ^= x; + assert(y == 0xff); // fails + assert(y == 0xff00); + + y = 0xf1; + x = 0xff00; + y ^= x | y; + assert(y == 0xffff); // fails + assert(x == 0xff00); + } +} +// ---- +// Warning 6328: (121-135): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (298-315): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (422-441): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_mul.sol b/test/libsolidity/smtCheckerTests/operators/compound_mul.sol index 6723e5304c5f..85be56da46c2 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_mul.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_mul.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (150-164): Assertion violation happens here +// Warning 6328: (150-164): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_mul_array_index.sol b/test/libsolidity/smtCheckerTests/operators/compound_mul_array_index.sol index 663f0702baaf..29f50cb07255 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_mul_array_index.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_mul_array_index.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (191-212): Assertion violation happens here +// Warning 6328: (191-212): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 0\np = 38\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(0, 38) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol b/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol index cdfc7500a9c4..c3492d678f9c 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_mul_mapping.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (197-216): Assertion violation happens here +// Warning 6328: (197-216): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\np = 0\n\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_shl_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_shl_1.sol index 964c02b46a2e..8ccc8a8005f0 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_shl_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_shl_1.sol @@ -9,5 +9,3 @@ contract C { } } // ---- -// Warning 6328: (123-136): Assertion violation happens here -// Warning 9149: (112-119): Assertion checker does not yet implement this assignment operator. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_shr_1.sol b/test/libsolidity/smtCheckerTests/operators/compound_shr_1.sol index a42a1e3e675d..a2a7ca5ab1b2 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_shr_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_shr_1.sol @@ -9,5 +9,4 @@ contract C { } } // ---- -// Warning 6328: (117-130): Assertion violation happens here -// Warning 9149: (106-113): Assertion checker does not yet implement this assignment operator. +// Warning 6328: (117-130): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/compound_sub.sol b/test/libsolidity/smtCheckerTests/operators/compound_sub.sol index 609dedd6909a..5b46b0efed9c 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_sub.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_sub.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (150-164): Assertion violation happens here +// Warning 6328: (150-164): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 90\n\n\nTransaction trace:\nconstructor()\nf(90) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_sub_array_index.sol b/test/libsolidity/smtCheckerTests/operators/compound_sub_array_index.sol index 39ebd357bf1d..30e43b2e8169 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_sub_array_index.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_sub_array_index.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (191-212): Assertion violation happens here +// Warning 6328: (191-212): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 90\np = 0\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(90, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/compound_sub_mapping.sol b/test/libsolidity/smtCheckerTests/operators/compound_sub_mapping.sol index 883105dd5800..1ab625eac359 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_sub_mapping.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_sub_mapping.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (197-216): Assertion violation happens here +// Warning 6328: (197-216): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 99\np = 0\n\n\nTransaction trace:\nconstructor()\nf(99, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol new file mode 100644 index 000000000000..752bbac21be0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b) public pure { + uint a = b ? 2 : 3; + assert(a > 2); + } +} +// ---- +// Warning 6328: (104-117): CHC: Assertion violation happens here.\nCounterexample:\n\nb = true\n\n\nTransaction trace:\nconstructor()\nf(true) diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol new file mode 100644 index 000000000000..f11fb944058c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint b) public pure { + require(b < 3); + uint c = (b > 0) ? b++ : ++b; + assert(c == 0); + } +} +// ---- +// Warning 6328: (132-146): CHC: Assertion violation happens here.\nCounterexample:\n\nb = 1\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol new file mode 100644 index 000000000000..e8a4004939bb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_3.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a, uint b) public pure { + require(a < 10); + require(b <= a); + + uint c = (b > 4) ? a++ : b++; + assert(c > a); + } +} +// ---- +// Warning 6328: (161-174): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\nb = 1\n\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol new file mode 100644 index 000000000000..87b48e9820cc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_4.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() public { + x = 3; + } +} +// ---- +// Warning 2072: (273-279): Unused local variable. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol new file mode 100644 index 000000000000..713f407a859c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_5.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + d.d(); + return x; + } + function f() public { + x = 1; + uint y = g(); + assert(x == 2 || x == 1); + } + function h() public { + x = 3; + } +} +// ---- +// Warning 2072: (282-288): Unused local variable. +// Warning 6328: (304-328): CHC: Assertion violation happens here.\nCounterexample:\na = false, x = 3, d = 0\n\n\n\nTransaction trace:\nconstructor()\nState: a = false, x = 0, d = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol new file mode 100644 index 000000000000..d1fe62aac45e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_6.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; + +abstract contract D { + function d() public virtual ; +} + +contract C { + bool a; + uint x; + D d; + function g() public returns (uint) { + x = 2; + d.d(); + return x; + } + function f(bool b) public { + x = 1; + uint y = b ? g() : 3; + assert(x == 2 || x == 1); + } + function h() internal { + x = 3; + } +} +// ---- +// Warning 2072: (288-294): Unused local variable. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol new file mode 100644 index 000000000000..3f3eff79732d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_false.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint b) public pure returns (uint d) { + require(b < 10); + uint c = b < 5 ? 5 : 1; + d = c > 5 ? 3 : 2; + } +} +// ---- +// Warning 6838: (148-153): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol new file mode 100644 index 000000000000..c56fb58659be --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_always_true.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b) public pure { + require(b); + uint c = b ? 5 : 1; + assert(c < 5); + } +} +// ---- +// Warning 6328: (118-131): CHC: Assertion violation happens here.\nCounterexample:\n\nb = true\n\n\nTransaction trace:\nconstructor()\nf(true) +// Warning 6838: (105-106): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol new file mode 100644 index 000000000000..f8e1703c8f40 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a) internal pure returns (bool b) { + b = a > 5; + } + function g(uint a) public pure { + uint c = f(a) ? 3 : 4; + assert(c > 5); + } +} +// ---- +// Warning 6328: (203-216): CHC: Assertion violation happens here.\nCounterexample:\n\na = 6\n\n\nTransaction trace:\nconstructor()\ng(6) diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol new file mode 100644 index 000000000000..e52f85b3566b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_function_2.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a) internal pure returns (uint b) { + require(a < 1000); + return a * a; + } + function g(uint a) internal pure returns (uint b) { + require(a < 1000); + return a + 100; + } + function h(uint a) public pure { + uint c = a < 5 ? g(a) : f(a); + assert(c >= 25); + assert(c < 20); // should fail + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (378-392): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol new file mode 100644 index 000000000000..562631dec6e5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_always_true.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b1, bool b2) public pure { + require(b1 || b2); + uint c = b1 ? 3 : (b2 ? 2 : 1); + assert(c > 1); + } +} +// ---- +// Warning 6838: (147-149): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol new file mode 100644 index 000000000000..add6d0c1090f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_nested_unsafe.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool b1, bool b2) public pure { + uint c = b1 ? 3 : (b2 ? 2 : 1); + assert(c > 1); + } +} +// ---- +// Warning 6328: (141-154): CHC: Assertion violation happens here.\nCounterexample:\n\nb1 = false\nb2 = false\n\n\nTransaction trace:\nconstructor()\nf(false, false) diff --git a/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol new file mode 100644 index 000000000000..3df380fefac4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/conditional_assignment_statevar_1.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + uint a; + bool b; + + function f() public returns(uint c) { + c = b ? a + 1 : a--; + assert(c > a); + } +} +// ---- +// Warning 4984: (129-134): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\na = 115792089237316195423570985008687907853269984665640564039457584007913129639935, b = false\n\nc = 0\n\nTransaction trace:\nconstructor()\nState: a = 0, b = false\nf()\nState: a = 115792089237316195423570985008687907853269984665640564039457584007913129639935, b = false\nf() +// Warning 3944: (137-140): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\na = 0, b = false\n\nc = 0\n\nTransaction trace:\nconstructor()\nState: a = 0, b = false\nf() +// Warning 6328: (150-163): CHC: Assertion violation happens here.\nCounterexample:\na = 115792089237316195423570985008687907853269984665640564039457584007913129639935, b = false\n\nc = 0\n\nTransaction trace:\nconstructor()\nState: a = 0, b = false\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/const_exp_1.sol b/test/libsolidity/smtCheckerTests/operators/const_exp_1.sol new file mode 100644 index 000000000000..d156b9ab3c7f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/const_exp_1.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + uint constant x = 2; + uint constant y = x ** 10; + + function f() public view { + assert(y == 2 ** 10); + assert(y == 1024); + assert(y == 14); // should fail + } +} +// ---- +// Warning 2018: (98-206): Function state mutability can be restricted to pure +// Warning 6328: (172-187): CHC: Assertion violation happens here.\nCounterexample:\nx = 2, y = 1024\n\n\n\nTransaction trace:\nconstructor()\nState: x = 2, y = 1024\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/constant_propagation_1.sol b/test/libsolidity/smtCheckerTests/operators/constant_propagation_1.sol new file mode 100644 index 000000000000..536f437680d3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/constant_propagation_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + uint constant DEPOSIT_CONTRACT_TREE_DEPTH = 32; + uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1; + function f() public pure { + assert(DEPOSIT_CONTRACT_TREE_DEPTH == 32); + assert(MAX_DEPOSIT_COUNT == 4294967295); + assert(MAX_DEPOSIT_COUNT == 2); // should fail + } +} +// ---- +// Warning 6328: (284-314): CHC: Assertion violation happens here.\nCounterexample:\nDEPOSIT_CONTRACT_TREE_DEPTH = 32, MAX_DEPOSIT_COUNT = 4294967295\n\n\n\nTransaction trace:\nconstructor()\nState: DEPOSIT_CONTRACT_TREE_DEPTH = 32, MAX_DEPOSIT_COUNT = 4294967295\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/constant_propagation_2.sol b/test/libsolidity/smtCheckerTests/operators/constant_propagation_2.sol new file mode 100644 index 000000000000..2aadde8e93de --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/constant_propagation_2.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + uint constant x = 7; + uint constant y = 3; + uint constant z = x / y; + + function f() public pure { + assert(z == 2); + assert(z == x / 3); + assert(z == 7 / y); + assert(z * 3 != 7); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array.sol b/test/libsolidity/smtCheckerTests/operators/delete_array.sol index 9c513e140050..d5687299595e 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_array.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_array.sol @@ -15,4 +15,4 @@ contract C } } // ---- -// Warning 6838: (118-119): Condition is always true. +// Warning 6838: (118-119): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array_index.sol b/test/libsolidity/smtCheckerTests/operators/delete_array_index.sol index 517fdf0ae830..f3da3ea29fa3 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_array_index.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_array_index.sol @@ -14,4 +14,4 @@ contract C } } // ---- -// Warning 6838: (119-120): Condition is always false. +// Warning 6838: (119-120): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol index 5627953eb738..f481f9722754 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_array_index_2d.sol @@ -17,4 +17,4 @@ contract C // ==== // SMTSolvers: z3 // ---- -// Warning 6328: (191-211): Assertion violation happens here +// Warning 6328: (191-211): CHC: Assertion violation happens here.\nCounterexample:\na = []\nb = false\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf(false) diff --git a/test/libsolidity/smtCheckerTests/operators/delete_array_push.sol b/test/libsolidity/smtCheckerTests/operators/delete_array_push.sol new file mode 100644 index 000000000000..050d7c6a62c2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/delete_array_push.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; +contract C { + int[][] array2d; + function s() public returns (int[] memory) { + delete array2d.push(); + assert(array2d[array2d.length - 1].length == 0); + // Fails + assert(array2d[array2d.length - 1].length != 0); + delete array2d.push().push(); + uint length = array2d.length; + uint length2 = array2d[length - 1].length; + assert(array2d[length - 1][length2 - 1] == 0); + // Fails + assert(array2d[length - 1][length2 - 1] != 0); + return array2d[2]; + } +} +// ---- +// Warning 6328: (198-245): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[]]\n\n = []\n\nTransaction trace:\nconstructor()\nState: array2d = []\ns() +// Warning 6328: (418-463): CHC: Assertion violation happens here.\nCounterexample:\narray2d = [[], [0]]\n\n = []\n\nTransaction trace:\nconstructor()\nState: array2d = []\ns() diff --git a/test/libsolidity/smtCheckerTests/operators/delete_function.sol b/test/libsolidity/smtCheckerTests/operators/delete_function.sol index 64e397ae11eb..f7a981e50684 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_function.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_function.sol @@ -21,4 +21,4 @@ contract C } } // ---- -// Warning 6838: (201-202): Condition is always true. +// Warning 6838: (201-202): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/operators/delete_multid_array.sol b/test/libsolidity/smtCheckerTests/operators/delete_multid_array.sol index f52da7a3dd4a..9aae7907d450 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_multid_array.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_multid_array.sol @@ -17,7 +17,8 @@ contract C { b[x][y] = v; delete b[x]; // Not necessarily the case. - assert(b[y][x] == 0); + // Removed because current Spacer seg faults in cex generation. + //assert(b[y][x] == 0); } function i(uint x, uint y, uint v) public { b[x][y] = v; @@ -38,7 +39,6 @@ contract C { } } // ==== -// SMTSolvers: cvc4 +// SMTIgnoreCex: yes // ---- -// Warning 4661: (372-392): Assertion violation happens here -// Warning 4661: (617-637): Assertion violation happens here +// Warning 6328: (685-705): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/delete_struct.sol b/test/libsolidity/smtCheckerTests/operators/delete_struct.sol index 5b7be5add4a7..01fa7558b96d 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_struct.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_struct.sol @@ -6,7 +6,7 @@ contract C { uint x; } - function f(bool b) public { + function f(bool b) public pure { S memory s; s.x = 2; if (b) @@ -17,15 +17,3 @@ contract C } } // ---- -// Warning 2018: (73-192): Function state mutability can be restricted to pure -// Warning 6328: (172-188): Assertion violation happens here -// Warning 8115: (103-113): Assertion checker does not yet support the type of this variable. -// Warning 7650: (117-120): Assertion checker does not yet support this expression. -// Warning 8364: (117-118): Assertion checker does not yet implement type struct C.S memory -// Warning 8182: (117-124): Assertion checker does not yet implement such assignments. -// Warning 8364: (145-146): Assertion checker does not yet implement type struct C.S memory -// Warning 7650: (165-168): Assertion checker does not yet support this expression. -// Warning 8364: (165-166): Assertion checker does not yet implement type struct C.S memory -// Warning 2683: (158-168): Assertion checker does not yet implement "delete" for this expression. -// Warning 7650: (179-182): Assertion checker does not yet support this expression. -// Warning 8364: (179-180): Assertion checker does not yet implement type struct C.S memory diff --git a/test/libsolidity/smtCheckerTests/operators/delete_tuple.sol b/test/libsolidity/smtCheckerTests/operators/delete_tuple.sol new file mode 100644 index 000000000000..cb37864d539b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/delete_tuple.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; + +contract A{ + function f() public pure { + delete ([""][0]); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/div_zero.sol b/test/libsolidity/smtCheckerTests/operators/div_zero.sol new file mode 100644 index 000000000000..e88756a66e81 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/div_zero.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + uint z = 0; + uint x = 2 / z; +} +// ---- +// Warning 4281: (69-74): CHC: Division by zero happens here.\nCounterexample:\nz = 0, x = 0\n\nTransaction trace:\nconstructor() diff --git a/test/libsolidity/smtCheckerTests/operators/division_1.sol b/test/libsolidity/smtCheckerTests/operators/division_1.sol index 85e978905730..4de050c87209 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_1.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// Warning 3046: (111-116): Division by zero happens here +// Warning 4281: (111-116): CHC: Division by zero happens here.\nCounterexample:\n\nx = 0\ny = 0\n = 0\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/operators/division_3.sol b/test/libsolidity/smtCheckerTests/operators/division_3.sol index 03af864efd31..034999097254 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_3.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// Warning 2661: (127-132): Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here +// Warning 4984: (127-132): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\ny = (- 1)\n = 0\n\nTransaction trace:\nconstructor()\nf((- 57896044618658097711785492504343953926634992332820282019728792003956564819968), (- 1)) diff --git a/test/libsolidity/smtCheckerTests/operators/division_4.sol b/test/libsolidity/smtCheckerTests/operators/division_4.sol index ce67e3375bf8..90143db213ff 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_4.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_4.sol @@ -6,3 +6,4 @@ contract C { return x / y; } } +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/division_5.sol b/test/libsolidity/smtCheckerTests/operators/division_5.sol index ed6966223363..32f69de7e6db 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_5.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_5.sol @@ -9,3 +9,4 @@ contract C { return c; } } +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/division_6.sol b/test/libsolidity/smtCheckerTests/operators/division_6.sol index 24e7a6325fd0..dac9aa58ae51 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_6.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_6.sol @@ -1,13 +1,13 @@ pragma experimental SMTChecker; contract C { - function mul(uint256 a, uint256 b) internal pure returns (uint256) { + function mul(uint256 a, uint256 b) public pure returns (uint256) { if (a == 0) { return 0; } - // TODO remove when SMTChecker sees that this code is the `else` of the `return`. - require(a != 0); uint256 c = a * b; require(c / a == b); return c; } } +// ---- +// Warning 4984: (160-165): CHC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_1.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_1.sol index cd1365997d1b..ddffc10fda55 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_1.sol @@ -7,4 +7,3 @@ contract C { } } // ---- -// Warning 1218: (107-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol index 6473b98f3720..985e7fdeb106 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_2.sol @@ -7,4 +7,3 @@ contract C { } } // ---- -// Warning 1218: (105-123): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol index c9a932a427b5..58dc8026283f 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_3.sol @@ -7,4 +7,3 @@ contract C { } } // ---- -// Warning 1218: (106-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol index 95743c934d7b..44f34fb2a8f8 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_4.sol @@ -7,4 +7,3 @@ contract C { } } // ---- -// Warning 1218: (106-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol index 2570b375f537..87f0484829c6 100644 --- a/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol +++ b/test/libsolidity/smtCheckerTests/operators/division_truncates_correctly_5.sol @@ -7,4 +7,3 @@ contract C { } } // ---- -// Warning 1218: (107-125): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/exp.sol b/test/libsolidity/smtCheckerTests/operators/exp.sol new file mode 100644 index 000000000000..9c7235296a4e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/exp.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; +contract D { + function f(uint x) public pure { + assert(x**2 == 4); + } +} +// ---- +// Warning 5188: (88-92): Assertion checker does not yet implement this operator. +// Warning 6328: (81-98): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 5188: (88-92): Assertion checker does not yet implement this operator. diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol index c92a1ac5dbe5..c767d1f32694 100644 --- a/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_add.sol @@ -6,7 +6,3 @@ contract test { } // ---- // Warning 2072: (80-88): Unused local variable. -// Warning 5084: (91-100): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (103-112): Type conversion is not yet fully supported and might yield false positives. -// Warning 4144: (91-112): Underflow (resulting value less than 0) happens here -// Warning 2661: (91-112): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol index b815b0c9429e..8a8af3a5c5ea 100644 --- a/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/fixed_point_compound_add.sol @@ -4,5 +4,3 @@ contract C { function f() internal { b[0] += 1; } } // ---- -// Warning 4144: (84-93): Underflow (resulting value less than 0) happens here -// Warning 2661: (84-93): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/operators/function_call_named_arguments.sol b/test/libsolidity/smtCheckerTests/operators/function_call_named_arguments.sol new file mode 100644 index 000000000000..9277a5958d2b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/function_call_named_arguments.sol @@ -0,0 +1,32 @@ +pragma experimental SMTChecker; +library L { + function l(uint x, uint y) internal pure returns (uint) { + return x + y; + } +} + +contract C { + function f(uint u, uint s, bool b) internal pure returns (uint z) { + if (b) + z = u; + else + z = s; + } + + using L for uint; + + function call() public pure { + uint a = 2; + uint b = a.l({y: 3}); + assert(b == 5); + b = L.l({x: 3, y: 3}); + assert(b == 6); + b = f({b: true, u: 1, s: 2}); + assert(b == 1); + b = f({b: false, u: 1, s: 2}); + // Fails, should be 2. + assert(b == 6); + } +} +// ---- +// Warning 6328: (507-521): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ncall() diff --git a/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol index e4550c4dba7b..a08ed25ef356 100644 --- a/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol @@ -1,10 +1,13 @@ pragma experimental SMTChecker; + contract C { - bytes20 x; - function f(bytes16 b) public view { - b[uint8(x[2])]; + function f(uint i) public pure { + bytes memory x = hex"00112233"; + assert(x[0] == 0x00); + assert(x[1] == 0x11); + require(i > 3); + assert(x[i] == 0x00); } } // ---- -// Warning 7989: (116-120): Assertion checker does not yet support index accessing fixed bytes. -// Warning 7989: (108-122): Assertion checker does not yet support index accessing fixed bytes. +// Warning 6328: (215-235): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 4\n\n\nTransaction trace:\nconstructor()\nf(4) diff --git a/test/libsolidity/smtCheckerTests/operators/index_access_for_bytesNN.sol b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytesNN.sol new file mode 100644 index 000000000000..65ea05950cc2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytesNN.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract C { + bytes20 x; + function f(bytes16 b) public view { + b[uint8(x[2])]; + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/index_access_for_string.sol b/test/libsolidity/smtCheckerTests/operators/index_access_for_string.sol new file mode 100644 index 000000000000..fe406a76317a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/index_access_for_string.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint i) public pure { + string memory x = "\x12\x34"; + bytes memory y = bytes(x); + assert(y[0] == 0x12); + assert(y[1] == 0x34); + require(i > 2); + assert(y[i] == 0x00); + } +} +// ---- +// Warning 6328: (248-268): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 3\n\n\nTransaction trace:\nconstructor()\nf(3) diff --git a/test/libsolidity/smtCheckerTests/operators/index_access_side_effect.sol b/test/libsolidity/smtCheckerTests/operators/index_access_side_effect.sol new file mode 100644 index 000000000000..54e3b07f563f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/index_access_side_effect.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + uint[] a; + function h() internal returns (uint[] storage) { + if (a[2] == 0) + a[2] = 3; + return a; + } + function g() public { + h()[2] = 4; + assert(h()[2] == 3); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (191-210): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/integer_new.sol b/test/libsolidity/smtCheckerTests/operators/integer_new.sol new file mode 100644 index 000000000000..53db42141a10 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/integer_new.sol @@ -0,0 +1,36 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint[] memory x = new uint[](0); + assert(x.length == 0); + } + function g() public pure { + uint[] memory x = new uint[](3); + assert(x.length == 3); + assert(x[0] == 0); + assert(x[1] == 0); + assert(x[2] == 0); + } + function h() public pure { + uint[] memory x = new uint[](3); + assert(x.length == 3); + x[0] = 0x12; + x[1] = 0x34; + assert(x[0] == 0x12); + assert(x[1] == 0x34); + // This should be an out-of-bounds assertion. + x[5] = 0xff; + assert(x[5] == 0xff); + } + function h(uint size) public pure { + uint[] memory x = new uint[](size); + assert(x.length == size); + require(size >= 2); + x[0] = 0x12; + x[1] = 0x34; + assert(x[0] == 0x12); + assert(x[1] == 0x34); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/mod.sol b/test/libsolidity/smtCheckerTests/operators/mod.sol index de7f7b135b49..f147d7c0acc5 100644 --- a/test/libsolidity/smtCheckerTests/operators/mod.sol +++ b/test/libsolidity/smtCheckerTests/operators/mod.sol @@ -9,4 +9,3 @@ contract C { } } // ---- -// Warning 1218: (166-182): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/mod_even.sol b/test/libsolidity/smtCheckerTests/operators/mod_even.sol index ab88a7ede1c9..b91b06fa8e80 100644 --- a/test/libsolidity/smtCheckerTests/operators/mod_even.sol +++ b/test/libsolidity/smtCheckerTests/operators/mod_even.sol @@ -9,4 +9,3 @@ contract C } } // ---- -// Warning 1218: (122-142): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/mod_n.sol b/test/libsolidity/smtCheckerTests/operators/mod_n.sol index fb3643107489..9db8855db884 100644 --- a/test/libsolidity/smtCheckerTests/operators/mod_n.sol +++ b/test/libsolidity/smtCheckerTests/operators/mod_n.sol @@ -9,4 +9,3 @@ contract C } } // ---- -// Warning 1218: (126-139): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/mod_n_uint16.sol b/test/libsolidity/smtCheckerTests/operators/mod_n_uint16.sol index 2231d78349eb..eb13ab3a8e14 100644 --- a/test/libsolidity/smtCheckerTests/operators/mod_n_uint16.sol +++ b/test/libsolidity/smtCheckerTests/operators/mod_n_uint16.sol @@ -9,4 +9,3 @@ contract C } } // ---- -// Warning 1218: (130-149): Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/operators/mod_signed.sol b/test/libsolidity/smtCheckerTests/operators/mod_signed.sol new file mode 100644 index 000000000000..8588fcf9af0c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/mod_signed.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function f(int x, int y) public pure { + require(y != 0); + require(x == 42); + int z1 = x % y; + int z2 = -x % y; + assert(z1 == -z2); + assert((x >= 0 && z1 >=0) || (x <= 0 && z1 <= 0)); + } +} +// ---- +// Warning 6328: (163-180): CHC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/operators/named_arguments_in_any_order.sol b/test/libsolidity/smtCheckerTests/operators/named_arguments_in_any_order.sol new file mode 100644 index 000000000000..805698357a2c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/named_arguments_in_any_order.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C { + function f(uint u, string memory s, bool b) internal {} + + function call() public { + f({s: "abc", u: 1, b: true}); + f({s: "abc", b: true, u: 1}); + f({u: 1, s: "abc", b: true}); + f({b: true, s: "abc", u: 1}); + f({u: 1, b: true, s: "abc"}); + f({b: true, u: 1, s: "abc"}); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/named_arguments_overload_in_any_order.sol b/test/libsolidity/smtCheckerTests/operators/named_arguments_overload_in_any_order.sol new file mode 100644 index 000000000000..840033a8b14e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/named_arguments_overload_in_any_order.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + function f(uint u, string memory s, bool b) internal {} + function f(uint u, uint s, uint b) internal {} + + function call() public { + f({s: "abc", u: 1, b: true}); + f({s: "abc", b: true, u: 1}); + f({u: 1, s: "abc", b: true}); + f({b: true, s: "abc", u: 1}); + f({u: 1, b: true, s: "abc"}); + f({b: true, u: 1, s: "abc"}); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/compound_shift_left.sol b/test/libsolidity/smtCheckerTests/operators/shifts/compound_shift_left.sol new file mode 100644 index 000000000000..442bd3a77fa7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/compound_shift_left.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint256 a, uint256 b) internal pure returns (uint256) { + a <<= b; + return a; + } + function t() public pure { + assert(f(0x4266, 0x0) == 0x4266); + assert(f(0x4266, 0x8) == 0x426600); + assert(f(0x4266, 0xf0) == 0x4266000000000000000000000000000000000000000000000000000000000000); + assert(f(0x4266, 0x4266) == 0); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/compound_shift_right.sol b/test/libsolidity/smtCheckerTests/operators/shifts/compound_shift_right.sol new file mode 100644 index 000000000000..75dc0fe0d0e8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/compound_shift_right.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint256 a, uint256 b) internal pure returns (uint256) { + a >>= b; + return a; + } + function t() public pure { + assert(f(0x4266, 0) == 0x4266); + assert(f(0x4266, 0x8) == 0x42); + assert(f(0x4266, 0x11) == 0); + assert(f(57896044618658097711785492504343953926634992332820282019728792003956564819968, 5) == 1809251394333065553493296640760748560207343510400633813116524750123642650624); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_cleanup.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_cleanup.sol new file mode 100644 index 000000000000..e89c1b5c770b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_cleanup.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure returns (uint16 x) { + x = 0xffff; + x += 32; + x = x << 8; + x = x >> 16; + assert(x == 0); + // Fails because x = 0. + assert(x == 10); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 4984: (109-116): CHC: Overflow (resulting value larger than 65535) happens here. +// Warning 6328: (193-208): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_left.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left.sol new file mode 100644 index 000000000000..c489553fe4a0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint256 a, uint256 b) internal pure returns (uint256) { + return a << b; + } + function t() public pure { + assert(f(0x4266, 0x0) == 0x4266); + // Fails because the above is true. + assert(f(0x4266, 0x0) == 0x4268); + + assert(f(0x4266, 0x8) == 0x426600); + // Fails because the above is true. + assert(f(0x4266, 0x8) == 0x120939); + + assert(f(0x4266, 0xf0) == 0x4266000000000000000000000000000000000000000000000000000000000000); + // Fails because the above is true. + assert(f(0x4266, 0xf0) == 0x4266000000000000000000000000000000000000000000000000000000000001); + + assert(f(0x4266, 0x4266) == 0); + // Fails because the above is true. + assert(f(0x4266, 0x4266) == 1); + } +} +// ---- +// Warning 6328: (250-282): CHC: Assertion violation happens here. +// Warning 6328: (363-397): CHC: Assertion violation happens here. +// Warning 6328: (537-630): CHC: Assertion violation happens here. +// Warning 6328: (707-737): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_larger_type.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_larger_type.sol new file mode 100644 index 000000000000..da4f36bf6af8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_larger_type.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure returns (int8) { + uint8 x = 254; + int8 y = 1; + assert(y << x == 0); + // Fails because z = 0. + assert(y << x == 10); + return y << x; + } +} +// ---- +// Warning 6328: (171-191): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_uint32.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_uint32.sol new file mode 100644 index 000000000000..709de607e5f7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_uint32.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint32 a, uint32 b) internal pure returns (uint256) { + return a << b; + } + function t() public pure { + assert(f(0x4266, 0) == 0x4266); + // Fails because the above is true. + assert(f(0x4266, 0) == 0x4267); + + assert(f(0x4266, 0x10) == 0x42660000); + // Fails because the above is true. + assert(f(0x4266, 0x10) == 0x426600000); + + assert(f(0x4266, 0x11) == 0x84cc0000); + // Fails because the above is true. + assert(f(0x4266, 0x11) == 0x84cc000); + + assert(f(0x4266, 0x20) == 0); + // Fails because the above is true. + assert(f(0x4266, 0x20) == 1); + } +} +// ---- +// Warning 6328: (246-276): CHC: Assertion violation happens here. +// Warning 6328: (360-398): CHC: Assertion violation happens here. +// Warning 6328: (482-518): CHC: Assertion violation happens here. +// Warning 6328: (593-621): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_uint8.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_uint8.sol new file mode 100644 index 000000000000..03658f2367e1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_left_uint8.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint8 a, uint8 b) internal pure returns (uint256) { + return a << b; + } + function t() public pure { + assert(f(0x66, 0x0) == 0x66); + // Fails because the above is true. + assert(f(0x66, 0x0) == 0x660); + + assert(f(0x66, 0x8) == 0); + // Fails because the above is true. + assert(f(0x66, 0x8) == 1); + } +} +// ---- +// Warning 6328: (242-271): CHC: Assertion violation happens here. +// Warning 6328: (343-368): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_overflow.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_overflow.sol new file mode 100644 index 000000000000..e636b8968a9e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_overflow.sol @@ -0,0 +1,39 @@ +pragma experimental SMTChecker; + +contract C { + function leftU(uint8 x, uint8 y) internal pure returns (uint8) { + return x << y; + } + + function leftS(int8 x, uint8 y) internal pure returns (int8) { + return x << y; + } + + function t() public pure { + assert(leftU(255, 8) == 0); + // Fails because the above is true. + assert(leftU(255, 8) == 1); + + assert(leftU(255, 1) == 254); + // Fails because the above is true. + assert(leftU(255, 1) == 255); + + assert(leftU(255, 0) == 255); + // Fails because the above is true. + assert(leftU(255, 0) == 0); + + assert(leftS(1, 7) == -128); + // Fails because the above is true. + assert(leftS(1, 7) == 127); + + assert(leftS(1, 6) == 64); + // Fails because the above is true. + assert(leftS(1, 6) == -64); + } +} +// ---- +// Warning 6328: (340-366): CHC: Assertion violation happens here. +// Warning 6328: (441-469): CHC: Assertion violation happens here. +// Warning 6328: (544-570): CHC: Assertion violation happens here. +// Warning 6328: (644-670): CHC: Assertion violation happens here. +// Warning 6328: (742-768): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right.sol new file mode 100644 index 000000000000..da8c52414ef1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint256 a, uint256 b) internal pure returns (uint256) { + return a >> b; + } + function t() public pure { + assert(f(0x4266, 0) == 0x4266); + // Fails because the above is true. + assert(f(0x4266, 0) == 0x426); + + assert(f(0x4266, 0x8) == 0x42); + // Fails because the above is true. + assert(f(0x4266, 0x8) == 0x420); + + assert(f(0x4266, 0x11) == 0); + // Fails because the above is true. + assert(f(0x4266, 0x11) == 1); + + assert(f(57896044618658097711785492504343953926634992332820282019728792003956564819968, 5) == 1809251394333065553493296640760748560207343510400633813116524750123642650624); + // Fails because the above is true. + assert(f(57896044618658097711785492504343953926634992332820282019728792003956564819968, 5) == 0); + } +} +// ---- +// Warning 6328: (248-277): CHC: Assertion violation happens here. +// Warning 6328: (354-385): CHC: Assertion violation happens here. +// Warning 6328: (460-488): CHC: Assertion violation happens here. +// Warning 6328: (706-802): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_literal.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_literal.sol new file mode 100644 index 000000000000..4acb0dffd202 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_literal.sol @@ -0,0 +1,40 @@ +pragma experimental SMTChecker; + +contract C { + function f(int16 x, uint16 y, int16 z) internal pure returns (bool) { + return x >> y == z; + } + + function t() public pure { + assert(f(-4266, 0, -4266)); + // Fails because the above is true. + assert(f(-4266, 0, -426)); + + assert(f(-4266, 1, -2133)); + // Fails because the above is true. + assert(f(-4266, 1, -2134)); + + assert(f(-4266, 4, -267)); + // Fails because the above is true. + assert(f(-4266, 4, -2670)); + + assert(f(-4266, 8, -17)); + // Fails because the above is true. + assert(f(-4266, 8, -1)); + + assert(f(-4266, 16, -1)); + // Fails because the above is true. + assert(f(-4266, 16, -0)); + + assert(f(-4266, 17, -1)); + // Fails because the above is true. + assert(f(-4266, 17, -0)); + } +} +// ---- +// Warning 6328: (241-266): CHC: Assertion violation happens here. +// Warning 6328: (339-365): CHC: Assertion violation happens here. +// Warning 6328: (437-463): CHC: Assertion violation happens here. +// Warning 6328: (534-557): CHC: Assertion violation happens here. +// Warning 6328: (628-652): CHC: Assertion violation happens here. +// Warning 6328: (723-747): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue.sol new file mode 100644 index 000000000000..79a0eeedff2d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue.sol @@ -0,0 +1,40 @@ +pragma experimental SMTChecker; + +contract C { + function f(int256 a, uint256 b) internal pure returns (int256) { + return a >> b; + } + function t() public pure { + assert(f(-4266, 0) == -4266); + // Fails because the above is true. + assert(f(-4266, 0) == -426); + + assert(f(-4266, 1) == -2133); + // Fails because the above is true. + assert(f(-4266, 1) == -21330); + + assert(f(-4266, 4) == -267); + // Fails because the above is true. + assert(f(-4266, 4) == -255); + + assert(f(-4266, 8) == -17); + // Fails because the above is true. + assert(f(-4266, 8) == -1); + + assert(f(-4266, 16) == -1); + // Fails because the above is true. + assert(f(-4266, 16) == 0); + + assert(f(-4266, 17) == -1); + // Fails because the above is true. + assert(f(-4266, 17) == 0); + } + +} +// ---- +// Warning 6328: (244-271): CHC: Assertion violation happens here. +// Warning 6328: (346-375): CHC: Assertion violation happens here. +// Warning 6328: (449-476): CHC: Assertion violation happens here. +// Warning 6328: (549-574): CHC: Assertion violation happens here. +// Warning 6328: (647-672): CHC: Assertion violation happens here. +// Warning 6328: (745-770): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int16.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int16.sol new file mode 100644 index 000000000000..28008e6de9d0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int16.sol @@ -0,0 +1,39 @@ +pragma experimental SMTChecker; + +contract C { + function f(int16 a, uint16 b) internal pure returns (int256) { + return a >> b; + } + function t() public pure { + assert(f(-4266, 0) == -4266); + // Fails because the above is true. + assert(f(-4266, 0) == -426); + + assert(f(-4266, 1) == -2133); + // Fails because the above is true. + assert(f(-4266, 1) == -21330); + + assert(f(-4266, 4) == -267); + // Fails because the above is true. + assert(f(-4266, 4) == -255); + + assert(f(-4266, 8) == -17); + // Fails because the above is true. + assert(f(-4266, 8) == -1); + + assert(f(-4266, 16) == -1); + // Fails because the above is true. + assert(f(-4266, 16) == 0); + + assert(f(-4266, 17) == -1); + // Fails because the above is true. + assert(f(-4266, 17) == 0); + } +} +// ---- +// Warning 6328: (242-269): CHC: Assertion violation happens here. +// Warning 6328: (344-373): CHC: Assertion violation happens here. +// Warning 6328: (447-474): CHC: Assertion violation happens here. +// Warning 6328: (547-572): CHC: Assertion violation happens here. +// Warning 6328: (645-670): CHC: Assertion violation happens here. +// Warning 6328: (743-768): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int32.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int32.sol new file mode 100644 index 000000000000..62332e446d76 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int32.sol @@ -0,0 +1,39 @@ +pragma experimental SMTChecker; + +contract C { + function f(int32 a, uint32 b) internal pure returns (int256) { + return a >> b; + } + function t() public pure { + assert(f(-4266, 0) == -4266); + // Fails because the above is true. + assert(f(-4266, 0) == -426); + + assert(f(-4266, 1) == -2133); + // Fails because the above is true. + assert(f(-4266, 1) == -21330); + + assert(f(-4266, 4) == -267); + // Fails because the above is true. + assert(f(-4266, 4) == -255); + + assert(f(-4266, 8) == -17); + // Fails because the above is true. + assert(f(-4266, 8) == -1); + + assert(f(-4266, 16) == -1); + // Fails because the above is true. + assert(f(-4266, 16) == 0); + + assert(f(-4266, 17) == -1); + // Fails because the above is true. + assert(f(-4266, 17) == 0); + } +} +// ---- +// Warning 6328: (242-269): CHC: Assertion violation happens here. +// Warning 6328: (344-373): CHC: Assertion violation happens here. +// Warning 6328: (447-474): CHC: Assertion violation happens here. +// Warning 6328: (547-572): CHC: Assertion violation happens here. +// Warning 6328: (645-670): CHC: Assertion violation happens here. +// Warning 6328: (743-768): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int8.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int8.sol new file mode 100644 index 000000000000..103bf22d381a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_negative_lvalue_int8.sol @@ -0,0 +1,39 @@ +pragma experimental SMTChecker; + +contract C { + function f(int8 a, uint8 b) internal pure returns (int256) { + return a >> b; + } + function t() public pure { + assert(f(-66, 0) == -66); + // Fails because the above is true. + assert(f(-66, 0) == -6); + + assert(f(-66, 1) == -33); + // Fails because the above is true. + assert(f(-66, 1) == -3); + + assert(f(-66, 4) == -5); + // Fails because the above is true. + assert(f(-66, 4) == -2); + + assert(f(-66, 8) == -1); + // Fails because the above is true. + assert(f(-66, 8) == -2); + + assert(f(-66, 16) == -1); + // Fails because the above is true. + assert(f(-66, 16) == 0); + + assert(f(-66, 17) == -1); + // Fails because the above is true. + assert(f(-66, 17) == 0); + } +} +// ---- +// Warning 6328: (236-259): CHC: Assertion violation happens here. +// Warning 6328: (330-353): CHC: Assertion violation happens here. +// Warning 6328: (423-446): CHC: Assertion violation happens here. +// Warning 6328: (516-539): CHC: Assertion violation happens here. +// Warning 6328: (610-633): CHC: Assertion violation happens here. +// Warning 6328: (704-727): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_uint32.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_uint32.sol new file mode 100644 index 000000000000..1d7269f6c146 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_uint32.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint32 a, uint32 b) internal pure returns (uint256) { + return a >> b; + } + function t() public pure { + assert(f(0x4266, 0) == 0x4266); + // Fails because the above is true. + assert(f(0x4266, 0) == 0x426); + + assert(f(0x4266, 8) == 0x42); + // Fails because the above is true. + assert(f(0x4266, 8) == 0x420); + + assert(f(0x4266, 0x10) == 0); + // Fails because the above is true. + assert(f(0x4266, 0x10) == 255); + + assert(f(0x4266, 0x11) == 0); + // Fails because the above is true. + assert(f(0x4266, 0x11) == 255); + } +} +// ---- +// Warning 6328: (246-275): CHC: Assertion violation happens here. +// Warning 6328: (350-379): CHC: Assertion violation happens here. +// Warning 6328: (454-484): CHC: Assertion violation happens here. +// Warning 6328: (559-589): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_uint8.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_uint8.sol new file mode 100644 index 000000000000..677f97081967 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_right_uint8.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint8 a, uint8 b) internal pure returns (uint256) { + return a >> b; + } + function t() public pure { + assert(f(0x66, 0) == 0x66); + // Fails because the above is true. + assert(f(0x66, 0) == 0x6); + + assert(f(0x66, 8) == 0); + // Fails because the above is true. + assert(f(0x66, 8) == 1); + } +} +// ---- +// Warning 6328: (240-265): CHC: Assertion violation happens here. +// Warning 6328: (335-358): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shift_underflow_negative_rvalue.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shift_underflow_negative_rvalue.sol new file mode 100644 index 000000000000..56591e697bba --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shift_underflow_negative_rvalue.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract C { + function f(int256 a, uint256 b) internal pure returns (int256) { + return a << b; + } + + function g(int256 a, uint256 b) internal pure returns (int256) { + return a >> b; + } + + function t() public pure { + assert(f(1, 2**256 - 1) == 0); + // Fails because the above is true. + assert(f(1, 2**256 - 1) == 1); + + assert(g(1, 2**256 - 1) == 0); + // Fails because the above is true. + assert(g(1, 2**256 - 1) == 1); + } +} +// ---- +// Warning 6328: (345-374): CHC: Assertion violation happens here. +// Warning 6328: (450-479): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/shifts/shr_unused.sol b/test/libsolidity/smtCheckerTests/operators/shifts/shr_unused.sol new file mode 100644 index 000000000000..d9e78865f89a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/shifts/shr_unused.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + fixed x; + assert(x >>> 6 == 0); + } +} +// ---- +// UnimplementedFeatureError: Not yet implemented - FixedPointType. diff --git a/test/libsolidity/smtCheckerTests/operators/slice.sol b/test/libsolidity/smtCheckerTests/operators/slice.sol new file mode 100644 index 000000000000..08af575de627 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/slice.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f(bytes calldata b) external pure { + require(b[10] == 0xff); + assert(bytes(b[10:20]).length == 10); + assert(bytes(b[10:20])[0] == 0xff); + // Disabled because of Spacer nondeterminism + //assert(bytes(b[10:20])[5] == 0xff); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/operators/slice_bytes.sol b/test/libsolidity/smtCheckerTests/operators/slice_bytes.sol new file mode 100644 index 000000000000..d52106e6b6ad --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/slice_bytes.sol @@ -0,0 +1,6 @@ +pragma experimental SMTChecker; +contract C { + function f(bytes calldata b) external pure { + ((b[:])[5]); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/slice_default_end.sol b/test/libsolidity/smtCheckerTests/operators/slice_default_end.sol new file mode 100644 index 000000000000..9bf06e68ec44 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/slice_default_end.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + function f(bytes calldata b) external pure { + require(b.length == 30); + require(b[10] == 0xff); + require(b[b.length - 1] == 0xaa); + assert(bytes(b[10:]).length == 20); + assert(bytes(b[10:])[0] == 0xff); + //assert(bytes(b[10:])[5] == 0xff); // Removed because of Spacer's nondeterminism + //assert(bytes(b[10:])[19] == 0xaa); // Removed because of Spacer nondeterminism + } +} +// ---- +// Warning 6328: (221-253): CHC: Assertion violation might happen here. +// Warning 4661: (221-253): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/slice_default_start.sol b/test/libsolidity/smtCheckerTests/operators/slice_default_start.sol new file mode 100644 index 000000000000..79de4639ca2a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/slice_default_start.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C { + function f(bytes calldata b) external pure { + require(b[0] == 0xff); + assert(bytes(b[:20]).length == 20); + assert(bytes(b[:20])[0] == 0xff); + assert(bytes(b[:20])[5] == 0xff); + } +} +// ---- +// Warning 6328: (157-189): CHC: Assertion violation might happen here. +// Warning 6328: (193-225): CHC: Assertion violation happens here. +// Warning 4661: (157-189): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/slices_1.sol b/test/libsolidity/smtCheckerTests/operators/slices_1.sol index 15e3c8e171f0..1f5fcf4e12cd 100644 --- a/test/libsolidity/smtCheckerTests/operators/slices_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/slices_1.sol @@ -8,6 +8,3 @@ contract C { } } // ---- -// Warning 2923: (94-109): Assertion checker does not yet implement this expression. -// Warning 2923: (113-128): Assertion checker does not yet implement this expression. -// Warning 2923: (132-165): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/operators/tuple_rationals_conditional.sol b/test/libsolidity/smtCheckerTests/operators/tuple_rationals_conditional.sol new file mode 100644 index 000000000000..cdcff3f4fa00 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/tuple_rationals_conditional.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f(bool x) public pure { + (uint a, uint b) = x ? (10000000001, 2) : (3, 4); + assert(a != 0); + assert(b != 0); + assert(a % 2 == 1); + assert(b % 2 == 0); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add.sol b/test/libsolidity/smtCheckerTests/operators/unary_add.sol index 1a1ae611d678..eef32e9d1346 100644 --- a/test/libsolidity/smtCheckerTests/operators/unary_add.sol +++ b/test/libsolidity/smtCheckerTests/operators/unary_add.sol @@ -14,4 +14,4 @@ contract C } } // ---- -// Warning 6328: (194-207): Assertion violation happens here +// Warning 6328: (194-207): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add_array.sol b/test/libsolidity/smtCheckerTests/operators/unary_add_array.sol index 96e43ff8f544..401e4af62690 100644 --- a/test/libsolidity/smtCheckerTests/operators/unary_add_array.sol +++ b/test/libsolidity/smtCheckerTests/operators/unary_add_array.sol @@ -15,4 +15,4 @@ contract C } } // ---- -// Warning 6328: (240-253): Assertion violation happens here +// Warning 6328: (240-253): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add_array_push_1.sol b/test/libsolidity/smtCheckerTests/operators/unary_add_array_push_1.sol new file mode 100644 index 000000000000..db823c29e76e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_add_array_push_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + uint[] x; + function f() public { + require(x.length == 0); + ++x.push(); + assert(x.length == 1); + assert(x[0] == 1); // should hold + assert(x[0] == 42); // should fail + } +} +// ---- +// Warning 6328: (182-200): CHC: Assertion violation happens here.\nCounterexample:\nx = [1]\n\n\n\nTransaction trace:\nconstructor()\nState: x = []\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add_array_push_2.sol b/test/libsolidity/smtCheckerTests/operators/unary_add_array_push_2.sol new file mode 100644 index 000000000000..1aaa8a80f938 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_add_array_push_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + int[][] d; + } + S[] data; + function f() public { + ++data[1].d[3].push(); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add_mapping.sol b/test/libsolidity/smtCheckerTests/operators/unary_add_mapping.sol index cdf4cbdbd706..ac136acd2425 100644 --- a/test/libsolidity/smtCheckerTests/operators/unary_add_mapping.sol +++ b/test/libsolidity/smtCheckerTests/operators/unary_add_mapping.sol @@ -14,5 +14,7 @@ contract C assert(b < 3); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (244-257): Assertion violation happens here +// Warning 6328: (244-257): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add_minus_overflow_detected.sol b/test/libsolidity/smtCheckerTests/operators/unary_add_minus_overflow_detected.sol new file mode 100644 index 000000000000..79ecefd42333 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_add_minus_overflow_detected.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; +contract C { + uint8 x; + + function inc_pre() public { + ++x; + } + + function dec_pre() public { + --x; + } + + /* Commented out because Spacer segfaults in Z3 4.8.9 + function inc_post() public { + x++; + } + + function dec_post() public { + x--; + } + */ +} +// ---- +// Warning 4984: (87-90): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\nx = 255\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ndec_pre()\nState: x = 255\ninc_pre() +// Warning 3944: (127-130): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ndec_pre() diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add_overflows_correctly.sol b/test/libsolidity/smtCheckerTests/operators/unary_add_overflows_correctly.sol new file mode 100644 index 000000000000..c06b45196142 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_add_overflows_correctly.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + uint8 x = 254; + + function inc_pre() public { + ++x; + } + + function check() view public { + uint y = x; + assert(y < 256); + } +} +// ---- +// Warning 4984: (94-97): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\nx = 255\n\n\n\nTransaction trace:\nconstructor()\nState: x = 254\ninc_pre()\nState: x = 255\ninc_pre() diff --git a/test/libsolidity/smtCheckerTests/operators/unary_add_overflows_correctly_struct.sol b/test/libsolidity/smtCheckerTests/operators/unary_add_overflows_correctly_struct.sol new file mode 100644 index 000000000000..35c835f8b54a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_add_overflows_correctly_struct.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint8 x; + } + + S s; + + constructor() { + s.x = 254; + } + + function inc_pre() public { + ++s.x; + } + + function check() view public { + uint y = s.x; + assert(y < 256); + } +} +// ---- +// Warning 4984: (145-150): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\ns = {x: 255}\n\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 254}\ninc_pre()\nState: s = {x: 255}\ninc_pre() diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol new file mode 100644 index 000000000000..69411e3c2b9b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_1.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++(x); + if (b) --(x); + if (b) delete(b); + assert(x == 0); + assert(!b); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol new file mode 100644 index 000000000000..c16f01402529 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_2.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++((((((x)))))); + if (b) --((((((x)))))); + if (b) delete((((((b)))))); + assert(x == 0); + assert(!b); + } +} diff --git a/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol new file mode 100644 index 000000000000..e3ed7a1789cf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/operators/unary_operators_tuple_3.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f(bool b) public pure { + uint x; + if (b) ++(x); + else x += 1; + assert(x == 1); + assert(!b); + } +} +// ---- +// Warning 6328: (140-150): CHC: Assertion violation happens here.\nCounterexample:\n\nb = true\n\n\nTransaction trace:\nconstructor()\nf(true) diff --git a/test/libsolidity/smtCheckerTests/operators/unary_sub.sol b/test/libsolidity/smtCheckerTests/operators/unary_sub.sol index dba43ac8733e..d15aaf845e56 100644 --- a/test/libsolidity/smtCheckerTests/operators/unary_sub.sol +++ b/test/libsolidity/smtCheckerTests/operators/unary_sub.sol @@ -14,4 +14,4 @@ contract C } } // ---- -// Warning 6328: (194-207): Assertion violation happens here +// Warning 6328: (194-207): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/operators/unary_sub_array.sol b/test/libsolidity/smtCheckerTests/operators/unary_sub_array.sol index 73393df91264..0b5a6e59ea1b 100644 --- a/test/libsolidity/smtCheckerTests/operators/unary_sub_array.sol +++ b/test/libsolidity/smtCheckerTests/operators/unary_sub_array.sol @@ -15,4 +15,4 @@ contract C } } // ---- -// Warning 6328: (240-253): Assertion violation happens here +// Warning 6328: (240-253): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 0\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(0) diff --git a/test/libsolidity/smtCheckerTests/operators/unary_sub_mapping.sol b/test/libsolidity/smtCheckerTests/operators/unary_sub_mapping.sol index 22191a935c09..0c40ad5445dc 100644 --- a/test/libsolidity/smtCheckerTests/operators/unary_sub_mapping.sol +++ b/test/libsolidity/smtCheckerTests/operators/unary_sub_mapping.sol @@ -15,4 +15,4 @@ contract C } } // ---- -// Warning 6328: (244-257): Assertion violation happens here +// Warning 6328: (244-257): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol index 86697dea0b65..f27ffa88935e 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_mul.sol @@ -13,5 +13,5 @@ contract C } } // ---- -// Warning 2661: (120-125): Overflow (resulting value larger than 255) happens here -// Warning 2661: (163-168): Overflow (resulting value larger than 255) happens here +// Warning 4984: (120-125): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\n\nx = 100\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 4984: (163-168): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\n\nx = 128\n = 0\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_mul_cex_with_array.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_cex_with_array.sol new file mode 100644 index 000000000000..d2452f7ce884 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_cex_with_array.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(bytes calldata x, uint y) external pure { + x[8][0]; + x[8][5*y]; + } +} +// ---- +// Warning 4984: (118-121): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\ny = 23158417847463239084714197001737581570653996933128112807891516801582625927988\n\n\nTransaction trace:\nconstructor()\nf(x, 23158417847463239084714197001737581570653996933128112807891516801582625927988) diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol index 7663fa34ec6a..2c02bbed923c 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_mul_signed.sol @@ -12,5 +12,5 @@ contract C } } // ---- -// Warning 2661: (117-122): Overflow (resulting value larger than 127) happens here -// Warning 2661: (150-157): Overflow (resulting value larger than 127) happens here +// Warning 4984: (117-122): CHC: Overflow (resulting value larger than 127) happens here.\nCounterexample:\n\nx = 100\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 4984: (150-157): CHC: Overflow (resulting value larger than 127) happens here.\nCounterexample:\n\nx = 100\n = 0\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol index 3eb3fd13f507..78ff0c1375dc 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_sum.sol @@ -14,5 +14,6 @@ contract C } } // ---- -// Warning 2661: (154-159): Overflow (resulting value larger than 255) happens here -// Warning 2661: (185-192): Overflow (resulting value larger than 255) happens here +// Warning 4984: (109-116): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\n\nx = 1\n = 0\n\nTransaction trace:\nconstructor()\nf(1) +// Warning 4984: (154-159): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\n\nx = 255\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 4984: (185-192): CHC: Overflow (resulting value larger than 255) happens here.\nCounterexample:\n\nx = 255\n = 0\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol b/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol index 7e9938c74413..bee06174d7ed 100644 --- a/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/overflow_sum_signed.sol @@ -14,6 +14,7 @@ contract C } } // ---- -// Warning 2661: (117-122): Overflow (resulting value larger than 127) happens here -// Warning 2661: (151-158): Overflow (resulting value larger than 127) happens here -// Warning 4144: (197-205): Underflow (resulting value less than -128) happens here +// Warning 6321: (87-91): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 4984: (117-122): CHC: Overflow (resulting value larger than 127) happens here.\nCounterexample:\n\nx = 127\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 4984: (151-158): CHC: Overflow (resulting value larger than 127) happens here.\nCounterexample:\n\nx = 127\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 3944: (197-205): CHC: Underflow (resulting value less than -128) happens here.\nCounterexample:\n\nx = (- 127)\n = 0\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/overflow/safe_add_1.sol b/test/libsolidity/smtCheckerTests/overflow/safe_add_1.sol index 97e7ea34320f..df071a9c1afc 100644 --- a/test/libsolidity/smtCheckerTests/overflow/safe_add_1.sol +++ b/test/libsolidity/smtCheckerTests/overflow/safe_add_1.sol @@ -7,3 +7,5 @@ contract C return x + y; } } +// ---- +// Warning 4984: (115-120): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 1\ny = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n = 0\n\nTransaction trace:\nconstructor()\nadd(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/overflow/safe_add_2.sol b/test/libsolidity/smtCheckerTests/overflow/safe_add_2.sol index 9b64a888572e..98951f488123 100644 --- a/test/libsolidity/smtCheckerTests/overflow/safe_add_2.sol +++ b/test/libsolidity/smtCheckerTests/overflow/safe_add_2.sol @@ -8,3 +8,5 @@ contract C return z; } } +// ---- +// Warning 4984: (116-121): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 1\ny = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n = 0\n\nTransaction trace:\nconstructor()\nadd(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol new file mode 100644 index 000000000000..fb67403e4098 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_div_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x / y; + } +} +// ---- +// Warning 4281: (110-115): CHC: Division by zero happens here.\nCounterexample:\n\nx = 0\ny = 0\n = 0\n\nTransaction trace:\nconstructor()\nf(0, 0) +// Warning 4984: (110-115): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol new file mode 100644 index 000000000000..a13d2db3d5fb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sub_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + require(x >= y); + return x - y; + } +} +// ---- +// Warning 4984: (129-134): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = 0\ny = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n = 0\n\nTransaction trace:\nconstructor()\nf(0, (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)) diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol new file mode 100644 index 000000000000..d4dede3afc4b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_guard_sum_overflow.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + require(x + y >= x); + return x + y; + } +} +// ---- +// Warning 3944: (111-116): CHC: Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here.\nCounterexample:\n\nx = (- 1)\ny = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n = 0\n\nTransaction trace:\nconstructor()\nf((- 1), (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)) +// Warning 4984: (111-116): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = 1\ny = 57896044618658097711785492504343953926634992332820282019728792003956564819967\n = 0\n\nTransaction trace:\nconstructor()\nf(1, 57896044618658097711785492504343953926634992332820282019728792003956564819967) +// Warning 3944: (133-138): CHC: Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here.\nCounterexample:\n\nx = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\ny = (- 1)\n = 0\n\nTransaction trace:\nconstructor()\nf((- 57896044618658097711785492504343953926634992332820282019728792003956564819968), (- 1)) diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol new file mode 100644 index 000000000000..b14730966419 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_mod_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x % y; + } +} +// ---- +// Warning 4281: (110-115): CHC: Division by zero happens here.\nCounterexample:\n\nx = 0\ny = 0\n = 0\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol new file mode 100644 index 000000000000..ff91b206d91e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_mul_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x * y; + } +} +// ---- +// Warning 3944: (110-115): CHC: Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here.\nCounterexample:\n\nx = (- 3)\ny = 19298681539552699237261830834781317975544997444273427339909597334652188273323\n = 0\n\nTransaction trace:\nconstructor()\nf((- 3), 19298681539552699237261830834781317975544997444273427339909597334652188273323) +// Warning 4984: (110-115): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = (- 1)\ny = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n = 0\n\nTransaction trace:\nconstructor()\nf((- 1), (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)) diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol new file mode 100644 index 000000000000..945a620b678b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_sub_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x - y; + } +} +// ---- +// Warning 3944: (110-115): CHC: Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here.\nCounterexample:\n\nx = (- 2)\ny = 57896044618658097711785492504343953926634992332820282019728792003956564819967\n = 0\n\nTransaction trace:\nconstructor()\nf((- 2), 57896044618658097711785492504343953926634992332820282019728792003956564819967) +// Warning 4984: (110-115): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = 0\ny = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n = 0\n\nTransaction trace:\nconstructor()\nf(0, (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)) diff --git a/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol new file mode 100644 index 000000000000..f9492549fa02 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/signed_sum_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(int x, int y) public pure returns (int) { + return x + y; + } +} +// ---- +// Warning 3944: (110-115): CHC: Underflow (resulting value less than -57896044618658097711785492504343953926634992332820282019728792003956564819968) happens here.\nCounterexample:\n\nx = (- 1)\ny = (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)\n = 0\n\nTransaction trace:\nconstructor()\nf((- 1), (- 57896044618658097711785492504343953926634992332820282019728792003956564819968)) +// Warning 4984: (110-115): CHC: Overflow (resulting value larger than 0x80 * 2**248 - 1) happens here.\nCounterexample:\n\nx = 1\ny = 57896044618658097711785492504343953926634992332820282019728792003956564819967\n = 0\n\nTransaction trace:\nconstructor()\nf(1, 57896044618658097711785492504343953926634992332820282019728792003956564819967) diff --git a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol index 117554ecab46..7e5f88a79970 100644 --- a/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol +++ b/test/libsolidity/smtCheckerTests/overflow/simple_overflow.sol @@ -3,4 +3,4 @@ contract C { function f(uint a, uint b) public pure returns (uint) { return a + b; } } // ---- -// Warning 2661: (112-117): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (112-117): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\na = 1\nb = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n = 0\n\nTransaction trace:\nconstructor()\nf(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol b/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol index 1029e90a5be8..c62097b5d2fb 100644 --- a/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol +++ b/test/libsolidity/smtCheckerTests/overflow/underflow_sub.sol @@ -12,5 +12,5 @@ contract C } } // ---- -// Warning 4144: (117-122): Underflow (resulting value less than 0) happens here -// Warning 4144: (150-157): Underflow (resulting value less than 0) happens here +// Warning 3944: (117-122): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\nx = 0\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 3944: (150-157): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\nx = 0\n = 0\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol b/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol index 84634a213e99..7b14621f984b 100644 --- a/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol +++ b/test/libsolidity/smtCheckerTests/overflow/underflow_sub_signed.sol @@ -16,6 +16,6 @@ contract C } } // ---- -// Warning 4144: (116-123): Underflow (resulting value less than -128) happens here -// Warning 4144: (163-170): Underflow (resulting value less than -128) happens here -// Warning 2661: (207-217): Overflow (resulting value larger than 127) happens here +// Warning 3944: (116-123): CHC: Underflow (resulting value less than -128) happens here.\nCounterexample:\n\nx = (- 2)\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 3944: (163-170): CHC: Underflow (resulting value less than -128) happens here.\nCounterexample:\n\nx = (- 128)\n = 0\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 4984: (207-217): CHC: Overflow (resulting value larger than 127) happens here.\nCounterexample:\n\nx = 127\n = 0\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol new file mode 100644 index 000000000000..c8011f8e0684 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_div_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x / y; + } +} +// ---- +// Warning 4281: (113-118): CHC: Division by zero happens here.\nCounterexample:\n\nx = 0\ny = 0\n = 0\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol new file mode 100644 index 000000000000..879587e06cf0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sub_overflow.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + require(x >= y); + return x - y; + } +} diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol new file mode 100644 index 000000000000..45e76fb89cd6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_guard_sum_overflow.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + require(x + y >= x); + return x + y; + } +} +// ---- +// Warning 4984: (114-119): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 1\ny = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n = 0\n\nTransaction trace:\nconstructor()\nf(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol new file mode 100644 index 000000000000..1cfbdfebbbe5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mod_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x % y; + } +} +// ---- +// Warning 4281: (113-118): CHC: Division by zero happens here.\nCounterexample:\n\nx = 0\ny = 0\n = 0\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol new file mode 100644 index 000000000000..7d2a0c189f90 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_mul_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x * y; + } +} +// ---- +// Warning 4984: (113-118): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 57896044618658097711785492504343953926634992332820282019728792003956564819968\ny = 2\n = 0\n\nTransaction trace:\nconstructor()\nf(57896044618658097711785492504343953926634992332820282019728792003956564819968, 2) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol new file mode 100644 index 000000000000..748db3d4b43b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_sub_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x - y; + } +} +// ---- +// Warning 3944: (113-118): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\nx = 0\ny = 1\n = 0\n\nTransaction trace:\nconstructor()\nf(0, 1) diff --git a/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol b/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol new file mode 100644 index 000000000000..89801a5cfde2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/overflow/unsigned_sum_overflow.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint x, uint y) public pure returns (uint) { + return x + y; + } +} +// ---- +// Warning 4984: (113-118): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\nx = 1\ny = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n = 0\n\nTransaction trace:\nconstructor()\nf(1, 115792089237316195423570985008687907853269984665640564039457584007913129639935) diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol index 513166872f1e..94a71b1fa50f 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol @@ -2,15 +2,17 @@ pragma experimental SMTChecker; pragma experimental "ABIEncoderV2"; contract C { - struct S { uint x; uint[] b; } - function f() public pure returns (S memory, bytes memory, uint[][2] memory) { - return abi.decode("abc", (S, bytes, uint[][2])); - } + struct S { uint x; uint[] b; } + function f() public pure returns (S memory, bytes memory, uint[][2] memory) { + return abi.decode("abc", (S, bytes, uint[][2])); + } } // ---- -// Warning 8115: (151-159): Assertion checker does not yet support the type of this variable. -// Warning 8364: (206-209): Assertion checker does not yet implement type abi -// Warning 8364: (225-226): Assertion checker does not yet implement type type(struct C.S storage pointer) -// Warning 8364: (235-241): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (235-244): Assertion checker does not yet implement type type(uint256[] memory[2] memory) -// Warning 4588: (206-246): Assertion checker does not yet implement this type of function call. +// Warning 8364: (231-237): Assertion checker does not yet implement type type(uint256[] memory) +// Warning 8364: (231-240): Assertion checker does not yet implement type type(uint256[] memory[2] memory) +// Warning 8364: (221-222): Assertion checker does not yet implement type type(struct C.S storage pointer) +// Warning 4588: (202-242): Assertion checker does not yet implement this type of function call. +// Warning 8364: (231-237): Assertion checker does not yet implement type type(uint256[] memory) +// Warning 8364: (231-240): Assertion checker does not yet implement type type(uint256[] memory[2] memory) +// Warning 8364: (221-222): Assertion checker does not yet implement type type(struct C.S storage pointer) +// Warning 4588: (202-242): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol index 066a861e4d09..1031aa12a96c 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2_value_types.sol @@ -2,18 +2,18 @@ pragma experimental SMTChecker; pragma experimental "ABIEncoderV2"; contract C { - function f() public pure { - (uint x1, bool b1) = abi.decode("abc", (uint, bool)); - (uint x2, bool b2) = abi.decode("abc", (uint, bool)); - // False positive until abi.* are implemented as uninterpreted functions. - assert(x1 == x2); - } + function f() public pure { + (uint x1, bool b1) = abi.decode("abc", (uint, bool)); + (uint x2, bool b2) = abi.decode("abc", (uint, bool)); + // False positive until abi.* are implemented as uninterpreted functions. + assert(x1 == x2); + } } // ---- -// Warning 2072: (125-132): Unused local variable. -// Warning 2072: (183-190): Unused local variable. -// Warning 6328: (303-319): Assertion violation happens here -// Warning 8364: (136-139): Assertion checker does not yet implement type abi -// Warning 4588: (136-167): Assertion checker does not yet implement this type of function call. -// Warning 8364: (194-197): Assertion checker does not yet implement type abi -// Warning 4588: (194-225): Assertion checker does not yet implement this type of function call. +// Warning 2072: (122-129): Unused local variable. +// Warning 2072: (178-185): Unused local variable. +// Warning 4588: (133-164): Assertion checker does not yet implement this type of function call. +// Warning 4588: (189-220): Assertion checker does not yet implement this type of function call. +// Warning 6328: (300-316): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 4588: (133-164): Assertion checker does not yet implement this type of function call. +// Warning 4588: (189-220): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol index 1715fa4e0293..1902fd5e5b3e 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol @@ -1,24 +1,25 @@ pragma experimental SMTChecker; contract C { - function f() public pure { - (uint a1, bytes32 b1, C c1) = abi.decode("abc", (uint, bytes32, C)); - (uint a2, bytes32 b2, C c2) = abi.decode("abc", (uint, bytes32, C)); - // False positive until abi.* are implemented as uninterpreted functions. - assert(a1 == a2); - assert(a1 != a2); - } - + function f() public pure { + (uint a1, bytes32 b1, C c1) = abi.decode("abc", (uint, bytes32, C)); + (uint a2, bytes32 b2, C c2) = abi.decode("abc", (uint, bytes32, C)); + // False positive until abi.* are implemented as uninterpreted functions. + assert(a1 == a2); + assert(a1 != a2); + } } // ---- -// Warning 2072: (88-98): Unused local variable. -// Warning 2072: (100-104): Unused local variable. -// Warning 2072: (161-171): Unused local variable. -// Warning 2072: (173-177): Unused local variable. -// Warning 6328: (296-312): Assertion violation happens here -// Warning 6328: (315-331): Assertion violation happens here -// Warning 8364: (108-111): Assertion checker does not yet implement type abi -// Warning 8364: (142-143): Assertion checker does not yet implement type type(contract C) -// Warning 4588: (108-145): Assertion checker does not yet implement this type of function call. -// Warning 8364: (181-184): Assertion checker does not yet implement type abi -// Warning 8364: (215-216): Assertion checker does not yet implement type type(contract C) -// Warning 4588: (181-218): Assertion checker does not yet implement this type of function call. +// Warning 2072: (85-95): Unused local variable. +// Warning 2072: (97-101): Unused local variable. +// Warning 2072: (156-166): Unused local variable. +// Warning 2072: (168-172): Unused local variable. +// Warning 8364: (139-140): Assertion checker does not yet implement type type(contract C) +// Warning 4588: (105-142): Assertion checker does not yet implement this type of function call. +// Warning 8364: (210-211): Assertion checker does not yet implement type type(contract C) +// Warning 4588: (176-213): Assertion checker does not yet implement this type of function call. +// Warning 6328: (293-309): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (313-329): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8364: (139-140): Assertion checker does not yet implement type type(contract C) +// Warning 4588: (105-142): Assertion checker does not yet implement this type of function call. +// Warning 8364: (210-211): Assertion checker does not yet implement type type(contract C) +// Warning 4588: (176-213): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/special/abi_encode_slice.sol b/test/libsolidity/smtCheckerTests/special/abi_encode_slice.sol new file mode 100644 index 000000000000..b56c7780a8bb --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/abi_encode_slice.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f(bytes calldata data) external pure returns (bytes memory) { + return abi.encode(bytes(data[:32])); + } +} +// ---- +// Warning 4588: (126-154): Assertion checker does not yet implement this type of function call. +// Warning 4588: (126-154): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/special/blockhash.sol b/test/libsolidity/smtCheckerTests/special/blockhash.sol index d08e056e7f31..6f67152ba998 100644 --- a/test/libsolidity/smtCheckerTests/special/blockhash.sol +++ b/test/libsolidity/smtCheckerTests/special/blockhash.sol @@ -10,6 +10,5 @@ contract C } } // ---- -// Warning 6328: (85-109): Assertion violation happens here -// Warning 6328: (113-137): Assertion violation happens here -// Warning 6328: (155-191): Assertion violation happens here +// Warning 6328: (85-109): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 7719\n\n\nTransaction trace:\nconstructor()\nf(7719) +// Warning 6328: (113-137): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 7719\n\n\nTransaction trace:\nconstructor()\nf(7719) diff --git a/test/libsolidity/smtCheckerTests/special/difficulty.sol b/test/libsolidity/smtCheckerTests/special/difficulty.sol index 90e43e2043b1..e57690835d2e 100644 --- a/test/libsolidity/smtCheckerTests/special/difficulty.sol +++ b/test/libsolidity/smtCheckerTests/special/difficulty.sol @@ -7,4 +7,4 @@ contract C } } // ---- -// Warning 6328: (91-129): Assertion violation happens here +// Warning 6328: (91-129): CHC: Assertion violation happens here.\nCounterexample:\n\ndifficulty = 38\n\n\nTransaction trace:\nconstructor()\nf(38) diff --git a/test/libsolidity/smtCheckerTests/special/ether_units.sol b/test/libsolidity/smtCheckerTests/special/ether_units.sol new file mode 100644 index 000000000000..e79704b01c61 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/ether_units.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract D { + function f() public pure { + assert(1000000000000000000 wei == 1 ether); + assert(100000000000000000 wei == 1 ether); + assert(1000000000 wei == 1 gwei); + assert(100000000 wei == 1 gwei); + assert(1000000000 gwei == 1 ether); + assert(100000000 gwei == 1 ether); + } +} +// ---- +// Warning 6328: (121-162): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (202-233): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (275-308): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/special/event.sol b/test/libsolidity/smtCheckerTests/special/event.sol new file mode 100644 index 000000000000..4b016a843c1d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/event.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; + +contract C { + event Nudge(); + event SomeArgs(uint, uint); + event Caller(address, uint); + function f() payable external { + emit Nudge(); + emit SomeArgs(134, 567); + emit Caller(msg.sender, msg.value); + } + function g_data() pure internal returns (uint) { + assert(true); + } + function g() external { + emit SomeArgs(g_data(), g_data()); + } + bool x = true; + function h_data() view internal returns (uint) { + assert(x); + } + function h() external { + x = false; + emit SomeArgs(h_data(), h_data()); + } +} +// ---- +// Warning 6321: (280-284): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (430-434): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (440-449): CHC: Assertion violation happens here.\nCounterexample:\nx = false\n\n\n\nTransaction trace:\nconstructor()\nState: x = true\nh() diff --git a/test/libsolidity/smtCheckerTests/special/gasleft.sol b/test/libsolidity/smtCheckerTests/special/gasleft.sol index 0b14ee4e7912..4d4b1a4c101d 100644 --- a/test/libsolidity/smtCheckerTests/special/gasleft.sol +++ b/test/libsolidity/smtCheckerTests/special/gasleft.sol @@ -10,5 +10,5 @@ contract C } } // ---- -// Warning 6328: (76-97): Assertion violation happens here -// Warning 6328: (123-144): Assertion violation happens here +// Warning 6328: (76-97): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (123-144): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol index 1fbd2f905bf4..6243acff3467 100644 --- a/test/libsolidity/smtCheckerTests/special/many.sol +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -15,12 +15,12 @@ contract C } } // ---- -// Warning 6328: (79-115): Assertion violation happens here -// Warning 6328: (119-161): Assertion violation happens here -// Warning 6328: (165-204): Assertion violation happens here -// Warning 6328: (208-240): Assertion violation happens here -// Warning 6328: (244-275): Assertion violation happens here -// Warning 6328: (304-332): Assertion violation happens here -// Warning 6328: (336-364): Assertion violation happens here -// Warning 6328: (368-391): Assertion violation happens here -// Warning 2661: (311-316): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 6328: (79-115): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (119-161): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (165-204): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (208-240): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (244-275): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 4984: (311-316): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (304-332): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (336-364): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (368-391): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/special/msg_data.sol b/test/libsolidity/smtCheckerTests/special/msg_data.sol index 3936af6dd32b..026e1683b753 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_data.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_data.sol @@ -4,7 +4,23 @@ contract C { function f() public payable { assert(msg.data.length > 0); + // Fails since calldata size should be 4 + assert(msg.data.length > 4); + // f's sig is 0x26121ff0 + assert(msg.data[0] == 0x26); + assert(msg.data[1] == 0x12); + assert(msg.data[2] == 0x1f); + assert(msg.data[3] == 0xf0); + } + function g() public payable { + // g's sig is 0xe2179b8e + assert(msg.data[0] == 0xe2); + assert(msg.data[1] == 0x17); + assert(msg.data[2] == 0x9b); + // Fails + assert(msg.data[3] == 0x8f); } } // ---- -// Warning 6328: (79-106): Assertion violation happens here +// Warning 6328: (153-180): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (500-527): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol index df5620d8b483..65a5b2bc7f9b 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol @@ -10,4 +10,3 @@ contract C } } // ---- -// Warning 5084: (98-108): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol index 91aa8d2c93cd..9c188535cb6f 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (155-178): Assertion violation happens here +// Warning 6328: (155-178): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 39\n\n\nTransaction trace:\nconstructor()\nf(39) diff --git a/test/libsolidity/smtCheckerTests/special/msg_sig.sol b/test/libsolidity/smtCheckerTests/special/msg_sig.sol index e3c86f5aa401..7602480dd8d6 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_sig.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_sig.sol @@ -2,9 +2,29 @@ pragma experimental SMTChecker; contract C { - function f() public payable { + function f() public pure { assert(msg.sig == 0x00000000); + assert(msg.sig == 0x26121ff0); + fi(); + gi(); + } + function fi() internal pure { + assert(msg.sig == 0x26121ff0); + } + function g() public pure { + assert(msg.sig == 0xe2179b8e); + gi(); + } + function gi() internal pure { + // Fails since f can also call gi in which case msg.sig == 0x26121ff0 + assert(msg.sig == 0xe2179b8e); + } + function h() public pure { + // Fails since gi can also call h in which case msg.sig can be f() or g() + assert(msg.sig == 0xe2179b8e); } } // ---- -// Warning 6328: (79-108): Assertion violation happens here +// Warning 6328: (76-105): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (403-432): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (543-572): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nh() diff --git a/test/libsolidity/smtCheckerTests/special/this.sol b/test/libsolidity/smtCheckerTests/special/this.sol index 59c5f2d96fbd..647a3d7f788d 100644 --- a/test/libsolidity/smtCheckerTests/special/this.sol +++ b/test/libsolidity/smtCheckerTests/special/this.sol @@ -7,4 +7,4 @@ contract C } } // ---- -// Warning 6328: (85-111): Assertion violation happens here +// Warning 6328: (85-111): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/special/time_units.sol b/test/libsolidity/smtCheckerTests/special/time_units.sol new file mode 100644 index 000000000000..5b89ffca12e9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/time_units.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; +contract D { + function f() public pure { + assert(1 == 1 seconds); + assert(2 == 1 seconds); + assert(2 minutes == 120 seconds); + assert(3 minutes == 120 seconds); + assert(2 hours == 120 minutes); + assert(3 hours == 120 minutes); + assert(2 days == 48 hours); + assert(4 days == 48 hours); + assert(2 weeks == 14 days); + assert(25 weeks == 14 days); + } +} +// ---- +// Warning 6328: (101-123): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (163-195): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (233-263): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (297-323): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (357-384): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/special/tx_data_gasleft_changes.sol b/test/libsolidity/smtCheckerTests/special/tx_data_gasleft_changes.sol new file mode 100644 index 000000000000..0f3c7a3be6d7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/tx_data_gasleft_changes.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; + +contract C { + uint gleft; + + function f() public payable { + gleft = gasleft(); + + fi(); + + assert(gleft == gasleft()); + assert(gleft >= gasleft()); + } + + function fi() internal view { + assert(gleft == gasleft()); + } +} +// ---- +// Warning 6328: (124-150): CHC: Assertion violation happens here.\nCounterexample:\ngleft = 1\n\n\n\nTransaction trace:\nconstructor()\nState: gleft = 0\nf() +// Warning 6328: (219-245): CHC: Assertion violation happens here.\nCounterexample:\ngleft = 1\n\n\n\nTransaction trace:\nconstructor()\nState: gleft = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/special/tx_data_immutable.sol b/test/libsolidity/smtCheckerTests/special/tx_data_immutable.sol new file mode 100644 index 000000000000..371897b3bfe5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/tx_data_immutable.sol @@ -0,0 +1,61 @@ +pragma experimental SMTChecker; + +contract C { + bytes32 bhash; + address coin; + uint dif; + uint glimit; + uint number; + uint tstamp; + bytes mdata; + address sender; + bytes4 sig; + uint value; + uint gprice; + address origin; + + function f() public payable { + bhash = blockhash(12); + coin = block.coinbase; + dif = block.difficulty; + glimit = block.gaslimit; + number = block.number; + tstamp = block.timestamp; + mdata = msg.data; + sender = msg.sender; + sig = msg.sig; + value = msg.value; + gprice = tx.gasprice; + origin = tx.origin; + + fi(); + + assert(bhash == blockhash(12)); + assert(coin == block.coinbase); + assert(dif == block.difficulty); + assert(glimit == block.gaslimit); + assert(number == block.number); + assert(tstamp == block.timestamp); + assert(mdata.length == msg.data.length); + assert(sender == msg.sender); + assert(sig == msg.sig); + assert(value == msg.value); + assert(gprice == tx.gasprice); + assert(origin == tx.origin); + } + + function fi() internal view { + assert(bhash == blockhash(12)); + assert(coin == block.coinbase); + assert(dif == block.difficulty); + assert(glimit == block.gaslimit); + assert(number == block.number); + assert(tstamp == block.timestamp); + assert(mdata.length == msg.data.length); + assert(sender == msg.sender); + assert(sig == msg.sig); + assert(value == msg.value); + assert(gprice == tx.gasprice); + assert(origin == tx.origin); + } +} diff --git a/test/libsolidity/smtCheckerTests/special/tx_data_immutable_fail.sol b/test/libsolidity/smtCheckerTests/special/tx_data_immutable_fail.sol new file mode 100644 index 000000000000..5033f6a7b62e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/tx_data_immutable_fail.sol @@ -0,0 +1,88 @@ +pragma experimental SMTChecker; + +contract C { + bytes32 bhash; + address coin; + uint dif; + uint glimit; + uint number; + uint tstamp; + bytes mdata; + address sender; + bytes4 sig; + uint value; + uint gprice; + address origin; + + function f() public payable { + bhash = blockhash(12); + coin = block.coinbase; + dif = block.difficulty; + glimit = block.gaslimit; + number = block.number; + tstamp = block.timestamp; + mdata = msg.data; + sender = msg.sender; + sig = msg.sig; + value = msg.value; + gprice = tx.gasprice; + origin = tx.origin; + + fi(); + + assert(bhash == blockhash(122)); + assert(coin != block.coinbase); + assert(dif != block.difficulty); + assert(glimit != block.gaslimit); + assert(number != block.number); + assert(tstamp != block.timestamp); + assert(mdata.length != msg.data.length); + assert(sender != msg.sender); + assert(sig != msg.sig); + assert(value != msg.value); + assert(gprice != tx.gasprice); + assert(origin != tx.origin); + } + + function fi() internal view { + assert(bhash == blockhash(122)); + assert(coin != block.coinbase); + assert(dif != block.difficulty); + assert(glimit != block.gaslimit); + assert(number != block.number); + assert(tstamp != block.timestamp); + assert(mdata.length != msg.data.length); + assert(sender != msg.sender); + assert(sig != msg.sig); + assert(value != msg.value); + assert(gprice != tx.gasprice); + assert(origin != tx.origin); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (545-576): CHC: Assertion violation happens here. +// Warning 6328: (580-610): CHC: Assertion violation happens here. +// Warning 6328: (614-645): CHC: Assertion violation happens here. +// Warning 6328: (649-681): CHC: Assertion violation happens here. +// Warning 6328: (685-715): CHC: Assertion violation happens here. +// Warning 6328: (719-752): CHC: Assertion violation happens here. +// Warning 6328: (756-795): CHC: Assertion violation happens here. +// Warning 6328: (799-827): CHC: Assertion violation happens here. +// Warning 6328: (831-853): CHC: Assertion violation happens here. +// Warning 6328: (857-883): CHC: Assertion violation happens here. +// Warning 6328: (887-916): CHC: Assertion violation happens here. +// Warning 6328: (920-947): CHC: Assertion violation happens here. +// Warning 6328: (986-1017): CHC: Assertion violation happens here. +// Warning 6328: (1021-1051): CHC: Assertion violation happens here. +// Warning 6328: (1055-1086): CHC: Assertion violation happens here. +// Warning 6328: (1090-1122): CHC: Assertion violation happens here. +// Warning 6328: (1126-1156): CHC: Assertion violation happens here. +// Warning 6328: (1160-1193): CHC: Assertion violation happens here. +// Warning 6328: (1197-1236): CHC: Assertion violation happens here. +// Warning 6328: (1240-1268): CHC: Assertion violation happens here. +// Warning 6328: (1272-1294): CHC: Assertion violation happens here. +// Warning 6328: (1298-1324): CHC: Assertion violation happens here. +// Warning 6328: (1328-1357): CHC: Assertion violation happens here. +// Warning 6328: (1361-1388): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/typecast/address_literal.sol b/test/libsolidity/smtCheckerTests/typecast/address_literal.sol new file mode 100644 index 000000000000..a833af284f31 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/address_literal.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C { + address x; // We know that this is "zero initialised". + function f() public view { + address a = address(0); + assert(x == address(0)); + assert(x == a); + } + + function g() public pure { + address a = address(0); + address b = address(1); + address c = address(0); + address d = a; + address e = address(0x12345678); + assert(c == d); + assert(a == c); + assert(e == address(305419896)); + // This is untrue. + assert(a == b); + } +} +// ---- +// Warning 6328: (487-501): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\n\n\nTransaction trace:\nconstructor()\nState: x = 0\ng() diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol index 0bce016c9c8e..c76e4a9c7e32 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_address_1.sol @@ -8,5 +8,3 @@ contract C } } // ---- -// Warning 5084: (98-108): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (125-135): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol index bde664225da9..21efadc37c9f 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_different_size_1.sol @@ -7,20 +7,10 @@ contract C uint32 b = uint16(a); // b will be 0x00001234 assert(b == 0x1234); uint32 c = uint32(bytes4(a)); // c will be 0x12340000 - // This fails because right padding is not supported. assert(c == 0x12340000); uint8 d = uint8(uint16(a)); // d will be 0x34 - // False positive since truncating is not supported yet. assert(d == 0x34); uint8 e = uint8(bytes1(a)); // e will be 0x12 - // False positive since truncating is not supported yet. assert(e == 0x12); } } -// ---- -// Warning 6328: (280-303): Assertion violation happens here -// Warning 6328: (414-431): Assertion violation happens here -// Warning 6328: (542-559): Assertion violation happens here -// Warning 5084: (186-195): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (317-333): Type conversion is not yet fully supported and might yield false positives. -// Warning 5084: (451-460): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol index af7f1fdf6b4d..6ae5a337844e 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_1.sol @@ -9,4 +9,3 @@ contract C } } // ---- -// Warning 5084: (94-103): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol index 34ef9aef24d1..bbdeb0bf1654 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2.sol @@ -10,4 +10,3 @@ contract C } } // ---- -// Warning 5084: (108-117): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol index d5cf22e3f36b..a98687a4dda5 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_2_fail.sol @@ -9,5 +9,4 @@ contract C } } // ---- -// Warning 6328: (149-163): Assertion violation happens here -// Warning 5084: (108-117): Type conversion is not yet fully supported and might yield false positives. +// Warning 6328: (149-163): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol index 1bb3b7463ce2..626730f1f61d 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol @@ -12,6 +12,4 @@ contract C } } // ---- -// Warning 6328: (207-230): Assertion violation happens here -// Warning 6328: (273-287): Assertion violation happens here -// Warning 5084: (108-117): Type conversion is not yet fully supported and might yield false positives. +// Warning 6328: (273-287): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol index 223141476a26..310df7e34992 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_1.sol @@ -9,4 +9,3 @@ contract C } } // ---- -// Warning 5084: (94-102): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol index f6adcf0cef0c..e634cb22a690 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_2.sol @@ -5,10 +5,6 @@ contract C function f() public pure { uint32 a = 0x12345678; uint16 b = uint16(a); // b will be 0x5678 now - // False positive since truncation is not supported yet. assert(b == 0x5678); } } -// ---- -// Warning 6328: (208-227): Assertion violation happens here -// Warning 5084: (112-121): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol index 2d6cbada48a1..f6d9c36bcbe5 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_smaller_3.sol @@ -5,10 +5,6 @@ contract C function f() public pure { bytes2 a = 0x1234; bytes1 b = bytes1(a); // b will be 0x12 - // False positive since truncation is not supported yet. assert(b == 0x12); } } -// ---- -// Warning 6328: (198-215): Assertion violation happens here -// Warning 5084: (108-117): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/downcast.sol b/test/libsolidity/smtCheckerTests/typecast/downcast.sol new file mode 100644 index 000000000000..89475f296470 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/downcast.sol @@ -0,0 +1,48 @@ +pragma experimental SMTChecker; + +contract C { + function f1() public pure { + // signed <- signed + int8 z = int8(-1); + assert(z == -1); + z = int8(int(0) - 1); + assert(z == -1); + z = int8(int(0) - 1); + assert(z == -1); + z = int8(int(0) - 2); + assert(z == -2); + z = int8(int(0) - 1); + assert(z == -1); + + // unsigned <= unsigned + uint8 x = uint8(type(uint256).max); + assert(x == 255); + x = uint8(type(uint256).max); + assert(x == 255); + + // signed <- unsigned + int8 y = int8(uint8(type(uint16).max)); + assert(y == -1); + y = int8(uint8(uint16(100))); + assert(y == 100); + y = int8(uint8(uint16(200))); + assert(y == -56); + + // unsigned <- signed + uint8 v = uint8(type(uint16).max); + assert(v == 255); + v = uint8(int8(int16(300))); + assert(v == 44); + v = uint8(int8(int16(200))); + assert(v == 200); + + // fixed bytes + bytes2 b = bytes2(bytes4(0xcafeffff)); + assert(b == 0xcafe); + b = bytes2(bytes4(bytes8(0xaaaabbbbccccdddd))); + assert(b == 0xaaaa); + b = bytes2(bytes8(0xaaaabbbbccccdddd)); + assert(b == 0xaaaa); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol b/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol index 28b845fb665d..3ec8c585dd6c 100644 --- a/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol +++ b/test/libsolidity/smtCheckerTests/typecast/enum_from_uint.sol @@ -10,6 +10,3 @@ contract C } } // ---- -// Warning 6328: (140-160): Assertion violation happens here -// Warning 8364: (132-133): Assertion checker does not yet implement type type(enum C.D) -// Warning 5084: (132-136): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/enum_to_uint_max_value.sol b/test/libsolidity/smtCheckerTests/typecast/enum_to_uint_max_value.sol index 419cc6c2d3b4..89b0e7ab047e 100644 --- a/test/libsolidity/smtCheckerTests/typecast/enum_to_uint_max_value.sol +++ b/test/libsolidity/smtCheckerTests/typecast/enum_to_uint_max_value.sol @@ -9,4 +9,3 @@ contract C } } // ---- -// Warning 5084: (113-121): Type conversion is not yet fully supported and might yield false positives. diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol index 16eda02168e4..93e07ff6d1ed 100644 --- a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_external.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// Warning 6328: (155-175): Assertion violation happens here +// Warning 6328: (155-175): CHC: Assertion violation happens here.\nCounterexample:\n\ng = 0\nh = 0\n\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol index 1f6869a1c00f..d75594fb34d2 100644 --- a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -11,11 +11,15 @@ contract C { } } // ---- -// Warning 6328: (207-227): Assertion violation happens here -// Warning 6328: (231-245): Assertion violation happens here -// Warning 5729: (214-218): Assertion checker does not yet implement this type of function call. -// Warning 5729: (222-226): Assertion checker does not yet implement this type of function call. +// Warning 2519: (128-159): This declaration shadows an existing declaration. +// Warning 6031: (214-218): Internal error: Expression undefined for SMT solver. +// Warning 6031: (222-226): Internal error: Expression undefined for SMT solver. // Warning 7229: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons -// Warning 5729: (214-218): Assertion checker does not yet implement this type of function call. -// Warning 5729: (222-226): Assertion checker does not yet implement this type of function call. +// Warning 6328: (207-227): CHC: Assertion violation happens here.\nCounterexample:\na = 0, b = 0\n\n\n\nTransaction trace:\nconstructor()\nState: a = 0, b = 0\ng() +// Warning 6328: (231-245): CHC: Assertion violation happens here.\nCounterexample:\na = 0, b = 0\n\n\n\nTransaction trace:\nconstructor()\nState: a = 0, b = 0\ng() +// Warning 5729: (214-218): BMC does not yet implement this type of function call. +// Warning 5729: (222-226): BMC does not yet implement this type of function call. +// Warning 7229: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning 5729: (214-218): BMC does not yet implement this type of function call. +// Warning 5729: (222-226): BMC does not yet implement this type of function call. // Warning 7229: (238-244): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons diff --git a/test/libsolidity/smtCheckerTests/typecast/implicit_cast_string_literal_byte.sol b/test/libsolidity/smtCheckerTests/typecast/implicit_cast_string_literal_byte.sol new file mode 100644 index 000000000000..849052a990dc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/implicit_cast_string_literal_byte.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + mapping (bytes1 => uint) map; + function f() public { + map[""] = 2; + uint x = map[""]; + g(""); + bytes1 b = ""; + assert(x == map[b]); + assert(x == map["x"]); + } + function g(bytes1 b) internal pure {} +} +// ---- +// Warning 6328: (186-207): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/typecast/number_literal.sol b/test/libsolidity/smtCheckerTests/typecast/number_literal.sol new file mode 100644 index 000000000000..d3df0343aa59 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/number_literal.sol @@ -0,0 +1,31 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + uint x = 1234; + uint y = 0; + assert(x != y); + assert(x == uint(1234)); + assert(y == uint(0)); + } + function g() public pure { + uint a = uint(0); + uint b = type(uint256).max; + uint c = 115792089237316195423570985008687907853269984665640564039457584007913129639935; + int d = -1; + uint e = uint(d); + assert(a != b); + assert(b == c); + assert(b == e); + } + function h() public pure { + uint32 a = uint32(0); + uint32 b = type(uint32).max; + uint32 c = 4294967295; + int32 d = -1; + uint32 e = uint32(d); + assert(a != b); + assert(b == c); + assert(b == e); + } +} diff --git a/test/libsolidity/smtCheckerTests/typecast/same_size.sol b/test/libsolidity/smtCheckerTests/typecast/same_size.sol new file mode 100644 index 000000000000..6e153fad7e97 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/same_size.sol @@ -0,0 +1,73 @@ +pragma experimental SMTChecker; + +abstract contract D {} + +enum E {A, B} + +contract C { + function f1() public pure { + // signed <- unsigned + int8 x = int8(uint8(200)); + assert(x == -56); + int256 y = int256(uint256(2**255 + 10)); + assert(y == -(2**255) + 10); + int256 z = int256(uint(2**255 + 10)); + assert(z == -(2**255) + 10); + int256 t = int256(uint256(bytes32(uint256(200)))); + assert(t == 200); + int256 v = int256(uint256(bytes32(uint256(2**255 + 10)))); + assert(v == -(2**255) + 10); + int160 a = int160(uint160(address(type(uint160).max))); + assert(a == -1); + int160 b = int160(uint160(address(uint160(uint(2**159 + 10))))); + assert(b == -(2**159) + 10); + D d; + int160 e = int160(uint160(address(d))); + assert(e == 0); + } + + function f2() public pure { + // unsigned <- signed + uint8 x = uint8(int8(100)); + assert(x == 100); + uint16 y = uint16(int16(200)); + assert(y == 200); + uint8 z = uint8(int8(-100)); + assert(z == 156); + uint16 t = uint16(int16(-200)); + assert(t == type(uint16).max - 200 + 1); + uint256 v = uint256(int256(-200)); + assert(v == 2**256 - 200); + uint256 w = uint256(type(uint256).max - 1); + assert(w == 2**256 - 2); + bytes4 b = bytes4(uint32(type(uint256).max - 1)); + assert(uint32(b) == uint32(2**32 - 2)); + address a = address(type(uint160).max); + assert(uint160(a) == uint160(2**160 - 1)); + address c = address(0); + assert(uint160(c) == 0); + D d; + address e = address(d); + assert(uint160(e) == 0); + E f = E(1); + assert(uint(f) == 1); + } + + function f3() public pure { + // unsigned <- unsigned + uint8 x = uint8(bytes1(uint8(100))); + assert(x == 100); + address a = address(0); + assert(a == address(uint160(0))); + D d; + assert(a == address(d)); + } + + function f4() public pure { + // signed <- signed + int8 x = -10; + int8 y = int8(x); + assert(y == -10); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/typecast/slice_to_bytes.sol b/test/libsolidity/smtCheckerTests/typecast/slice_to_bytes.sol index ff3760dd4575..bae24de4c670 100644 --- a/test/libsolidity/smtCheckerTests/typecast/slice_to_bytes.sol +++ b/test/libsolidity/smtCheckerTests/typecast/slice_to_bytes.sol @@ -8,6 +8,3 @@ contract C { } } // ---- -// Warning 2923: (100-115): Assertion checker does not yet implement this expression. -// Warning 2923: (126-141): Assertion checker does not yet implement this expression. -// Warning 2923: (152-185): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_dynamic_bytes.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_dynamic_bytes.sol new file mode 100644 index 000000000000..5fdd0b96cba3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_dynamic_bytes.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes memory b = bytes(hex"ffff"); + assert(b.length == 2); // should hold + assert(b[0] == bytes1(uint8(255))); // should hold + assert(b[1] == bytes1(uint8(100))); // should fail + } +} +// ---- +// Warning 6328: (206-240): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_explicit.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_explicit.sol new file mode 100644 index 000000000000..8c454474b103 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_explicit.sol @@ -0,0 +1,4 @@ +pragma experimental SMTChecker; +contract SMT { + bytes32 constant internal NULL_BYTES32 = bytes32(''); +} diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol index 3bfd2b7514d9..88b5154be29f 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_function_call.sol @@ -9,5 +9,5 @@ contract B { } } // ---- -// Warning 6328: (162-184): Assertion violation happens here -// Warning 6328: (136-158): Assertion violation happens here +// Warning 6328: (162-184): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (136-158): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\n\n\nTransaction trace:\nconstructor()\ng(0) diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol index b4a482437d40..73ade5009ab4 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_modifier.sol @@ -8,4 +8,4 @@ contract B { } } // ---- -// Warning 6328: (152-174): Assertion violation happens here +// Warning 6328: (152-174): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol index 3042084e6470..60276c54f74d 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (238-259): Assertion violation happens here +// Warning 6328: (238-259): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\na() diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol index c29efb6f5bbb..2e06f960cd6d 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_return_multi.sol @@ -12,4 +12,4 @@ contract C { } } // ---- -// Warning 6328: (442-461): Assertion violation happens here +// Warning 6328: (442-461): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\na() diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_upcast.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_upcast.sol new file mode 100644 index 000000000000..881c9fb4613f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_fixed_bytes_upcast.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + assert(bytes4(hex"0000ffff") == bytes4(hex"ffff")); // should fail + assert(bytes4(hex"ffff0000") == bytes4(hex"ffff")); // should hold + } +} +// ---- +// Warning 6328: (76-126): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/typecast/upcast.sol b/test/libsolidity/smtCheckerTests/typecast/upcast.sol new file mode 100644 index 000000000000..d9f4d6730947 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/typecast/upcast.sol @@ -0,0 +1,69 @@ +pragma experimental SMTChecker; + +abstract contract D {} + +contract C { + function f1() public pure { + // unsigned <- signed + uint16 x = type(uint16).max; + assert(x == 65535); + int8 i = int8(-1); + assert(i == -1); + x = uint16(uint8(int8(-1))); + assert(x == 255); + x = uint16(int16(i)); + assert(x == 65535); + uint z = uint(uint8(i)); + assert(z == 255); + } + + function f2() public pure { + // signed <- unsigned + int16 y = int16(uint16(uint8(uint(65535)))); + assert(y == 255); + int z = int(uint(uint8(type(uint).max))); + assert(z == 255); + z = int(uint(uint8(255))); + assert(z == 255); + } + + function f3() public pure { + // signed <- signed + int16 y = int16(uint16(uint8(int8(int(uint(65535)))))); + assert(y == 255); + int z = int(int8(-1)); + assert(z == -1); + z = int(int8(int(255))); + assert(z == -1); + z = int(int16(5000)); + assert(z == 5000); + } + + function f4() public pure { + // unsigned <- unsigned + uint x = uint(uint8(type(uint).max)); + assert(x == 255); + x = uint(uint16(type(uint).max)); + assert(x == 65535); + x = uint(uint16(5000)); + assert(x == 5000); + uint16 y = uint16(type(uint).max); + assert(y == 65535); + y = uint16(uint8(type(uint16).max)); + assert(y == 255); + address a = address(uint160(uint8(0))); + assert(a == address(0)); + D d = D(address(uint160(uint8(0)))); + assert(a == address(d)); + bytes2 b1 = 0xcafe; + bytes4 b2 = bytes4(b1); + assert(b2 == 0xcafe0000); + bytes16 b3 = bytes16(b1); + assert(b3 == 0xcafe0000000000000000000000000000); + bytes4 b4 = bytes4(uint32(0xcafe)); + assert(b4 == 0x0000cafe); + bytes4 b5 = bytes4(uint32(0xcafe0000)); + assert(b5 == 0xcafe0000); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/address_balance.sol b/test/libsolidity/smtCheckerTests/types/address_balance.sol index 8edae0c8c6e4..21158f129f0b 100644 --- a/test/libsolidity/smtCheckerTests/types/address_balance.sol +++ b/test/libsolidity/smtCheckerTests/types/address_balance.sol @@ -9,5 +9,5 @@ contract C } // ---- // Warning 2072: (96-102): Unused local variable. -// Warning 6328: (131-160): Assertion violation happens here -// Warning 2661: (105-127): Overflow (resulting value larger than 2**256 - 1) happens here +// Warning 4984: (105-127): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\na = 0\nb = 0\n\n\nTransaction trace:\nconstructor()\nf(0, 0) +// Warning 6328: (131-160): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\nb = 0\n\n\nTransaction trace:\nconstructor()\nf(0, 0) diff --git a/test/libsolidity/smtCheckerTests/types/address_call.sol b/test/libsolidity/smtCheckerTests/types/address_call.sol index 275f1b6cf311..9bcbd1f02dab 100644 --- a/test/libsolidity/smtCheckerTests/types/address_call.sol +++ b/test/libsolidity/smtCheckerTests/types/address_call.sol @@ -19,7 +19,7 @@ contract C // EVMVersion: >spuriousDragon // ---- // Warning 2072: (224-240): Unused local variable. -// Warning 6328: (260-275): Assertion violation happens here -// Warning 6328: (279-293): Assertion violation happens here -// Warning 6328: (297-316): Assertion violation happens here -// Warning 6328: (320-344): Assertion violation happens here +// Warning 6328: (260-275): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) +// Warning 6328: (279-293): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) +// Warning 6328: (297-316): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) +// Warning 6328: (320-344): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) diff --git a/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol b/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol index 3dd9e07cbe77..2da8f1669268 100644 --- a/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol +++ b/test/libsolidity/smtCheckerTests/types/address_delegatecall.sol @@ -19,7 +19,7 @@ contract C // EVMVersion: >spuriousDragon // ---- // Warning 2072: (224-240): Unused local variable. -// Warning 6328: (268-283): Assertion violation happens here -// Warning 6328: (287-301): Assertion violation happens here -// Warning 6328: (305-324): Assertion violation happens here -// Warning 6328: (328-352): Assertion violation happens here +// Warning 6328: (268-283): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) +// Warning 6328: (287-301): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) +// Warning 6328: (305-324): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) +// Warning 6328: (328-352): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 0\ndata = []\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, []) diff --git a/test/libsolidity/smtCheckerTests/types/address_staticcall.sol b/test/libsolidity/smtCheckerTests/types/address_staticcall.sol index 05c10febc52e..93abd09b292c 100644 --- a/test/libsolidity/smtCheckerTests/types/address_staticcall.sol +++ b/test/libsolidity/smtCheckerTests/types/address_staticcall.sol @@ -19,4 +19,4 @@ contract C // EVMVersion: >spuriousDragon // ---- // Warning 2072: (224-240): Unused local variable. -// Warning 6328: (266-281): Assertion violation happens here +// Warning 6328: (266-281): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\na = 0\ndata = [10, 10]\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0, [10, 10]) diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer.sol b/test/libsolidity/smtCheckerTests/types/address_transfer.sol index ae435cec5812..b78768b94c74 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer.sol @@ -11,5 +11,5 @@ contract C } } // ---- -// Warning 6328: (195-219): Assertion violation happens here -// Warning 1236: (131-146): Insufficient funds happens here +// Warning 6328: (195-219): CHC: Assertion violation happens here.\nCounterexample:\n\na = 38\n\n\nTransaction trace:\nconstructor()\nf(38) +// Warning 1236: (131-146): BMC: Insufficient funds happens here. diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol b/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol index f6bc0ef6d953..d1e64336f9cf 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol @@ -14,6 +14,6 @@ contract C } } // ---- -// Warning 6328: (295-324): Assertion violation happens here -// Warning 1236: (217-232): Insufficient funds happens here -// Warning 1236: (236-251): Insufficient funds happens here +// Warning 6328: (295-324): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 100\na = 7720\nb = 7719\n\n\nTransaction trace:\nconstructor()\nf(100, 7720, 7719) +// Warning 1236: (217-232): BMC: Insufficient funds happens here. +// Warning 1236: (236-251): BMC: Insufficient funds happens here. diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol b/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol index 8c4f37bf29e3..9597dfa62dfb 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol @@ -11,6 +11,6 @@ contract C } } // ---- -// Warning 6328: (213-237): Assertion violation happens here -// Warning 1236: (134-149): Insufficient funds happens here -// Warning 1236: (153-169): Insufficient funds happens here +// Warning 6328: (213-237): CHC: Assertion violation happens here.\nCounterexample:\n\na = 7719\nb = 7719\n\n\nTransaction trace:\nconstructor()\nf(7719, 7719) +// Warning 1236: (134-149): BMC: Insufficient funds happens here. +// Warning 1236: (153-169): BMC: Insufficient funds happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol index 6b09f780d9ca..bf02fcec3906 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_1.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -26,4 +26,4 @@ contract C } } // ---- -// Warning 6328: (400-457): Assertion violation happens here +// Warning 6328: (400-457): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol index 2e46f6890d9f..fd3c6759b9d7 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_2.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -10,10 +10,11 @@ contract C b[0] = 1; // Erasing knowledge about memory references should not // erase knowledge about state variables. - assert(array[0] == 42); + // Removed because current Spacer seg faults. + //assert(array[0] == 42); assert(a[0] == 2); assert(b[0] == 1); } } // ---- -// Warning 6328: (321-338): Assertion violation happens here +// Warning 6328: (371-388): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol index 5885053c5423..b13c8d300192 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_memory_3.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -15,9 +15,10 @@ contract C // Erasing knowledge about memory references should not // erase knowledge about storage references. assert(c[0] == 42); - assert(a[0] == 2); - assert(b[0] == 1); + // Removed because current Spacer seg faults in cex generation. + //assert(a[0] == 2); + // Removed because current Spacer seg faults in cex generation. + //assert(b[0] == 1); } } // ---- -// Warning 6328: (476-493): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol index 75b265284401..9e31d0a20c36 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_1.sol @@ -44,8 +44,8 @@ contract C } } // ---- -// Warning 6328: (468-485): Assertion violation happens here. -// Warning 6328: (532-554): Assertion violation happens here. -// Warning 6328: (606-633): Assertion violation happens here. -// Warning 6328: (774-796): Assertion violation happens here. -// Warning 6328: (936-962): Assertion violation happens here. +// Warning 6328: (468-485): CHC: Assertion violation happens here. +// Warning 6328: (532-554): CHC: Assertion violation happens here. +// Warning 6328: (606-633): CHC: Assertion violation happens here. +// Warning 6328: (774-796): CHC: Assertion violation happens here. +// Warning 6328: (936-962): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol index 75077c31acb1..85a868505eaf 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_2.sol @@ -20,4 +20,4 @@ contract C } } // ---- -// Warning 6328: (436-453): Assertion violation happens here +// Warning 6328: (436-453): CHC: Assertion violation happens here.\nCounterexample:\narray2d = []\nx = 0\ny = 0\nc = [38, 8, 8, 8, 8, 8, 8, 8, 8]\n\n\nTransaction trace:\nconstructor()\nState: array2d = []\ng(0, 0, [38, 8, 8, 8, 8, 8, 8, 8, 8]) diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol index b59f340ad0a8..67cb7e86f436 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_3.sol @@ -22,6 +22,8 @@ contract C assert(b[0] == 1); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (524-542): Assertion violation happens here -// Warning 6328: (585-602): Assertion violation happens here +// Warning 6328: (524-542): CHC: Assertion violation happens here. +// Warning 6328: (585-602): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol index d1440dd92122..e3a5c1bf941b 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_4.sol @@ -19,5 +19,5 @@ contract C } } // ---- -// Warning 6328: (225-242): Assertion violation happens here -// Warning 6328: (289-307): Assertion violation happens here +// Warning 6328: (225-242): CHC: Assertion violation happens here.\nCounterexample:\narray = [1, 19, 19, 19, 19], array2d = []\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\nState: array = [], array2d = []\ng(0, 0) +// Warning 6328: (289-307): CHC: Assertion violation happens here.\nCounterexample:\narray = [1, 19, 19, 19, 19], array2d = []\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\nState: array = [], array2d = []\ng(0, 0) diff --git a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol index 8f592202f256..efa958d554b6 100644 --- a/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_aliasing_storage_5.sol @@ -17,7 +17,8 @@ contract C // erase knowledge about memory references. assert(c[0] == 42); // Fails because d == a is possible. - assert(d[0] == 42); + // Removed because current Spacer seg faults in cex generation. + //assert(d[0] == 42); // Fails because b == a and d == a are possible. assert(a[0] == 2); // b == a is possible, but does not fail because b @@ -26,5 +27,4 @@ contract C } } // ---- -// Warning 6328: (431-449): Assertion violation happens here -// Warning 6328: (504-521): Assertion violation happens here +// Warning 6328: (572-589): CHC: Assertion violation happens here.\nCounterexample:\nb = [1, 20, 20, 20, 20], d = [], array2d = []\nx = 0\nc = [0, 9, 9, 9, 9, 9, 9, 9, 9, 9]\n\n\nTransaction trace:\nconstructor()\nState: b = [], d = [], array2d = []\ng(0, [0, 9, 9, 9, 9, 9, 9, 9, 9, 9]) diff --git a/test/libsolidity/smtCheckerTests/types/array_branch_1d.sol b/test/libsolidity/smtCheckerTests/types/array_branch_1d.sol index f518188ddeb4..80769e6db27f 100644 --- a/test/libsolidity/smtCheckerTests/types/array_branch_1d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_branch_1d.sol @@ -11,4 +11,4 @@ contract C } // ---- // Warning 2018: (47-148): Function state mutability can be restricted to pure -// Warning 6328: (128-144): Assertion violation happens here +// Warning 6328: (128-144): CHC: Assertion violation happens here.\nCounterexample:\n\nb = false\nc = [0, 5, 5, 5, 5, 5]\n\n\nTransaction trace:\nconstructor()\nf(false, [38, 5, 5, 5, 5, 5]) diff --git a/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol b/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol index 1c782058fdd0..df0b0f1cdda4 100644 --- a/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_branch_2d.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (130-149): Assertion violation happens here +// Warning 6328: (130-149): CHC: Assertion violation happens here.\nCounterexample:\nc = []\nb = false\n\n\nTransaction trace:\nconstructor()\nState: c = []\nf(false) diff --git a/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol b/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol index f4606955319f..f37deebff59b 100644 --- a/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_branch_3d.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (138-160): Assertion violation happens here +// Warning 6328: (138-160): CHC: Assertion violation happens here.\nCounterexample:\nc = []\nb = false\n\n\nTransaction trace:\nconstructor()\nState: c = []\nf(false) diff --git a/test/libsolidity/smtCheckerTests/types/array_dynamic_1_fail.sol b/test/libsolidity/smtCheckerTests/types/array_dynamic_1_fail.sol index 4d0ab0b8bb98..5649f5f8932c 100644 --- a/test/libsolidity/smtCheckerTests/types/array_dynamic_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/array_dynamic_1_fail.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (137-159): Assertion violation happens here +// Warning 6328: (137-159): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 38\ny = 38\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(38, 38) diff --git a/test/libsolidity/smtCheckerTests/types/array_dynamic_2_fail.sol b/test/libsolidity/smtCheckerTests/types/array_dynamic_2_fail.sol index 6111c724765d..8ad7869e0e38 100644 --- a/test/libsolidity/smtCheckerTests/types/array_dynamic_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/array_dynamic_2_fail.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (243-268): Assertion violation happens here +// Warning 6328: (243-268): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 38\ny = 7719\nz = 38\nt = 7719\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(38, 7719, 38, 7719) diff --git a/test/libsolidity/smtCheckerTests/types/array_dynamic_3_fail.sol b/test/libsolidity/smtCheckerTests/types/array_dynamic_3_fail.sol index 5c44924cb1a3..007ef73fdb4c 100644 --- a/test/libsolidity/smtCheckerTests/types/array_dynamic_3_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/array_dynamic_3_fail.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (274-302): Assertion violation happens here +// Warning 6328: (274-302): CHC: Assertion violation happens here.\nCounterexample:\narray = []\nx = 21238\ny = 38\nz = 7719\nt = 21238\nw = 38\nv = 7719\n\n\nTransaction trace:\nconstructor()\nState: array = []\nf(21238, 38, 7719, 21238, 38, 7719) diff --git a/test/libsolidity/smtCheckerTests/types/array_dynamic_parameter_1_fail.sol b/test/libsolidity/smtCheckerTests/types/array_dynamic_parameter_1_fail.sol index b49c7d5e04c0..69a78e925ffa 100644 --- a/test/libsolidity/smtCheckerTests/types/array_dynamic_parameter_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/array_dynamic_parameter_1_fail.sol @@ -9,4 +9,4 @@ contract C } } // ---- -// Warning 6328: (148-170): Assertion violation happens here +// Warning 6328: (148-170): CHC: Assertion violation happens here.\nCounterexample:\n\narray = [15, 15, 15, 15, 15]\nx = 38\ny = 38\n\n\nTransaction trace:\nconstructor()\nf([15, 15, 15, 15, 15], 38, 38) diff --git a/test/libsolidity/smtCheckerTests/types/array_literal_1.sol b/test/libsolidity/smtCheckerTests/types/array_literal_1.sol index a72031f1dfbc..1a6fb3b51b85 100644 --- a/test/libsolidity/smtCheckerTests/types/array_literal_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_literal_1.sol @@ -4,8 +4,9 @@ contract C { function f() public pure { uint[3] memory array = [uint(1), 2, 3]; + assert(array[0] == 1); + assert(array[1] == 2); + assert(array[2] == 3); } } // ---- -// Warning 2072: (76-96): Unused local variable. -// Warning 2177: (99-114): Assertion checker does not yet implement inline arrays. diff --git a/test/libsolidity/smtCheckerTests/types/array_literal_2.sol b/test/libsolidity/smtCheckerTests/types/array_literal_2.sol new file mode 100644 index 000000000000..c13dfb13d788 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_literal_2.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + uint[3] memory a = [uint(1), 2, 3]; + uint[3] memory b = [uint(1), 2, 4]; + assert(a[0] == b[0]); + assert(a[1] == b[1]); + assert(a[2] == b[2]); // fails + } +} +// ---- +// Warning 6328: (200-220): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/array_literal_3.sol b/test/libsolidity/smtCheckerTests/types/array_literal_3.sol new file mode 100644 index 000000000000..c6719f8f60b8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_literal_3.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + (uint[3] memory a, uint[3] memory b) = ([uint(1), 2, 3], [uint(1), 2, 4]); + assert(a[0] == b[0]); + assert(a[1] == b[1]); + assert(a[2] == b[2]); // fails + } +} +// ---- +// Warning 6328: (201-221): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/array_literal_4.sol b/test/libsolidity/smtCheckerTests/types/array_literal_4.sol new file mode 100644 index 000000000000..1606c43fa16f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_literal_4.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(bool c) public pure { + uint[3] memory a = c ? [uint(1), 2, 3] : [uint(1), 2, 4]; + uint[3] memory b = [uint(1), 2, c ? 3 : 4]; + assert(a[0] == b[0]); + assert(a[1] == b[1]); + assert(a[2] == b[2]); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/array_literal_5.sol b/test/libsolidity/smtCheckerTests/types/array_literal_5.sol new file mode 100644 index 000000000000..d0b9fea2708b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_literal_5.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C +{ + uint[] s; + function f() public { + uint[3] memory a = [uint(1), 2, 3]; + s = a; + assert(s.length == a.length); + assert(s[0] == a[0]); + assert(s[1] == a[1]); + assert(s[2] != a[2]); // fails + } +} +// ---- +// Warning 6328: (209-229): CHC: Assertion violation happens here.\nCounterexample:\ns = [1, 2, 3]\n\n\n\nTransaction trace:\nconstructor()\nState: s = []\nf() diff --git a/test/libsolidity/smtCheckerTests/types/array_literal_6.sol b/test/libsolidity/smtCheckerTests/types/array_literal_6.sol new file mode 100644 index 000000000000..d7cec8f166ec --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_literal_6.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public pure { + uint[3] memory a = [uint(1), 2, 3]; + uint[4] memory b = [uint(1), 2, 4, 3]; + uint[4] memory c = b; + assert(a.length == c.length); // fails + assert(a[0] == c[0]); + assert(a[1] == c[1]); + assert(a[2] == c[2]); // fails + assert(a[2] == c[3]); + } +} +// ---- +// Warning 6328: (179-207): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (268-288): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/array_literal_7.sol b/test/libsolidity/smtCheckerTests/types/array_literal_7.sol new file mode 100644 index 000000000000..45841b670c05 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/array_literal_7.sol @@ -0,0 +1,23 @@ +pragma experimental SMTChecker; + +contract C +{ + uint[] s; + function f() public { + uint[3] memory a = [uint(1), 2, 3]; + uint[4] memory b = [uint(1), 2, 4, 3]; + uint[4] memory c = b; + assert(c.length == b.length); + s = a; + assert(s.length == a.length); + + assert(s.length == c.length); // fails + assert(s[0] == c[0]); + assert(s[1] == c[1]); + assert(s[2] == c[2]); // fails + assert(s[2] == c[3]); + } +} +// ---- +// Warning 6328: (259-287): CHC: Assertion violation happens here.\nCounterexample:\ns = [1, 2, 3]\n\n\n\nTransaction trace:\nconstructor()\nState: s = []\nf() +// Warning 6328: (348-368): CHC: Assertion violation happens here.\nCounterexample:\ns = [1, 2, 3]\n\n\n\nTransaction trace:\nconstructor()\nState: s = []\nf() diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol index 82508ab21bb3..82cfd36ae1df 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_1.sol @@ -16,12 +16,12 @@ contract C // Should not fail since knowledge is erased only for mapping (uint => uint). assert(severalMaps8[0][0] == 42); // Should fail since map == severalMaps3d[0][0] is possible. - assert(severalMaps3d[0][0][0] == 42); + // Removed because current Spacer seg faults in cex generation. + //assert(severalMaps3d[0][0][0] == 42); } function g(uint x) public { f(severalMaps[x]); } } // ---- -// Warning 6328: (421-452): Assertion violation happens here -// Warning 6328: (635-671): Assertion violation happens here +// Warning 6328: (421-452): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\n\n\nTransaction trace:\nconstructor()\ng(38) diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol index 7fa85234f902..024a44fa6aa6 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol @@ -25,5 +25,7 @@ contract C f(severalMaps[x]); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (777-797): Assertion violation happens here +// Warning 6328: (777-797): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_static_1_fail.sol b/test/libsolidity/smtCheckerTests/types/array_static_1_fail.sol index 87d385190c5d..4ce55c2642d5 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_1_fail.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (139-161): Assertion violation happens here +// Warning 6328: (139-161): CHC: Assertion violation happens here.\nCounterexample:\narray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\nx = 38\ny = 38\n\n\nTransaction trace:\nconstructor()\nState: array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\nf(38, 38) diff --git a/test/libsolidity/smtCheckerTests/types/array_static_2_fail.sol b/test/libsolidity/smtCheckerTests/types/array_static_2_fail.sol index 3ef862bf8ac8..6251935b6a1b 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_2_fail.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (186-211): Assertion violation happens here +// Warning 6328: (186-211): CHC: Assertion violation happens here.\nCounterexample:\narray = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]\nx = 38\ny = 7719\nz = 38\nt = 7719\n\n\nTransaction trace:\nconstructor()\nState: array = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]\nf(38, 7719, 38, 7719) diff --git a/test/libsolidity/smtCheckerTests/types/array_static_3_fail.sol b/test/libsolidity/smtCheckerTests/types/array_static_3_fail.sol index 2e6fc54c78fb..9749fc63bbd6 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_3_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_3_fail.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (280-308): Assertion violation happens here +// Warning 6328: (280-308): CHC: Assertion violation happens here.\nCounterexample:\narray = [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]\nx = 21238\ny = 38\nz = 7719\nt = 21238\nw = 38\nv = 7719\n\n\nTransaction trace:\nconstructor()\nState: array = [[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]\nf(21238, 38, 7719, 21238, 38, 7719) diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol index c2b2b9ab537c..1f88f6dd1a12 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_memory_5.sol @@ -7,12 +7,12 @@ contract C require(a[0] == 2); b[0] = 1; // Should fail since b == c is possible. - assert(c[0] == 42); + // Removed because current Spacer seg faults in cex generation. + //assert(c[0] == 42); // Should fail since b == a is possible. assert(a[0] == 2); assert(b[0] == 1); } } // ---- -// Warning 6328: (228-246): Assertion violation happens here -// Warning 6328: (293-310): Assertion violation happens here +// Warning 6328: (361-378): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol index 429e5428db2e..60d9979dc7c4 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_aliasing_storage_5.sol @@ -20,5 +20,7 @@ contract C else f(b2, c); } } +// ==== +// SMTIgnoreCex: yes // ---- -// Warning 6328: (338-355): Assertion violation happens here +// Warning 6328: (338-355): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol index 185797d6919b..899b72fbc75e 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_1.sol @@ -16,12 +16,12 @@ contract C // Should not fail since knowledge is erased only for mapping (uint => uint). assert(severalMaps8[0][0] == 42); // Should fail since map == severalMaps3d[0][0] is possible. - assert(severalMaps3d[0][0][0] == 42); + // Removed because current Spacer seg faults in cex generation. + //assert(severalMaps3d[0][0][0] == 42); } function g(uint x) public { f(severalMaps[x]); } } // ---- -// Warning 6328: (425-456): Assertion violation happens here -// Warning 6328: (639-675): Assertion violation happens here +// Warning 6328: (425-456): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 38\n\n\nTransaction trace:\nconstructor()\ng(38) diff --git a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol index c9ed4f34cd92..b1a5c7532616 100644 --- a/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_static_mapping_aliasing_2.sol @@ -17,7 +17,8 @@ contract C // Should not fail since knowledge is erased only for mapping (uint => uint). assert(severalMaps8[0][0] == 42); // Should not fail since singleMap == severalMaps3d[0][0] is not possible. - assert(severalMaps3d[0][0][0] == 42); + // Removed because of Spacer nondeterminism. + //assert(severalMaps3d[0][0][0] == 42); // Should fail since singleMap == map is possible. assert(map[0] == 42); } @@ -26,4 +27,4 @@ contract C } } // ---- -// Warning 6328: (781-801): Assertion violation happens here +// Warning 6328: (830-850): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\ng(0) diff --git a/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol b/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol index f7cb6f957bb5..a4531079bebc 100644 --- a/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/array_struct_array_branches_2d.sol @@ -14,19 +14,3 @@ contract C } } // ---- -// Warning 6328: (202-226): Assertion violation happens here -// Warning 7650: (124-130): Assertion checker does not yet support this expression. -// Warning 8364: (124-128): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (124-133): Assertion checker does not yet implement this expression. -// Warning 9056: (124-136): Assertion checker does not yet implement this expression. -// Warning 7650: (154-160): Assertion checker does not yet support this expression. -// Warning 8364: (154-158): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (154-163): Assertion checker does not yet implement this expression. -// Warning 9056: (154-166): Assertion checker does not yet implement this expression. -// Warning 7650: (182-188): Assertion checker does not yet support this expression. -// Warning 8364: (182-186): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (182-191): Assertion checker does not yet implement this expression. -// Warning 9056: (182-194): Assertion checker does not yet implement this expression. -// Warning 7650: (209-215): Assertion checker does not yet support this expression. -// Warning 8364: (209-213): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (209-218): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_1.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_1.sol index f499d14fd39c..2443f09afa38 100644 --- a/test/libsolidity/smtCheckerTests/types/bool_simple_1.sol +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_1.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// Warning 6328: (90-99): Assertion violation happens here +// Warning 6328: (90-99): CHC: Assertion violation happens here.\nCounterexample:\n\nx = false\n\n\nTransaction trace:\nconstructor()\nf(false) diff --git a/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol b/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol index 9905ca0ba0bb..8ef22c7e244c 100644 --- a/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol +++ b/test/libsolidity/smtCheckerTests/types/bool_simple_2.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// Warning 6328: (98-112): Assertion violation happens here +// Warning 6328: (98-112): CHC: Assertion violation happens here.\nCounterexample:\n\nx = true\ny = false\n\n\nTransaction trace:\nconstructor()\nf(true, false) diff --git a/test/libsolidity/smtCheckerTests/types/bytes_2_fail.sol b/test/libsolidity/smtCheckerTests/types/bytes_2_fail.sol index 433125a63289..0e3e1160e3b2 100644 --- a/test/libsolidity/smtCheckerTests/types/bytes_2_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/bytes_2_fail.sol @@ -8,4 +8,4 @@ contract C } } // ---- -// Warning 6328: (119-141): Assertion violation happens here +// Warning 6328: (119-141): CHC: Assertion violation happens here.\nCounterexample:\n\nb1 = [28958, 28957, 28958, 28958, 28958]\nb2 = [28958, 28957, 28958, 28958, 28958]\n\n\nTransaction trace:\nconstructor()\nf([], [28958, 28957, 28958, 28958, 28958]) diff --git a/test/libsolidity/smtCheckerTests/types/bytes_length.sol b/test/libsolidity/smtCheckerTests/types/bytes_length.sol new file mode 100644 index 000000000000..e248165af7a9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/bytes_length.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes memory x = hex"0123"; + assert(x.length == 2); + } + function g() public pure { + bytes memory x = bytes(hex"0123"); + assert(x.length == 2); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/contract.sol b/test/libsolidity/smtCheckerTests/types/contract.sol index aca1a4613419..6a489d4792ef 100644 --- a/test/libsolidity/smtCheckerTests/types/contract.sol +++ b/test/libsolidity/smtCheckerTests/types/contract.sol @@ -7,4 +7,4 @@ contract C } } // ---- -// Warning 6328: (84-98): Assertion violation happens here +// Warning 6328: (84-98): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0\nd = 1\n\n\nTransaction trace:\nconstructor()\nf(0, 1) diff --git a/test/libsolidity/smtCheckerTests/types/contract_2.sol b/test/libsolidity/smtCheckerTests/types/contract_2.sol index 8d79761e5042..285dfff04a02 100644 --- a/test/libsolidity/smtCheckerTests/types/contract_2.sol +++ b/test/libsolidity/smtCheckerTests/types/contract_2.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (109-123): Assertion violation happens here +// Warning 6328: (109-123): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0\nd = 1\n\n\nTransaction trace:\nconstructor()\nf(0, 1) diff --git a/test/libsolidity/smtCheckerTests/types/contract_address_conversion.sol b/test/libsolidity/smtCheckerTests/types/contract_address_conversion.sol index ae1e407b7dbb..1e179dcaadf0 100644 --- a/test/libsolidity/smtCheckerTests/types/contract_address_conversion.sol +++ b/test/libsolidity/smtCheckerTests/types/contract_address_conversion.sol @@ -7,4 +7,4 @@ contract C } } // ---- -// Warning 6328: (90-113): Assertion violation happens here +// Warning 6328: (90-113): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 1\na = 0\n\n\nTransaction trace:\nconstructor()\nf(1, 0) diff --git a/test/libsolidity/smtCheckerTests/types/enum_explicit_values_2.sol b/test/libsolidity/smtCheckerTests/types/enum_explicit_values_2.sol index 787cb2807e22..d35e893746ae 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_explicit_values_2.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_explicit_values_2.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (144-159): Assertion violation happens here +// Warning 6328: (144-159): CHC: Assertion violation happens here.\nCounterexample:\nd = 0\n_a = 0\n\n\nTransaction trace:\nconstructor()\nState: d = 0\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol index 7e54307ed30d..a8b8902adb4e 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_library_2.sol @@ -14,4 +14,4 @@ contract C } } // ---- -// Warning 6328: (159-179): Assertion violation happens here +// Warning 6328: (159-179): CHC: Assertion violation happens here.\nCounterexample:\n\n_d = 1\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol index 8c8636e59893..179b624fd44a 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_in_struct.sol @@ -1,5 +1,5 @@ pragma experimental SMTChecker; -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { @@ -11,10 +11,3 @@ contract C } } // ---- -// Warning 6328: (187-208): Assertion violation happens here -// Warning 8115: (143-153): Assertion checker does not yet support the type of this variable. -// Warning 7650: (171-174): Assertion checker does not yet support this expression. -// Warning 8364: (171-172): Assertion checker does not yet implement type struct C.S memory -// Warning 8182: (171-183): Assertion checker does not yet implement such assignments. -// Warning 7650: (194-197): Assertion checker does not yet support this expression. -// Warning 8364: (194-195): Assertion checker does not yet implement type struct C.S memory diff --git a/test/libsolidity/smtCheckerTests/types/enum_storage_eq.sol b/test/libsolidity/smtCheckerTests/types/enum_storage_eq.sol index e434046005ce..abca71ce2251 100644 --- a/test/libsolidity/smtCheckerTests/types/enum_storage_eq.sol +++ b/test/libsolidity/smtCheckerTests/types/enum_storage_eq.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (115-130): Assertion violation happens here +// Warning 6328: (115-130): CHC: Assertion violation happens here.\nCounterexample:\nd = 0\n_d = 0\n\n\nTransaction trace:\nconstructor()\nState: d = 0\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol index 55138482ccdf..69285d11234c 100644 --- a/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol @@ -12,5 +12,5 @@ contract C } } // ---- -// Warning 6328: (96-110): Assertion violation happens here -// Warning 6328: (114-130): Assertion violation happens here +// Warning 6328: (96-110): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0) +// Warning 6328: (114-130): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol index 46409c1e6181..e6fbdc156c73 100644 --- a/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol @@ -12,4 +12,4 @@ contract C } } // ---- -// Warning 6328: (116-130): Assertion violation happens here +// Warning 6328: (116-130): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\nState: x = 0\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_1.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_1.sol new file mode 100644 index 000000000000..76e8c2eadb6c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_1.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; +contract c { + bytes10[6] data2; + function test() public view returns (bytes10 r2) { + r2 = data2[4][5]; + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_2.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_2.sol new file mode 100644 index 000000000000..264b92a7bba7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_2.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract C { + function f(bytes calldata x, uint y) external pure { + x[8][0]; + x[8][5%y]; + } +} +// ---- +// Warning 4281: (117-120): CHC: Division by zero happens here.\nCounterexample:\n\nx = [7, 7]\ny = 0\n\n\nTransaction trace:\nconstructor()\nf([7, 7], 0) diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_3.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_3.sol new file mode 100644 index 000000000000..a85802993f80 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_3.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C { + bytes16[][] a; + function g() internal view returns (bytes16[] storage) { + return a[2]; + } + function h() internal view returns (bytes16) { + return a[2][2]; + } + function f() external view { + g()[3][4]; + h()[5]; + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_4.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_4.sol new file mode 100644 index 000000000000..01f1746a9fd6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_4.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes32 x = 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff; + bytes1 z = 0x00; + bytes1 o = 0xff; + assert(x[0] == z); + assert(x[31] == o); + assert(x[0] == x[22]); + assert(x[0] == x[23]); + } +} +// ---- +// Warning 6328: (264-285): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_5.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_5.sol new file mode 100644 index 000000000000..9afd9f56f024 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_5.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes4 x = 0x01020304; + bytes1 b = 0x02; + assert(x[0] == b); // fails + assert(x[1] == b); + assert(x[2] == b); // fails + assert(x[3] == b); // fails + } +} +// ---- +// Warning 6328: (120-137): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (171-188): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (201-218): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_6.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_6.sol new file mode 100644 index 000000000000..0a0edf86476e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_6.sol @@ -0,0 +1,11 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + bytes4 x = 0x01020304; + bytes1 b = x[3]; + assert(b == b[0]); + assert(b == b[0][0]); + assert(b == b[0][0][0][0][0][0][0][0][0][0][0]); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_7.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_7.sol new file mode 100644 index 000000000000..750c7fb6c1e1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_7.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint i) public pure { + bytes4 x = 0x01020304; + require(i > 3); + assert(x[i] == 0x00); + i = 5; + assert(x[i] == 0); + assert(x[i] != 0); + } +} +// ---- +// Warning 6328: (125-145): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 4\n\n\nTransaction trace:\nconstructor()\nf(4) +// Warning 6328: (158-175): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 5\n\n\nTransaction trace:\nconstructor()\nf(4) +// Warning 6328: (179-196): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 5\n\n\nTransaction trace:\nconstructor()\nf(4) diff --git a/test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol b/test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol index 40b0c0b7a616..1a41149e9372 100644 --- a/test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol +++ b/test/libsolidity/smtCheckerTests/types/function_in_tuple_1.sol @@ -7,4 +7,3 @@ contract K { } // ---- // Warning 6133: (76-91): Statement has no effect. -// Warning 8364: (77-80): Assertion checker does not yet implement type abi diff --git a/test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol b/test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol index cecb9f967a6d..f6431192256a 100644 --- a/test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol +++ b/test/libsolidity/smtCheckerTests/types/function_in_tuple_2.sol @@ -7,4 +7,3 @@ contract K { } // ---- // Warning 6133: (76-92): Statement has no effect. -// Warning 8364: (77-80): Assertion checker does not yet implement type abi diff --git a/test/libsolidity/smtCheckerTests/types/function_type_arrays.sol b/test/libsolidity/smtCheckerTests/types/function_type_arrays.sol index e2810721b90f..a0ebf437996c 100644 --- a/test/libsolidity/smtCheckerTests/types/function_type_arrays.sol +++ b/test/libsolidity/smtCheckerTests/types/function_type_arrays.sol @@ -7,8 +7,8 @@ contract C { function(uint) returns (uint)[10] storage b = y; function(uint) external returns (uint)[] memory c; c = new function(uint) external returns (uint)[](200); + assert(c.length == 200); a; b; } } // ---- -// Warning 4588: (361-410): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_call.sol b/test/libsolidity/smtCheckerTests/types/function_type_call.sol index 52b019947366..a41bc035c49f 100644 --- a/test/libsolidity/smtCheckerTests/types/function_type_call.sol +++ b/test/libsolidity/smtCheckerTests/types/function_type_call.sol @@ -9,5 +9,5 @@ contract C { } } // ---- -// Warning 5729: (121-125): Assertion checker does not yet implement this type of function call. -// Warning 5729: (121-125): Assertion checker does not yet implement this type of function call. +// Warning 5729: (121-125): BMC does not yet implement this type of function call. +// Warning 5729: (121-125): BMC does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_external_address.sol b/test/libsolidity/smtCheckerTests/types/function_type_external_address.sol index 549701105bba..07bcce0a8efb 100644 --- a/test/libsolidity/smtCheckerTests/types/function_type_external_address.sol +++ b/test/libsolidity/smtCheckerTests/types/function_type_external_address.sol @@ -7,4 +7,4 @@ contract C { } // ---- // Warning 7650: (128-137): Assertion checker does not yet support this expression. -// Warning 4661: (141-155): Assertion violation happens here +// Warning 7650: (128-137): Assertion checker does not yet support this expression. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_members.sol b/test/libsolidity/smtCheckerTests/types/function_type_members.sol index 7b9a0f44da0c..b5f825b2b215 100644 --- a/test/libsolidity/smtCheckerTests/types/function_type_members.sol +++ b/test/libsolidity/smtCheckerTests/types/function_type_members.sol @@ -7,3 +7,4 @@ contract C { } // ---- // Warning 7650: (108-118): Assertion checker does not yet support this expression. +// Warning 7650: (108-118): Assertion checker does not yet support this expression. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_nested.sol b/test/libsolidity/smtCheckerTests/types/function_type_nested.sol index 2b6a0e538b8f..40fa83699d4a 100644 --- a/test/libsolidity/smtCheckerTests/types/function_type_nested.sol +++ b/test/libsolidity/smtCheckerTests/types/function_type_nested.sol @@ -12,10 +12,14 @@ contract C { } } // ---- -// Warning 5729: (123-128): Assertion checker does not yet implement this type of function call. // Warning 8115: (152-197): Assertion checker does not yet support the type of this variable. // Warning 8364: (212-214): Assertion checker does not yet implement type function (function (uint256)) -// Warning 5729: (212-219): Assertion checker does not yet implement this type of function call. // Warning 6031: (255-257): Internal error: Expression undefined for SMT solver. // Warning 8364: (255-257): Assertion checker does not yet implement type function (function (uint256)) -// Warning 5729: (212-219): Assertion checker does not yet implement this type of function call. +// Warning 5729: (123-128): BMC does not yet implement this type of function call. +// Warning 8115: (152-197): Assertion checker does not yet support the type of this variable. +// Warning 8364: (212-214): Assertion checker does not yet implement type function (function (uint256)) +// Warning 5729: (212-219): BMC does not yet implement this type of function call. +// Warning 6031: (255-257): Internal error: Expression undefined for SMT solver. +// Warning 8364: (255-257): Assertion checker does not yet implement type function (function (uint256)) +// Warning 5729: (212-219): BMC does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol b/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol index 65f321656c9b..ad2d02fa96ef 100644 --- a/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol +++ b/test/libsolidity/smtCheckerTests/types/function_type_nested_return.sol @@ -15,11 +15,16 @@ contract C { } } // ---- -// Warning 5729: (195-200): Assertion checker does not yet implement this type of function call. // Warning 8115: (224-269): Assertion checker does not yet support the type of this variable. // Warning 8364: (284-286): Assertion checker does not yet implement type function (function (uint256)) // Warning 1695: (287-288): Assertion checker does not yet support this global variable. -// Warning 5729: (284-291): Assertion checker does not yet implement this type of function call. // Warning 6031: (327-329): Internal error: Expression undefined for SMT solver. // Warning 8364: (327-329): Assertion checker does not yet implement type function (function (uint256)) -// Warning 5729: (284-291): Assertion checker does not yet implement this type of function call. +// Warning 5729: (195-200): BMC does not yet implement this type of function call. +// Warning 8115: (224-269): Assertion checker does not yet support the type of this variable. +// Warning 8364: (284-286): Assertion checker does not yet implement type function (function (uint256)) +// Warning 1695: (287-288): Assertion checker does not yet support this global variable. +// Warning 5729: (284-291): BMC does not yet implement this type of function call. +// Warning 6031: (327-329): Internal error: Expression undefined for SMT solver. +// Warning 8364: (327-329): Assertion checker does not yet implement type function (function (uint256)) +// Warning 5729: (284-291): BMC does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol index 079e1eded6ef..29e861862786 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_1_fail.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (134-153): Assertion violation happens here +// Warning 6328: (134-153): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 3\n\n\nTransaction trace:\nconstructor()\nf(3) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_2.sol index b8e9ebe5b233..9d663945fca8 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_2.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_2.sol @@ -8,4 +8,4 @@ contract C } } // ---- -// Warning 6328: (111-130): Assertion violation happens here +// Warning 6328: (111-130): CHC: Assertion violation happens here.\nCounterexample:\n\nx = false\n\n\nTransaction trace:\nconstructor()\nf(false) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol index 2156b469e20b..39c61a014969 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_2d_1_fail.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (154-178): Assertion violation happens here +// Warning 6328: (154-178): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 41\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol b/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol index 9792df9056bc..dfff57fdfd38 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_3d_1_fail.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (176-204): Assertion violation happens here +// Warning 6328: (176-204): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 41\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_5.sol b/test/libsolidity/smtCheckerTests/types/mapping_5.sol index 6ebdf518b89c..b2337ddd1505 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_5.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_5.sol @@ -8,4 +8,4 @@ contract C } } // ---- -// Warning 6328: (125-144): Assertion violation happens here +// Warning 6328: (125-144): CHC: Assertion violation happens here.\nCounterexample:\n\na = 38\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(38, 0) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol index afe1ce6dc325..5893f10ac06b 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_1.sol @@ -16,4 +16,4 @@ contract C } } // ---- -// Warning 6328: (266-286): Assertion violation happens here +// Warning 6328: (266-286): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol new file mode 100644 index 000000000000..e13b2fd5b29e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/mapping_aliasing_2.sol @@ -0,0 +1,35 @@ +pragma experimental SMTChecker; + +contract C +{ + mapping (uint => uint) a; + mapping (uint => mapping (uint => uint)) maps; + mapping (uint => mapping (uint => uint8)) maps8; + function f(mapping (uint => uint) storage map1, mapping (uint => uint) storage map2) internal { + map1[0] = 2; + a[0] = 42; + maps[0][0] = 42; + maps8[0][0] = 42; + map2[0] = 1; + // Fails because map2 == map1 is possible. + assert(map1[0] == 2); + // Fails because map2 == a is possible. + assert(a[0] == 42); + // Fails because map2 == maps[0] is possible. + // Removed because current Spacer seg faults in cex generation. + //assert(maps[0][0] == 42); + // Should not fail since knowledge is erased only for mapping (uint => uint). + assert(maps8[0][0] == 42); + assert(map2[0] == 1); + } + + function g(bool b, uint x, uint y) public { + if (b) + f(a, maps[y]); + else + f(maps[x], maps[y]); + } +} +// ---- +// Warning 6328: (397-417): CHC: Assertion violation happens here.\nCounterexample:\n\nb = true\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\ng(true, 0, 0) +// Warning 6328: (463-481): CHC: Assertion violation happens here.\nCounterexample:\n\nb = true\nx = 0\ny = 0\n\n\nTransaction trace:\nconstructor()\ng(true, 0, 0) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol index ec8c795a5311..c0531fe1ac80 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_as_local_var_1.sol @@ -16,6 +16,5 @@ contract c { } } // ---- -// Warning 6328: (288-324): Assertion violation happens here -// Warning 6328: (336-372): Assertion violation happens here -// Warning 6031: (166-178): Internal error: Expression undefined for SMT solver. +// Warning 6328: (288-324): CHC: Assertion violation happens here.\nCounterexample:\n\ncond = true\n\n\nTransaction trace:\nconstructor()\nf(true) +// Warning 6328: (336-372): CHC: Assertion violation happens here.\nCounterexample:\n\ncond = false\n\n\nTransaction trace:\nconstructor()\nf(false) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol b/test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol index cd2f34168b71..18b7efb43c15 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_as_parameter_1.sol @@ -12,4 +12,4 @@ contract c { } } // ---- -// Warning 6328: (289-306): Assertion violation happens here +// Warning 6328: (289-306): CHC: Assertion violation happens here.\nCounterexample:\n\na = 7719\nb = 38\n\n\nTransaction trace:\nconstructor()\ng(7719, 38) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol b/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol index f77baadef4c6..be5e034e9105 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_equal_keys_2.sol @@ -9,4 +9,4 @@ contract C } } // ---- -// Warning 6328: (119-133): Assertion violation happens here +// Warning 6328: (119-133): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 1\n\n\nTransaction trace:\nconstructor()\nf(0, 1) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_struct_assignment.sol b/test/libsolidity/smtCheckerTests/types/mapping_struct_assignment.sol index ec9ca8145a62..ae1da32023d2 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_struct_assignment.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_struct_assignment.sol @@ -11,9 +11,4 @@ contract C } } // ---- -// Warning 6838: (140-144): Condition is always false. -// Warning 8364: (149-156): Assertion checker does not yet implement type struct C.S storage ref -// Warning 8364: (159-160): Assertion checker does not yet implement type type(struct C.S storage pointer) -// Warning 8364: (159-163): Assertion checker does not yet implement type struct C.S memory -// Warning 4639: (159-163): Assertion checker does not yet implement this expression. -// Warning 8364: (149-163): Assertion checker does not yet implement type struct C.S storage ref +// Warning 6838: (140-144): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/types/no_effect_statements.sol b/test/libsolidity/smtCheckerTests/types/no_effect_statements.sol index 761b401c26a7..779fb13f6fd2 100644 --- a/test/libsolidity/smtCheckerTests/types/no_effect_statements.sol +++ b/test/libsolidity/smtCheckerTests/types/no_effect_statements.sol @@ -1,7 +1,7 @@ pragma experimental SMTChecker; contract test { - struct s { uint a; uint b;} - function f() pure public returns (byte) { + struct s { uint a; uint b;} + function f() pure public returns (bytes1) { s; s(1,2); s[7]; @@ -10,15 +10,13 @@ contract test { } } // ---- -// Warning 6133: (125-126): Statement has no effect. -// Warning 6133: (130-136): Statement has no effect. -// Warning 6133: (140-144): Statement has no effect. -// Warning 6133: (148-152): Statement has no effect. -// Warning 6133: (156-163): Statement has no effect. -// Warning 8364: (125-126): Assertion checker does not yet implement type type(struct test.s storage pointer) -// Warning 8364: (130-131): Assertion checker does not yet implement type type(struct test.s storage pointer) -// Warning 8364: (130-136): Assertion checker does not yet implement type struct test.s memory -// Warning 4639: (130-136): Assertion checker does not yet implement this expression. -// Warning 8364: (140-141): Assertion checker does not yet implement type type(struct test.s storage pointer) -// Warning 8364: (140-144): Assertion checker does not yet implement type type(struct test.s memory[7] memory) -// Warning 8364: (156-163): Assertion checker does not yet implement type type(uint256[7] memory) +// Warning 6321: (118-124): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6133: (130-131): Statement has no effect. +// Warning 6133: (135-141): Statement has no effect. +// Warning 6133: (145-149): Statement has no effect. +// Warning 6133: (153-157): Statement has no effect. +// Warning 6133: (161-168): Statement has no effect. +// Warning 8364: (145-149): Assertion checker does not yet implement type type(struct test.s memory[7] memory) +// Warning 8364: (161-168): Assertion checker does not yet implement type type(uint256[7] memory) +// Warning 8364: (145-149): Assertion checker does not yet implement type type(struct test.s memory[7] memory) +// Warning 8364: (161-168): Assertion checker does not yet implement type type(uint256[7] memory) diff --git a/test/libsolidity/smtCheckerTests/types/rational_large_1.sol b/test/libsolidity/smtCheckerTests/types/rational_large_1.sol index 5353ed6f8279..ef7d32b3cb10 100644 --- a/test/libsolidity/smtCheckerTests/types/rational_large_1.sol +++ b/test/libsolidity/smtCheckerTests/types/rational_large_1.sol @@ -7,4 +7,5 @@ contract c { } } // ---- -// Warning 6328: (128-142): Assertion violation happens here +// Warning 6321: (80-84): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (128-142): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol new file mode 100644 index 000000000000..15e9c4dfd25b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_1.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + uint[][] a; + function f(uint[1] memory x) public { + a.push(x); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol new file mode 100644 index 000000000000..2a267cdd9b04 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_2.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C { + uint[][] a; + function f(uint[1][] memory x) public { + a.push(x[2]); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol new file mode 100644 index 000000000000..09b8a9773e84 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_3.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract D { + bytes16[] inner; + bytes32[][] data; + function t() public { + data.push(inner); + } +} + diff --git a/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol new file mode 100644 index 000000000000..cbfd162caf19 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/static_array_implicit_push_4.sol @@ -0,0 +1,9 @@ +pragma experimental SMTChecker; +contract D { + int16[] inner; + int[][] data; + function t() public { + data.push(inner); + } +} + diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol index 4bb6409ca654..e6c683b1694f 100644 --- a/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol @@ -19,4 +19,4 @@ contract C } } // ---- -// Warning 6328: (362-421): Assertion violation happens here +// Warning 6328: (362-421): CHC: Assertion violation happens here.\nCounterexample:\na = 512, b = false, c = 0\nx = 1\n\n\nTransaction trace:\nconstructor()\nState: a = 0, b = false, c = 0\nf(1) diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol index f18a466b1bd9..1790ccc95923 100644 --- a/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol @@ -9,4 +9,4 @@ contract C } } // ---- -// Warning 6328: (123-136): Assertion violation happens here +// Warning 6328: (123-136): CHC: Assertion violation happens here.\nCounterexample:\na = 0, b = false, c = 0\n\n\n\nTransaction trace:\nconstructor()\nState: a = 0, b = false, c = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_4.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_4.sol index 776c2541ca19..d709b7244746 100644 --- a/test/libsolidity/smtCheckerTests/types/storage_value_vars_4.sol +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_4.sol @@ -7,4 +7,4 @@ contract C uint c; } // ---- -// Warning 6328: (84-97): Assertion violation happens here +// Warning 6328: (84-97): CHC: Assertion violation happens here.\nCounterexample:\nc = 0\n\n\n\nTransaction trace:\nconstructor()\nState: c = 0\nf() diff --git a/test/libsolidity/smtCheckerTests/types/string_1.sol b/test/libsolidity/smtCheckerTests/types/string_1.sol index 19a7fa094fff..efe8fd26921f 100644 --- a/test/libsolidity/smtCheckerTests/types/string_1.sol +++ b/test/libsolidity/smtCheckerTests/types/string_1.sol @@ -7,4 +7,4 @@ contract C } } // ---- -// Warning 6328: (110-154): Assertion violation happens here +// Warning 6328: (110-154): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf(s1, s2) diff --git a/test/libsolidity/smtCheckerTests/types/string_length.sol b/test/libsolidity/smtCheckerTests/types/string_length.sol new file mode 100644 index 000000000000..632eada19cc5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/string_length.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + function f() public pure { + string memory x = "Hello World"; + assert(bytes(x).length == 11); + } + function g() public pure { + string memory x = unicode"Hello World"; + assert(bytes(x).length == 11); + } + function h() public pure { + bytes memory x = unicode"Hello World"; + string memory y = string(x); + assert(bytes(y).length == 11); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_1.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_1.sol index 834cce276daf..ae127808e84a 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_1.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_1.sol @@ -10,4 +10,4 @@ contract C { } } // ---- -// Warning 6328: (175-190): Assertion violation happens here +// Warning 6328: (175-190): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\n\n\nTransaction trace:\nconstructor()\nf(52647538817385212172903286807934654968315727694643370704309751478220717293568) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_2.sol index c2b74b2fe113..fe6cf0840a5a 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_2.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_2.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// Warning 6328: (176-191): Assertion violation happens here +// Warning 6328: (176-191): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\n\n\nTransaction trace:\nconstructor()\nf(52647538817385212172903286807934654968315727694643370704309751478220717293568) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_3.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_3.sol index 3fe3bf2d1e42..1bc96e33a397 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_3.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_3.sol @@ -11,4 +11,4 @@ contract C { } } // ---- -// Warning 6328: (186-201): Assertion violation happens here +// Warning 6328: (186-201): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\n\n\nTransaction trace:\nconstructor()\nf(52647538817385212172903286807934654968315727694643370704309751478220717293568) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_4.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_4.sol index f72e6386f886..e23df76fb141 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_4.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_4.sol @@ -15,4 +15,4 @@ contract C { } } // ---- -// Warning 6328: (261-276): Assertion violation happens here +// Warning 6328: (261-276): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\n\n\nTransaction trace:\nconstructor()\nf(52647538817385212172903286807934654968315727694643370704309751478220717293568) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_5.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_5.sol index ee49949139a1..3e51776410eb 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_5.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_5.sol @@ -13,4 +13,4 @@ contract C { } } // ---- -// Warning 6328: (251-266): Assertion violation happens here +// Warning 6328: (251-266): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\n\n\nTransaction trace:\nconstructor()\nf(52647538817385212172903286807934654968315727694643370704309751478220717293568) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_1.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_1.sol index 92f3c7e41cd0..261d3f19a646 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_1.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_1.sol @@ -10,4 +10,4 @@ contract C { } } // ---- -// Warning 6328: (170-190): Assertion violation happens here +// Warning 6328: (170-190): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\n\n\nTransaction trace:\nconstructor()\nf(52647538817385212172903286807934654968315727694643370704309751478220717293568) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol index 28f296addbe3..476114d86af8 100644 --- a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol +++ b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_2.sol @@ -10,5 +10,5 @@ contract C { } } // ---- -// Warning 6328: (147-166): Assertion violation happens here -// Warning 6328: (170-190): Assertion violation happens here +// Warning 6328: (147-166): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0\n\n\nTransaction trace:\nconstructor()\nf(0) +// Warning 6328: (170-190): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538830022687173130149211684818290356179572910782152375644828738034597888\n\n\nTransaction trace:\nconstructor()\nf(52647538830022687173130149211684818290356179572910782152375644828738034597888) diff --git a/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_memory_safe.sol b/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_memory_safe.sol new file mode 100644 index 000000000000..a52c66f06de8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_memory_safe.sol @@ -0,0 +1,30 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + function f() public pure { + S[] memory s1 = new S[](3); + assert(s1.length == 3); + s1[0].x = 2; + assert(s1[0].x == 2); + s1[1].t.y = 3; + assert(s1[1].t.y == 3); + s1[2].a[2] = 4; + assert(s1[2].a[2] == 4); + s1[0].ts[3].y = 5; + assert(s1[0].ts[3].y == 5); + s1[1].ts[4].a[5] = 6; + assert(s1[1].ts[4].a[5] == 6); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_memory_unsafe.sol b/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_memory_unsafe.sol new file mode 100644 index 000000000000..7cdacc86b53b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_memory_unsafe.sol @@ -0,0 +1,34 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + function f(S memory s2) public pure { + S[] memory s1 = new S[](3); + // Removed because current Spacer seg faults in cex generation. + //assert(s1.length == 3); + s1[0].x = 2; + // Removed because current Spacer seg faults in cex generation. + //assert(s1[0].x == s2.x); + s1[1].t.y = 3; + // Removed because current Spacer seg faults in cex generation. + //assert(s1[1].t.y == s2.t.y); + s1[2].a[2] = 4; + // Removed because current Spacer seg faults in cex generation. + //assert(s1[2].a[2] == s2.a[2]); + s1[0].ts[3].y = 5; + // Removed because current Spacer seg faults in cex generation. + //assert(s1[0].ts[3].y == s2.ts[3].y); + } +} +// ---- +// Warning 5667: (183-194): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_storage_safe.sol b/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_storage_safe.sol new file mode 100644 index 000000000000..941710403cfc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/array_struct_array_struct_storage_safe.sol @@ -0,0 +1,34 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + S[] s1; + function f() public { + s1.push(); + s1.push(); + s1.push(); + s1[0].x = 2; + assert(s1[0].x == 2); + s1[1].t.y = 3; + assert(s1[1].t.y == 3); + s1[2].a[2] = 4; + assert(s1[2].a[2] == 4); + s1[0].ts[3].y = 5; + assert(s1[0].ts[3].y == 5); + s1[1].ts[4].a[5] = 6; + assert(s1[1].ts[4].a[5] == 6); + s1.pop(); + s1.pop(); + s1.pop(); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_aliasing_memory.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_aliasing_memory.sol new file mode 100644 index 000000000000..5748823acc5f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_aliasing_memory.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct S { + uint x; + uint[] a; + } + function f(S memory s1, S memory s2, bool b) public pure { + S memory s3 = b ? s1 : s2; + assert(s3.x == s1.x); + assert(s3.x == s2.x); + // This is safe. + assert(s3.x == s1.x || s3.x == s2.x); + // This fails as false positive because of lack of support to aliasing. + s3.x = 42; + assert(s3.x == s1.x || s3.x == s2.x); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (208-228): CHC: Assertion violation happens here. +// Warning 6328: (232-252): CHC: Assertion violation happens here. +// Warning 6328: (402-438): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_aliasing_storage.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_aliasing_storage.sol new file mode 100644 index 000000000000..344839990059 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_aliasing_storage.sol @@ -0,0 +1,29 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + uint[] a; + } + S s1; + S s2; + function f(bool b) public { + S storage s3 = b ? s1 : s2; + // Disabled because Spacer 4.8.9 seg fauts. + //assert(s3.x == s1.x); + //assert(s3.x == s2.x); + // This is safe. + assert(s3.x == s1.x || s3.x == s2.x); + // This fails as false positive because of lack of support to aliasing. + s3.x = 42; + assert(s3.x == s1.x || s3.x == s2.x); + } + function g(bool b, uint _x) public { + if (b) + s1.x = _x; + else + s2.x = _x; + } +} +// ---- +// Warning 6328: (402-438): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 0, a: []}, s2 = {x: 0, a: []}\nb = false\n\n\nTransaction trace:\nconstructor()\nState: s1 = {x: 0, a: []}, s2 = {x: 0, a: []}\nf(false) diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_safe.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_safe.sol new file mode 100644 index 000000000000..bb48fe150cb9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_safe.sol @@ -0,0 +1,28 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + function f() public pure { + S memory s1; + s1.x = 2; + assert(s1.x == 2); + s1.t.y = 3; + assert(s1.t.y == 3); + s1.a[2] = 4; + assert(s1.a[2] == 4); + s1.ts[3].y = 5; + assert(s1.ts[3].y == 5); + s1.ts[4].a[5] = 6; + assert(s1.ts[4].a[5] == 6); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_1.sol new file mode 100644 index 000000000000..29c6a9684107 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_1.sol @@ -0,0 +1,34 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + function f() public pure { + S memory s1; + s1.x = 2; + assert(s1.x != 2); + s1.t.y = 3; + assert(s1.t.y != 3); + s1.a[2] = 4; + assert(s1.a[2] != 4); + s1.ts[3].y = 5; + assert(s1.ts[3].y != 5); + s1.ts[4].a[5] = 6; + assert(s1.ts[4].a[5] != 6); + } +} +// ---- +// Warning 6328: (228-245): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (263-282): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (301-321): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (343-366): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (391-417): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol new file mode 100644 index 000000000000..abf3441dfc91 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_memory_unsafe_2.sol @@ -0,0 +1,34 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + function f(S memory s2) public pure { + S memory s1; + s1.x = 2; + // Removed because current Spacer seg faults in cex generation. + //assert(s1.x == s2.x); + s1.t.y = 3; + // Removed because current Spacer seg faults in cex generation. + //assert(s1.t.y == s2.t.y); + s1.a[2] = 4; + // Removed because current Spacer seg faults in cex generation. + //assert(s1.a[2] == s2.a[2]); + s1.ts[3].y = 5; + // Removed because current Spacer seg faults in cex generation. + //assert(s1.ts[3].y == s2.ts[3].y); + s1.ts[4].a[5] = 6; + assert(s1.ts[4].a[5] == s2.ts[4].a[5]); + } +} +// ---- +// Warning 6328: (697-735): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_safe.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_safe.sol new file mode 100644 index 000000000000..4240f6267173 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_safe.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + S s1; + function f() public { + s1.x = 2; + assert(s1.x == 2); + s1.t.y = 3; + assert(s1.t.y == 3); + s1.a[2] = 4; + assert(s1.a[2] == 4); + s1.ts[3].y = 5; + assert(s1.ts[3].y == 5); + s1.ts[4].a[5] = 6; + assert(s1.ts[4].a[5] == 6); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol new file mode 100644 index 000000000000..fac24eaa272c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_array_struct_array_storage_unsafe_1.sol @@ -0,0 +1,33 @@ +pragma experimental SMTChecker; + +contract C { + struct T { + uint y; + uint[] a; + } + struct S { + uint x; + T t; + uint[] a; + T[] ts; + } + S s1; + function f() public { + s1.x = 2; + assert(s1.x != 2); + s1.t.y = 3; + assert(s1.t.y != 3); + s1.a[2] = 4; + assert(s1.a[2] != 4); + s1.ts[3].y = 5; + assert(s1.ts[3].y != 5); + s1.ts[4].a[5] = 6; + assert(s1.ts[4].a[5] != 6); + } +} +// ---- +// Warning 6328: (181-198): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 0, a: []}, a: [], ts: []}\n\n\n\nTransaction trace:\nconstructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nf() +// Warning 6328: (216-235): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [], ts: []}\n\n\n\nTransaction trace:\nconstructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nf() +// Warning 6328: (254-274): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [], ts: []}\n\n\n\nTransaction trace:\nconstructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nf() +// Warning 6328: (296-319): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [], ts: []}\n\n\n\nTransaction trace:\nconstructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nf() +// Warning 6328: (344-370): CHC: Assertion violation happens here.\nCounterexample:\ns1 = {x: 2, t: {y: 3, a: []}, a: [], ts: []}\n\n\n\nTransaction trace:\nconstructor()\nState: s1 = {x: 0, t: {y: 0, a: []}, a: [], ts: []}\nf() diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_named_args.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_named_args.sol new file mode 100644 index 000000000000..212eeaf4b5bf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_named_args.sol @@ -0,0 +1,23 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + + struct T { + S s; + uint y; + } + + function test() pure public { + S memory inner = S({x: 43}); + T memory outer = T({y: 512, s: inner}); + assert(outer.y == 512); + assert(outer.s.x == 43); + assert(outer.s.x == 42); + } +} +// ---- +// Warning 6328: (265-288): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ntest() diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_named_args_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_named_args_2.sol new file mode 100644 index 000000000000..b178af610c47 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_named_args_2.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + uint y; + uint z; + } + + function test() pure public { + S memory s = S({z: 1, y: 2, x: 3}); + assert(s.x == 3); + assert(s.y == 2); + assert(s.z == 1); + assert(s.x == 0 || s.y == 0 || s.z == 0); // should fail + } +} +// ---- +// Warning 6328: (224-264): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ntest() diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_recursive_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_recursive_1.sol new file mode 100644 index 000000000000..1265adbb961d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_recursive_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract Test { + struct RecursiveStruct { + RecursiveStruct[] vals; + } + function func() public pure { + RecursiveStruct[1] memory val = [ RecursiveStruct(new RecursiveStruct[](42)) ]; + } +} +// ---- +// Warning 2072: (136-165): Unused local variable. +// Warning 8364: (170-212): Assertion checker does not yet implement type struct Test.RecursiveStruct memory +// Warning 8364: (170-212): Assertion checker does not yet implement type struct Test.RecursiveStruct memory diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_recursive_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_recursive_2.sol new file mode 100644 index 000000000000..08194e68bc62 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_recursive_2.sol @@ -0,0 +1,21 @@ +pragma experimental SMTChecker; +contract Test { + struct RecursiveStruct { + uint x; + RecursiveStruct[] vals; + } + function func() public pure { + RecursiveStruct memory val = RecursiveStruct(1, new RecursiveStruct[](42)); + assert(val.x == 1); + } +} +// ---- +// Warning 8115: (146-172): Assertion checker does not yet support the type of this variable. +// Warning 8364: (175-220): Assertion checker does not yet implement type struct Test.RecursiveStruct memory +// Warning 7650: (231-236): Assertion checker does not yet support this expression. +// Warning 8364: (231-234): Assertion checker does not yet implement type struct Test.RecursiveStruct memory +// Warning 6328: (224-242): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nfunc() +// Warning 8115: (146-172): Assertion checker does not yet support the type of this variable. +// Warning 8364: (175-220): Assertion checker does not yet implement type struct Test.RecursiveStruct memory +// Warning 7650: (231-236): Assertion checker does not yet support this expression. +// Warning 8364: (231-234): Assertion checker does not yet implement type struct Test.RecursiveStruct memory diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_delete_memory.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_delete_memory.sol new file mode 100644 index 000000000000..e63ea49f5b1f --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_delete_memory.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct S { + uint x; + uint[] a; + } + function f(S memory s1, S memory s2) public pure { + delete s1; + assert(s1.x == s2.x); + assert(s1.a.length == s2.a.length); + assert(s1.a.length == 0); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (184-204): CHC: Assertion violation happens here. +// Warning 6328: (208-242): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_delete_storage.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_delete_storage.sol new file mode 100644 index 000000000000..be21014e8693 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_delete_storage.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct S { + uint x; + uint[] a; + } + S s1; + function g(S memory s2) public { + s1.x = s2.x; + s1.a = s2.a; + } + function f(S memory s2) public { + delete s1; + assert(s1.x == s2.x); + assert(s1.a.length == s2.a.length); + assert(s1.a.length == 0); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (240-260): CHC: Assertion violation happens here. +// Warning 6328: (264-298): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_mapping.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_mapping.sol new file mode 100644 index 000000000000..d5ffe661589d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_mapping.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + mapping (uint => uint) m; + } + S s1; + S s2; + function f() public view { + // Disabled because Spacer 4.8.9 seg faults. + //assert(s1.m[0] == s2.m[0]); + } + function g(uint a, uint b) public { + s1.m[a] = b; + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_nested_constructor.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_nested_constructor.sol new file mode 100644 index 000000000000..eb82c271a070 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_nested_constructor.sol @@ -0,0 +1,23 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + + struct T { + S s; + uint y; + } + + function test() pure public { + S memory inner = S(43); + T memory outer = T(inner, 512); + assert(outer.y == 512); + assert(outer.s.x == 43); + assert(outer.s.x == 42); + } +} +// ---- +// Warning 6328: (252-275): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ntest() diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_nested_constructor_named_args.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_nested_constructor_named_args.sol new file mode 100644 index 000000000000..7fae9133da04 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_nested_constructor_named_args.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C { + struct B { uint b1; } + struct A { uint a1; B a2; } + function f() public pure { + A memory a = A({ a1: 1, a2: B({b1: 2}) }); + assert(a.a1 == 1 && a.a2.b1 == 2); + } +} diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_nested_temporary.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_nested_temporary.sol new file mode 100644 index 000000000000..9cc180219e8c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_nested_temporary.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + + struct T { + S s; + uint y; + } + + function test() pure public { + assert(T(S(42), 1).s.x == 42); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_1.sol new file mode 100644 index 000000000000..4027557dcec3 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_1.sol @@ -0,0 +1,37 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + S[] a; + } + S s1; + S s2; + function f() public view { + assert(s1.x == s2.x); + assert(s1.a.length == s2.a.length); + } +} +// ---- +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (131-135): Assertion checker does not yet support this expression. +// Warning 8364: (131-133): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (139-143): Assertion checker does not yet support this expression. +// Warning 8364: (139-141): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (155-159): Assertion checker does not yet support this expression. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-174): Assertion checker does not yet support this expression. +// Warning 8364: (170-172): Assertion checker does not yet implement type struct C.S storage ref +// Warning 6328: (124-144): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (148-182): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (131-135): Assertion checker does not yet support this expression. +// Warning 8364: (131-133): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (139-143): Assertion checker does not yet support this expression. +// Warning 8364: (139-141): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (155-159): Assertion checker does not yet support this expression. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-174): Assertion checker does not yet support this expression. +// Warning 8364: (170-172): Assertion checker does not yet implement type struct C.S storage ref diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_2.sol new file mode 100644 index 000000000000..c9011eab3618 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_2.sol @@ -0,0 +1,111 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + S[] a; + } + S s1; + S s2; + function f() public view { + assert(s1.x == s2.x); + assert(s1.a.length == s2.a.length); + assert(s1.a[0].x == s2.a[0].x); + } + function g() public { + s1.x = 42; + s2.x = 42; + s1.a.push(); + s2.a.push(); + s1.a[0].x = 43; + s2.a[0].x = 43; + } +} +// ---- +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (131-135): Assertion checker does not yet support this expression. +// Warning 8364: (131-133): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (139-143): Assertion checker does not yet support this expression. +// Warning 8364: (139-141): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (155-159): Assertion checker does not yet support this expression. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-174): Assertion checker does not yet support this expression. +// Warning 8364: (170-172): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (193-202): Assertion checker does not yet support this expression. +// Warning 7650: (193-197): Assertion checker does not yet support this expression. +// Warning 8364: (193-195): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (193-200): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (206-215): Assertion checker does not yet support this expression. +// Warning 7650: (206-210): Assertion checker does not yet support this expression. +// Warning 8364: (206-208): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (206-213): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (246-250): Assertion checker does not yet support this expression. +// Warning 8364: (246-248): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (246-250): Assertion checker does not support recursive structs. +// Warning 7650: (259-263): Assertion checker does not yet support this expression. +// Warning 8364: (259-261): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (259-263): Assertion checker does not support recursive structs. +// Warning 7650: (272-276): Assertion checker does not yet support this expression. +// Warning 8364: (272-274): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (272-283): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (272-276): Assertion checker does not support recursive structs. +// Warning 7650: (287-291): Assertion checker does not yet support this expression. +// Warning 8364: (287-289): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (287-298): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (287-291): Assertion checker does not support recursive structs. +// Warning 7650: (302-311): Assertion checker does not yet support this expression. +// Warning 7650: (302-306): Assertion checker does not yet support this expression. +// Warning 8364: (302-304): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (302-309): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (302-311): Assertion checker does not support recursive structs. +// Warning 7650: (320-329): Assertion checker does not yet support this expression. +// Warning 7650: (320-324): Assertion checker does not yet support this expression. +// Warning 8364: (320-322): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (320-327): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (320-329): Assertion checker does not support recursive structs. +// Warning 6328: (124-144): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (148-182): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (186-216): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (131-135): Assertion checker does not yet support this expression. +// Warning 8364: (131-133): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (139-143): Assertion checker does not yet support this expression. +// Warning 8364: (139-141): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (155-159): Assertion checker does not yet support this expression. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-174): Assertion checker does not yet support this expression. +// Warning 8364: (170-172): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (193-202): Assertion checker does not yet support this expression. +// Warning 7650: (193-197): Assertion checker does not yet support this expression. +// Warning 8364: (193-195): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (193-200): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (206-215): Assertion checker does not yet support this expression. +// Warning 7650: (206-210): Assertion checker does not yet support this expression. +// Warning 8364: (206-208): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (206-213): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (246-250): Assertion checker does not yet support this expression. +// Warning 8364: (246-248): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (246-250): Assertion checker does not support recursive structs. +// Warning 7650: (259-263): Assertion checker does not yet support this expression. +// Warning 8364: (259-261): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (259-263): Assertion checker does not support recursive structs. +// Warning 7650: (272-276): Assertion checker does not yet support this expression. +// Warning 8364: (272-274): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (272-283): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (272-276): Assertion checker does not support recursive structs. +// Warning 7650: (287-291): Assertion checker does not yet support this expression. +// Warning 8364: (287-289): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (287-298): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (287-291): Assertion checker does not support recursive structs. +// Warning 7650: (302-311): Assertion checker does not yet support this expression. +// Warning 7650: (302-306): Assertion checker does not yet support this expression. +// Warning 8364: (302-304): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (302-309): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (302-311): Assertion checker does not support recursive structs. +// Warning 7650: (320-329): Assertion checker does not yet support this expression. +// Warning 7650: (320-324): Assertion checker does not yet support this expression. +// Warning 8364: (320-322): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (320-327): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (320-329): Assertion checker does not support recursive structs. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_3.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_3.sol new file mode 100644 index 000000000000..56eff92c88b0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_3.sol @@ -0,0 +1,211 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + S[] a; + } + S s1; + S s2; + function f() public view { + assert(s1.x == s2.x); + assert(s1.a.length == s2.a.length); + assert(s1.a[0].x == s2.a[0].x); + assert(s1.a[0].a.length == s2.a[0].a.length); + assert(s1.a[0].a[0].x == s2.a[0].a[0].x); + } + function g() public { + s1.x = 42; + s2.x = 42; + s1.a.push(); + s2.a.push(); + s1.a[0].x = 43; + s2.a[0].x = 43; + s1.a[0].a.push(); + s2.a[0].a.push(); + s1.a[0].a[0].x = 44; + s2.a[0].a[0].x = 44; + } +} +// ---- +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (131-135): Assertion checker does not yet support this expression. +// Warning 8364: (131-133): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (139-143): Assertion checker does not yet support this expression. +// Warning 8364: (139-141): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (155-159): Assertion checker does not yet support this expression. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-174): Assertion checker does not yet support this expression. +// Warning 8364: (170-172): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (193-202): Assertion checker does not yet support this expression. +// Warning 7650: (193-197): Assertion checker does not yet support this expression. +// Warning 8364: (193-195): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (193-200): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (206-215): Assertion checker does not yet support this expression. +// Warning 7650: (206-210): Assertion checker does not yet support this expression. +// Warning 8364: (206-208): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (206-213): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (227-236): Assertion checker does not yet support this expression. +// Warning 7650: (227-231): Assertion checker does not yet support this expression. +// Warning 8364: (227-229): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (227-234): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (247-256): Assertion checker does not yet support this expression. +// Warning 7650: (247-251): Assertion checker does not yet support this expression. +// Warning 8364: (247-249): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (247-254): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (275-289): Assertion checker does not yet support this expression. +// Warning 7650: (275-284): Assertion checker does not yet support this expression. +// Warning 7650: (275-279): Assertion checker does not yet support this expression. +// Warning 8364: (275-277): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (275-282): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (275-287): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (293-307): Assertion checker does not yet support this expression. +// Warning 7650: (293-302): Assertion checker does not yet support this expression. +// Warning 7650: (293-297): Assertion checker does not yet support this expression. +// Warning 8364: (293-295): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (293-300): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (293-305): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (338-342): Assertion checker does not yet support this expression. +// Warning 8364: (338-340): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (338-342): Assertion checker does not support recursive structs. +// Warning 7650: (351-355): Assertion checker does not yet support this expression. +// Warning 8364: (351-353): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (351-355): Assertion checker does not support recursive structs. +// Warning 7650: (364-368): Assertion checker does not yet support this expression. +// Warning 8364: (364-366): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (364-375): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (364-368): Assertion checker does not support recursive structs. +// Warning 7650: (379-383): Assertion checker does not yet support this expression. +// Warning 8364: (379-381): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (379-390): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (379-383): Assertion checker does not support recursive structs. +// Warning 7650: (394-403): Assertion checker does not yet support this expression. +// Warning 7650: (394-398): Assertion checker does not yet support this expression. +// Warning 8364: (394-396): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (394-401): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (394-403): Assertion checker does not support recursive structs. +// Warning 7650: (412-421): Assertion checker does not yet support this expression. +// Warning 7650: (412-416): Assertion checker does not yet support this expression. +// Warning 8364: (412-414): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (412-419): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (412-421): Assertion checker does not support recursive structs. +// Warning 7650: (430-439): Assertion checker does not yet support this expression. +// Warning 7650: (430-434): Assertion checker does not yet support this expression. +// Warning 8364: (430-432): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (430-437): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (430-446): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (430-439): Assertion checker does not support recursive structs. +// Warning 7650: (450-459): Assertion checker does not yet support this expression. +// Warning 7650: (450-454): Assertion checker does not yet support this expression. +// Warning 8364: (450-452): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (450-457): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (450-466): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (450-459): Assertion checker does not support recursive structs. +// Warning 7650: (470-484): Assertion checker does not yet support this expression. +// Warning 7650: (470-479): Assertion checker does not yet support this expression. +// Warning 7650: (470-474): Assertion checker does not yet support this expression. +// Warning 8364: (470-472): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (470-477): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (470-482): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (470-484): Assertion checker does not support recursive structs. +// Warning 7650: (493-507): Assertion checker does not yet support this expression. +// Warning 7650: (493-502): Assertion checker does not yet support this expression. +// Warning 7650: (493-497): Assertion checker does not yet support this expression. +// Warning 8364: (493-495): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (493-500): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (493-505): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (493-507): Assertion checker does not support recursive structs. +// Warning 6328: (124-144): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (148-182): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (186-216): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (220-264): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (268-308): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (131-135): Assertion checker does not yet support this expression. +// Warning 8364: (131-133): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (139-143): Assertion checker does not yet support this expression. +// Warning 8364: (139-141): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (155-159): Assertion checker does not yet support this expression. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-174): Assertion checker does not yet support this expression. +// Warning 8364: (170-172): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (193-202): Assertion checker does not yet support this expression. +// Warning 7650: (193-197): Assertion checker does not yet support this expression. +// Warning 8364: (193-195): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (193-200): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (206-215): Assertion checker does not yet support this expression. +// Warning 7650: (206-210): Assertion checker does not yet support this expression. +// Warning 8364: (206-208): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (206-213): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (227-236): Assertion checker does not yet support this expression. +// Warning 7650: (227-231): Assertion checker does not yet support this expression. +// Warning 8364: (227-229): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (227-234): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (247-256): Assertion checker does not yet support this expression. +// Warning 7650: (247-251): Assertion checker does not yet support this expression. +// Warning 8364: (247-249): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (247-254): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (275-289): Assertion checker does not yet support this expression. +// Warning 7650: (275-284): Assertion checker does not yet support this expression. +// Warning 7650: (275-279): Assertion checker does not yet support this expression. +// Warning 8364: (275-277): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (275-282): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (275-287): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (293-307): Assertion checker does not yet support this expression. +// Warning 7650: (293-302): Assertion checker does not yet support this expression. +// Warning 7650: (293-297): Assertion checker does not yet support this expression. +// Warning 8364: (293-295): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (293-300): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (293-305): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (338-342): Assertion checker does not yet support this expression. +// Warning 8364: (338-340): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (338-342): Assertion checker does not support recursive structs. +// Warning 7650: (351-355): Assertion checker does not yet support this expression. +// Warning 8364: (351-353): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (351-355): Assertion checker does not support recursive structs. +// Warning 7650: (364-368): Assertion checker does not yet support this expression. +// Warning 8364: (364-366): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (364-375): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (364-368): Assertion checker does not support recursive structs. +// Warning 7650: (379-383): Assertion checker does not yet support this expression. +// Warning 8364: (379-381): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (379-390): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (379-383): Assertion checker does not support recursive structs. +// Warning 7650: (394-403): Assertion checker does not yet support this expression. +// Warning 7650: (394-398): Assertion checker does not yet support this expression. +// Warning 8364: (394-396): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (394-401): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (394-403): Assertion checker does not support recursive structs. +// Warning 7650: (412-421): Assertion checker does not yet support this expression. +// Warning 7650: (412-416): Assertion checker does not yet support this expression. +// Warning 8364: (412-414): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (412-419): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (412-421): Assertion checker does not support recursive structs. +// Warning 7650: (430-439): Assertion checker does not yet support this expression. +// Warning 7650: (430-434): Assertion checker does not yet support this expression. +// Warning 8364: (430-432): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (430-437): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (430-446): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (430-439): Assertion checker does not support recursive structs. +// Warning 7650: (450-459): Assertion checker does not yet support this expression. +// Warning 7650: (450-454): Assertion checker does not yet support this expression. +// Warning 8364: (450-452): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (450-457): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (450-466): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (450-459): Assertion checker does not support recursive structs. +// Warning 7650: (470-484): Assertion checker does not yet support this expression. +// Warning 7650: (470-479): Assertion checker does not yet support this expression. +// Warning 7650: (470-474): Assertion checker does not yet support this expression. +// Warning 8364: (470-472): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (470-477): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (470-482): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (470-484): Assertion checker does not support recursive structs. +// Warning 7650: (493-507): Assertion checker does not yet support this expression. +// Warning 7650: (493-502): Assertion checker does not yet support this expression. +// Warning 7650: (493-497): Assertion checker does not yet support this expression. +// Warning 8364: (493-495): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (493-500): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (493-505): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (493-507): Assertion checker does not support recursive structs. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_4.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_4.sol new file mode 100644 index 000000000000..3a6d975ec470 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_4.sol @@ -0,0 +1,89 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + S[] a; + } + S s1; + S s2; + function f(bool b1, bool b2) public { + S storage s3 = b1 ? s1 : s2; + S storage s4 = b2 ? s1 : s2; + assert(s3.x == s1.x || s3.x == s2.x); + assert(s4.x == s1.x || s4.x == s2.x); + s3.x = 44; + // Fails as false positive because of lack of support to aliasing. + assert(s1.x == 44 || s2.x == 44); + } +} +// ---- +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 8115: (135-147): Assertion checker does not yet support the type of this variable. +// Warning 8115: (166-178): Assertion checker does not yet support the type of this variable. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (160-162): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (150-162): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 8364: (186-188): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (191-193): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (181-193): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (204-208): Assertion checker does not yet support this expression. +// Warning 8364: (204-206): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (212-216): Assertion checker does not yet support this expression. +// Warning 8364: (212-214): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (220-224): Assertion checker does not yet support this expression. +// Warning 8364: (220-222): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (228-232): Assertion checker does not yet support this expression. +// Warning 8364: (228-230): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (244-248): Assertion checker does not yet support this expression. +// Warning 8364: (244-246): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (252-256): Assertion checker does not yet support this expression. +// Warning 8364: (252-254): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (260-264): Assertion checker does not yet support this expression. +// Warning 8364: (260-262): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (268-272): Assertion checker does not yet support this expression. +// Warning 8364: (268-270): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (277-281): Assertion checker does not yet support this expression. +// Warning 8364: (277-279): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 4375: (277-281): Assertion checker does not support recursive structs. +// Warning 7650: (366-370): Assertion checker does not yet support this expression. +// Warning 8364: (366-368): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (380-384): Assertion checker does not yet support this expression. +// Warning 8364: (380-382): Assertion checker does not yet implement type struct C.S storage ref +// Warning 6328: (197-233): CHC: Assertion violation happens here.\nCounterexample:\n\nb1 = false\nb2 = false\n\n\nTransaction trace:\nconstructor()\nf(false, false) +// Warning 6328: (237-273): CHC: Assertion violation happens here.\nCounterexample:\n\nb1 = false\nb2 = false\n\n\nTransaction trace:\nconstructor()\nf(false, false) +// Warning 6328: (359-391): CHC: Assertion violation happens here.\nCounterexample:\n\nb1 = false\nb2 = false\n\n\nTransaction trace:\nconstructor()\nf(false, false) +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 8115: (135-147): Assertion checker does not yet support the type of this variable. +// Warning 8115: (166-178): Assertion checker does not yet support the type of this variable. +// Warning 8364: (155-157): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (160-162): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (150-162): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 8364: (186-188): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (191-193): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (181-193): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (204-208): Assertion checker does not yet support this expression. +// Warning 8364: (204-206): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (212-216): Assertion checker does not yet support this expression. +// Warning 8364: (212-214): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (220-224): Assertion checker does not yet support this expression. +// Warning 8364: (220-222): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (228-232): Assertion checker does not yet support this expression. +// Warning 8364: (228-230): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (244-248): Assertion checker does not yet support this expression. +// Warning 8364: (244-246): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (252-256): Assertion checker does not yet support this expression. +// Warning 8364: (252-254): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (260-264): Assertion checker does not yet support this expression. +// Warning 8364: (260-262): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 7650: (268-272): Assertion checker does not yet support this expression. +// Warning 8364: (268-270): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (277-281): Assertion checker does not yet support this expression. +// Warning 8364: (277-279): Assertion checker does not yet implement type struct C.S storage pointer +// Warning 4375: (277-281): Assertion checker does not support recursive structs. +// Warning 7650: (366-370): Assertion checker does not yet support this expression. +// Warning 8364: (366-368): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (380-384): Assertion checker does not yet support this expression. +// Warning 8364: (380-382): Assertion checker does not yet implement type struct C.S storage ref diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_5.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_5.sol new file mode 100644 index 000000000000..878cbde70477 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_5.sol @@ -0,0 +1,39 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + S[] a; + } + S[] sa; + S[][] sa2; + function f() public { + sa.push(); + sa2.push(); + sa2[0].push(); + sa2[0][0].a.push(); + assert(sa2[0][0].a.length == sa[0].a.length); + } +} +// ---- +// Warning 8364: (126-135): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (153-166): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-181): Assertion checker does not yet support this expression. +// Warning 8364: (170-179): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (170-188): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (170-181): Assertion checker does not support recursive structs. +// Warning 7650: (199-210): Assertion checker does not yet support this expression. +// Warning 8364: (199-208): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (221-228): Assertion checker does not yet support this expression. +// Warning 8364: (221-226): Assertion checker does not yet implement type struct C.S storage ref +// Warning 6328: (192-236): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8364: (126-135): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (153-166): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (170-181): Assertion checker does not yet support this expression. +// Warning 8364: (170-179): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (170-188): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (170-181): Assertion checker does not support recursive structs. +// Warning 7650: (199-210): Assertion checker does not yet support this expression. +// Warning 8364: (199-208): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (221-228): Assertion checker does not yet support this expression. +// Warning 8364: (221-226): Assertion checker does not yet implement type struct C.S storage ref diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_6.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_6.sol new file mode 100644 index 000000000000..c165fb30fd02 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_6.sol @@ -0,0 +1,93 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + S[] a; + } + S s1; + S s2; + function f() public { + s1.x = 10; + ++s1.x; + s1.x++; + s2.x = 20; + --s2.x; + s2.x--; + assert(s1.x == s2.x + 6); + assert(s1.a.length == s2.a.length); + delete s1; + assert(s1.x == 0); + } +} +// ---- +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (119-123): Assertion checker does not yet support this expression. +// Warning 8364: (119-121): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (119-123): Assertion checker does not support recursive structs. +// Warning 7650: (134-138): Assertion checker does not yet support this expression. +// Warning 8364: (134-136): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (134-138): Assertion checker does not support recursive structs. +// Warning 7650: (142-146): Assertion checker does not yet support this expression. +// Warning 8364: (142-144): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (142-146): Assertion checker does not support recursive structs. +// Warning 7650: (152-156): Assertion checker does not yet support this expression. +// Warning 8364: (152-154): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (152-156): Assertion checker does not support recursive structs. +// Warning 7650: (167-171): Assertion checker does not yet support this expression. +// Warning 8364: (167-169): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (167-171): Assertion checker does not support recursive structs. +// Warning 7650: (175-179): Assertion checker does not yet support this expression. +// Warning 8364: (175-177): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (175-179): Assertion checker does not support recursive structs. +// Warning 7650: (192-196): Assertion checker does not yet support this expression. +// Warning 8364: (192-194): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (200-204): Assertion checker does not yet support this expression. +// Warning 8364: (200-202): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (220-224): Assertion checker does not yet support this expression. +// Warning 8364: (220-222): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (235-239): Assertion checker does not yet support this expression. +// Warning 8364: (235-237): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (258-260): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (271-275): Assertion checker does not yet support this expression. +// Warning 8364: (271-273): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4984: (132-138): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 4984: (142-148): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 3944: (165-171): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 3944: (175-181): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 4984: (200-208): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (185-209): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (213-247): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (264-281): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8115: (81-85): Assertion checker does not yet support the type of this variable. +// Warning 8115: (88-92): Assertion checker does not yet support the type of this variable. +// Warning 7650: (119-123): Assertion checker does not yet support this expression. +// Warning 8364: (119-121): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (119-123): Assertion checker does not support recursive structs. +// Warning 7650: (134-138): Assertion checker does not yet support this expression. +// Warning 8364: (134-136): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (134-138): Assertion checker does not support recursive structs. +// Warning 7650: (142-146): Assertion checker does not yet support this expression. +// Warning 8364: (142-144): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (142-146): Assertion checker does not support recursive structs. +// Warning 7650: (152-156): Assertion checker does not yet support this expression. +// Warning 8364: (152-154): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (152-156): Assertion checker does not support recursive structs. +// Warning 7650: (167-171): Assertion checker does not yet support this expression. +// Warning 8364: (167-169): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (167-171): Assertion checker does not support recursive structs. +// Warning 7650: (175-179): Assertion checker does not yet support this expression. +// Warning 8364: (175-177): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (175-179): Assertion checker does not support recursive structs. +// Warning 7650: (192-196): Assertion checker does not yet support this expression. +// Warning 8364: (192-194): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (200-204): Assertion checker does not yet support this expression. +// Warning 8364: (200-202): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (220-224): Assertion checker does not yet support this expression. +// Warning 8364: (220-222): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (235-239): Assertion checker does not yet support this expression. +// Warning 8364: (235-237): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (258-260): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (271-275): Assertion checker does not yet support this expression. +// Warning 8364: (271-273): Assertion checker does not yet implement type struct C.S storage ref diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_indirect_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_indirect_1.sol new file mode 100644 index 000000000000..ac40ffd6c43e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_indirect_1.sol @@ -0,0 +1,41 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + T[] a; + } + struct T { + uint y; + S[] a; + } + S s1; + S s2; + function f() public view { + assert(s1.x == s2.x); + assert(s1.a.length == s2.a.length); + } +} +// ---- +// Warning 8115: (115-119): Assertion checker does not yet support the type of this variable. +// Warning 8115: (122-126): Assertion checker does not yet support the type of this variable. +// Warning 7650: (165-169): Assertion checker does not yet support this expression. +// Warning 8364: (165-167): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (173-177): Assertion checker does not yet support this expression. +// Warning 8364: (173-175): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (189-193): Assertion checker does not yet support this expression. +// Warning 8364: (189-191): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (204-208): Assertion checker does not yet support this expression. +// Warning 8364: (204-206): Assertion checker does not yet implement type struct C.S storage ref +// Warning 6328: (158-178): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (182-216): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8115: (115-119): Assertion checker does not yet support the type of this variable. +// Warning 8115: (122-126): Assertion checker does not yet support the type of this variable. +// Warning 7650: (165-169): Assertion checker does not yet support this expression. +// Warning 8364: (165-167): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (173-177): Assertion checker does not yet support this expression. +// Warning 8364: (173-175): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (189-193): Assertion checker does not yet support this expression. +// Warning 8364: (189-191): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (204-208): Assertion checker does not yet support this expression. +// Warning 8364: (204-206): Assertion checker does not yet implement type struct C.S storage ref diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_indirect_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_indirect_2.sol new file mode 100644 index 000000000000..0907a2b9bbaf --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_recursive_indirect_2.sol @@ -0,0 +1,91 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + T[] a; + } + struct T { + uint y; + S[] a; + } + S s1; + S s2; + function f() public { + s1.a.push(); + s2.a.push(); + s1.a[0].a.push(); + s2.a[0].a.push(); + assert(s1.a[0].a[0].x == s2.a[0].a[0].x); + } +} +// ---- +// Warning 8115: (115-119): Assertion checker does not yet support the type of this variable. +// Warning 8115: (122-126): Assertion checker does not yet support the type of this variable. +// Warning 7650: (153-157): Assertion checker does not yet support this expression. +// Warning 8364: (153-155): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (153-164): Assertion checker does not yet implement type struct C.T storage ref +// Warning 4375: (153-157): Assertion checker does not support recursive structs. +// Warning 7650: (168-172): Assertion checker does not yet support this expression. +// Warning 8364: (168-170): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (168-179): Assertion checker does not yet implement type struct C.T storage ref +// Warning 4375: (168-172): Assertion checker does not support recursive structs. +// Warning 7650: (183-192): Assertion checker does not yet support this expression. +// Warning 7650: (183-187): Assertion checker does not yet support this expression. +// Warning 8364: (183-185): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (183-190): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (183-199): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (183-192): Assertion checker does not support recursive structs. +// Warning 7650: (203-212): Assertion checker does not yet support this expression. +// Warning 7650: (203-207): Assertion checker does not yet support this expression. +// Warning 8364: (203-205): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (203-210): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (203-219): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (203-212): Assertion checker does not support recursive structs. +// Warning 7650: (230-244): Assertion checker does not yet support this expression. +// Warning 7650: (230-239): Assertion checker does not yet support this expression. +// Warning 7650: (230-234): Assertion checker does not yet support this expression. +// Warning 8364: (230-232): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (230-237): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (230-242): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (248-262): Assertion checker does not yet support this expression. +// Warning 7650: (248-257): Assertion checker does not yet support this expression. +// Warning 7650: (248-252): Assertion checker does not yet support this expression. +// Warning 8364: (248-250): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (248-255): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (248-260): Assertion checker does not yet implement type struct C.S storage ref +// Warning 6328: (223-263): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 8115: (115-119): Assertion checker does not yet support the type of this variable. +// Warning 8115: (122-126): Assertion checker does not yet support the type of this variable. +// Warning 7650: (153-157): Assertion checker does not yet support this expression. +// Warning 8364: (153-155): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (153-164): Assertion checker does not yet implement type struct C.T storage ref +// Warning 4375: (153-157): Assertion checker does not support recursive structs. +// Warning 7650: (168-172): Assertion checker does not yet support this expression. +// Warning 8364: (168-170): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (168-179): Assertion checker does not yet implement type struct C.T storage ref +// Warning 4375: (168-172): Assertion checker does not support recursive structs. +// Warning 7650: (183-192): Assertion checker does not yet support this expression. +// Warning 7650: (183-187): Assertion checker does not yet support this expression. +// Warning 8364: (183-185): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (183-190): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (183-199): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (183-192): Assertion checker does not support recursive structs. +// Warning 7650: (203-212): Assertion checker does not yet support this expression. +// Warning 7650: (203-207): Assertion checker does not yet support this expression. +// Warning 8364: (203-205): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (203-210): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (203-219): Assertion checker does not yet implement type struct C.S storage ref +// Warning 4375: (203-212): Assertion checker does not support recursive structs. +// Warning 7650: (230-244): Assertion checker does not yet support this expression. +// Warning 7650: (230-239): Assertion checker does not yet support this expression. +// Warning 7650: (230-234): Assertion checker does not yet support this expression. +// Warning 8364: (230-232): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (230-237): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (230-242): Assertion checker does not yet implement type struct C.S storage ref +// Warning 7650: (248-262): Assertion checker does not yet support this expression. +// Warning 7650: (248-257): Assertion checker does not yet support this expression. +// Warning 7650: (248-252): Assertion checker does not yet support this expression. +// Warning 8364: (248-250): Assertion checker does not yet implement type struct C.S storage ref +// Warning 8364: (248-255): Assertion checker does not yet implement type struct C.T storage ref +// Warning 8364: (248-260): Assertion checker does not yet implement type struct C.S storage ref diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_return.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_return.sol new file mode 100644 index 000000000000..fa5c4be86634 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_return.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + uint[] a; + } + function s() internal pure returns (S memory s1) { + s1.x = 42; + s1.a[2] = 43; + } + function f() public pure { + S memory s2 = s(); + assert(s2.x == 42); + assert(s2.a[2] == 43); + assert(s2.a[3] == 43); + } +} +// ---- +// Warning 6328: (265-286): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_state_constructor.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_state_constructor.sol new file mode 100644 index 000000000000..46237c934417 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_state_constructor.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + + S s = S(42); + + function test() view public { + assert(s.x == 42); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_state_var.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_state_var.sol new file mode 100644 index 000000000000..11fb99e339fc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_state_var.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + uint[] a; + } + S s; + function f(uint _x) public { + s.x = _x; + s.a[0] = _x; + assert(s.a[1] == s.a[0]); + } +} +// ---- +// Warning 6328: (148-172): CHC: Assertion violation happens here.\nCounterexample:\ns = {x: 7720, a: []}\n_x = 7720\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 0, a: []}\nf(7720) diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_state_var_array_pop_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_state_var_array_pop_1.sol new file mode 100644 index 000000000000..50c9ede93441 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_state_var_array_pop_1.sol @@ -0,0 +1,27 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + uint[] a; + } + S s; + function f(uint _x) public { + s.a.pop(); + s.a.length; + s.a.push(); + s.x = _x; + s.a.pop(); + s.a.push(); + s.a.push(); + s.a[0] = _x; + assert(s.a[1] == s.a[0]); + s.a.pop(); + s.a.pop(); + } +} +// ==== +// SMTIgnoreCex: yes +// ---- +// Warning 2529: (121-130): CHC: Empty array "pop" happens here. +// Warning 6328: (230-254): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_state_var_array_pop_2.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_state_var_array_pop_2.sol new file mode 100644 index 000000000000..10052f6937d2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_state_var_array_pop_2.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract C { + struct S { + uint x; + uint[] a; + } + S s; + function f(uint _x) public { + s.x = _x; + s.a.pop(); + s.a.push(); + s.a.push(); + s.a[0] = _x; + assert(s.a[1] == s.a[0]); + s.a.pop(); + s.a.pop(); + } +} +// ---- +// Warning 2529: (133-142): CHC: Empty array "pop" happens here.\nCounterexample:\ns = {x: 38, a: []}\n_x = 38\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 0, a: []}\nf(38) +// Warning 6328: (189-213): CHC: Assertion violation happens here.\nCounterexample:\ns = {x: 115792089237316195423570985008687907853269984665640564039457584007913129639897, a: [115792089237316195423570985008687907853269984665640564039457584007913129639897, 0]}\n_x = 115792089237316195423570985008687907853269984665640564039457584007913129639897\n\n\nTransaction trace:\nconstructor()\nState: s = {x: 0, a: []}\nf(115792089237316195423570985008687907853269984665640564039457584007913129639897) diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_temporary.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_temporary.sol new file mode 100644 index 000000000000..b6451dd2a995 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_temporary.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C { + + struct S { + uint x; + } + + function test() pure public { + assert(S(42).x == 42); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_unary_add.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_unary_add.sol new file mode 100644 index 000000000000..b32ac2225142 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_unary_add.sol @@ -0,0 +1,18 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct S { + uint x; + uint[] a; + } + function f(S memory s1, S memory s2) public pure { + delete s1; + s1.x++; + ++s1.x; + assert(s1.x == 2); + assert(s1.x == s2.x); + } +} +// ---- +// Warning 6328: (225-245): CHC: Assertion violation happens here.\nCounterexample:\n\ns1 = {x: 2, a: []}\ns2 = {x: 3, a: [6, 6, 6, 6, 6, 6, 6]}\n\n\nTransaction trace:\nconstructor()\nf({x: 0, a: []}, {x: 3, a: [6, 6, 6, 6, 6, 6, 6]}) diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_unary_sub.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_unary_sub.sol new file mode 100644 index 000000000000..202c4566d778 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_unary_sub.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; +pragma abicoder v2; + +contract C { + struct S { + uint x; + uint[] a; + } + function f(S memory s1, S memory s2) public pure { + delete s1; + s1.x = 100; + s1.x--; + --s1.x; + assert(s1.x == 98); + assert(s1.x == s2.x); + } +} +// ---- +// Warning 6328: (240-260): CHC: Assertion violation happens here.\nCounterexample:\n\ns1 = {x: 98, a: []}\ns2 = {x: (- 38), a: [6, 6, 6, 6, 6, 6, 6]}\n\n\nTransaction trace:\nconstructor()\nf({x: 0, a: []}, {x: (- 38), a: [6, 6, 6, 6, 6, 6, 6]}) diff --git a/test/libsolidity/smtCheckerTests/types/struct_1.sol b/test/libsolidity/smtCheckerTests/types/struct_1.sol index e58d5c18fa79..37035c8060ad 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_1.sol @@ -11,16 +11,7 @@ contract C function f(uint y, uint v) public { smap[y] = S(v); S memory smem = S(v); + assert(smap[y].x == smem.x); } } // ---- -// Warning 2072: (157-170): Unused local variable. -// Warning 8115: (157-170): Assertion checker does not yet support the type of this variable. -// Warning 8364: (139-146): Assertion checker does not yet implement type struct C.S storage ref -// Warning 8364: (149-150): Assertion checker does not yet implement type type(struct C.S storage pointer) -// Warning 8364: (149-153): Assertion checker does not yet implement type struct C.S memory -// Warning 4639: (149-153): Assertion checker does not yet implement this expression. -// Warning 8364: (139-153): Assertion checker does not yet implement type struct C.S storage ref -// Warning 8364: (173-174): Assertion checker does not yet implement type type(struct C.S storage pointer) -// Warning 8364: (173-177): Assertion checker does not yet implement type struct C.S memory -// Warning 4639: (173-177): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/struct_array_branches_1d.sol b/test/libsolidity/smtCheckerTests/types/struct_array_branches_1d.sol index 7d5752c2bbce..96f8b1b7e7e1 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_array_branches_1d.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_array_branches_1d.sol @@ -3,7 +3,7 @@ pragma experimental SMTChecker; contract C { struct S { uint[] a; } - function f(bool b) public { + function f(bool b) public pure { S memory c; c.a[0] = 0; if (b) @@ -14,21 +14,3 @@ contract C } } // ---- -// Warning 2018: (71-197): Function state mutability can be restricted to pure -// Warning 6328: (175-193): Assertion violation happens here -// Warning 8115: (101-111): Assertion checker does not yet support the type of this variable. -// Warning 7650: (115-118): Assertion checker does not yet support this expression. -// Warning 8364: (115-116): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (115-121): Assertion checker does not yet implement this expression. -// Warning 9056: (115-121): Assertion checker does not yet implement this expression. -// Warning 7650: (139-142): Assertion checker does not yet support this expression. -// Warning 8364: (139-140): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (139-145): Assertion checker does not yet implement this expression. -// Warning 9056: (139-145): Assertion checker does not yet implement this expression. -// Warning 7650: (161-164): Assertion checker does not yet support this expression. -// Warning 8364: (161-162): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (161-167): Assertion checker does not yet implement this expression. -// Warning 9056: (161-167): Assertion checker does not yet implement this expression. -// Warning 7650: (182-185): Assertion checker does not yet support this expression. -// Warning 8364: (182-183): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (182-188): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol b/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol index d5fa12ed4b22..a1ceb4e803b1 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_array_branches_2d.sol @@ -3,7 +3,7 @@ pragma experimental SMTChecker; contract C { struct S { uint[][] a; } - function f(bool b) public { + function f(bool b) public pure { S memory c; c.a[0][0] = 0; if (b) @@ -14,21 +14,3 @@ contract C } } // ---- -// Warning 2018: (73-211): Function state mutability can be restricted to pure -// Warning 6328: (186-207): Assertion violation happens here -// Warning 8115: (103-113): Assertion checker does not yet support the type of this variable. -// Warning 7650: (117-120): Assertion checker does not yet support this expression. -// Warning 8364: (117-118): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (117-123): Assertion checker does not yet implement this expression. -// Warning 9056: (117-126): Assertion checker does not yet implement this expression. -// Warning 7650: (144-147): Assertion checker does not yet support this expression. -// Warning 8364: (144-145): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (144-150): Assertion checker does not yet implement this expression. -// Warning 9056: (144-153): Assertion checker does not yet implement this expression. -// Warning 7650: (169-172): Assertion checker does not yet support this expression. -// Warning 8364: (169-170): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (169-175): Assertion checker does not yet implement this expression. -// Warning 9056: (169-178): Assertion checker does not yet implement this expression. -// Warning 7650: (193-196): Assertion checker does not yet support this expression. -// Warning 8364: (193-194): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (193-199): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol b/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol index 16b5fef4e101..d23a954633a3 100644 --- a/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/struct_array_branches_3d.sol @@ -14,20 +14,3 @@ contract C } } // ---- -// Warning 6328: (202-226): Assertion violation happens here -// Warning 8115: (110-120): Assertion checker does not yet support the type of this variable. -// Warning 7650: (124-127): Assertion checker does not yet support this expression. -// Warning 8364: (124-125): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (124-130): Assertion checker does not yet implement this expression. -// Warning 9056: (124-136): Assertion checker does not yet implement this expression. -// Warning 7650: (154-157): Assertion checker does not yet support this expression. -// Warning 8364: (154-155): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (154-160): Assertion checker does not yet implement this expression. -// Warning 9056: (154-166): Assertion checker does not yet implement this expression. -// Warning 7650: (182-185): Assertion checker does not yet support this expression. -// Warning 8364: (182-183): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (182-188): Assertion checker does not yet implement this expression. -// Warning 9056: (182-194): Assertion checker does not yet implement this expression. -// Warning 7650: (209-212): Assertion checker does not yet support this expression. -// Warning 8364: (209-210): Assertion checker does not yet implement type struct C.S memory -// Warning 9118: (209-215): Assertion checker does not yet implement this expression. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_1_chain_1.sol b/test/libsolidity/smtCheckerTests/types/tuple_1_chain_1.sol index eded54d2e30a..b4db4cfd2eeb 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_1_chain_1.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_1_chain_1.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// Warning 6838: (96-100): Condition is always true. +// Warning 6838: (96-100): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_1_chain_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_1_chain_2.sol index e35dff70666e..4ad41dd1f1b1 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_1_chain_2.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_1_chain_2.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// Warning 6838: (96-100): Condition is always true. +// Warning 6838: (96-100): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_1_chain_n.sol b/test/libsolidity/smtCheckerTests/types/tuple_1_chain_n.sol index 5166ed1b622d..85a1fe8feb5c 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_1_chain_n.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_1_chain_n.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// Warning 6838: (96-100): Condition is always true. +// Warning 6838: (96-100): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_array_pop_1.sol b/test/libsolidity/smtCheckerTests/types/tuple_array_pop_1.sol index 089dcc5c16a2..af643bbfe2fb 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_array_pop_1.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_array_pop_1.sol @@ -4,4 +4,4 @@ contract C { function f() public { (a).pop();} } // ---- -// Warning 2529: (78-87): Empty array "pop" detected here +// Warning 2529: (78-87): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_array_pop_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_array_pop_2.sol index 70e00939b926..0a940b9b0ec8 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_array_pop_2.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_array_pop_2.sol @@ -4,4 +4,4 @@ contract C { function f() public { (((((a))))).pop();} } // ---- -// Warning 2529: (78-95): Empty array "pop" detected here +// Warning 2529: (78-95): CHC: Empty array "pop" happens here.\nCounterexample:\na = []\n\n\n\nTransaction trace:\nconstructor()\nState: a = []\nf() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_assignment_array_empty.sol b/test/libsolidity/smtCheckerTests/types/tuple_assignment_array_empty.sol index cac79a17282b..d2f5e9f9eeea 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_assignment_array_empty.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_assignment_array_empty.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (136-153): Assertion violation happens here +// Warning 6328: (136-153): CHC: Assertion violation happens here.\nCounterexample:\na = []\nx = 39\ny = 0\n\n\nTransaction trace:\nconstructor()\nState: a = []\ng(39, 0) diff --git a/test/libsolidity/smtCheckerTests/types/tuple_assignment_compound.sol b/test/libsolidity/smtCheckerTests/types/tuple_assignment_compound.sol index 9620603029de..289055ee974f 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_assignment_compound.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_assignment_compound.sol @@ -10,4 +10,4 @@ contract C } } // ---- -// Warning 6328: (122-136): Assertion violation happens here +// Warning 6328: (122-136): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_assignment_empty.sol b/test/libsolidity/smtCheckerTests/types/tuple_assignment_empty.sol index fd8d3485b42d..7b8a1bb6eaa0 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_assignment_empty.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_assignment_empty.sol @@ -11,4 +11,4 @@ contract C } } // ---- -// Warning 6328: (132-146): Assertion violation happens here +// Warning 6328: (132-146): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol index 0a89c9975848..a3620964660b 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_2.sol @@ -15,4 +15,3 @@ contract C } } // ---- -// Warning 2661: (152-157): Overflow (resulting value larger than 2**256 - 1) happens here diff --git a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_empty.sol b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_empty.sol index 8c0b2f059aa1..966cc3a55a3c 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_empty.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_declarations_function_empty.sol @@ -14,4 +14,4 @@ contract C } } // ---- -// Warning 6328: (224-234): Assertion violation happens here +// Warning 6328: (224-234): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol new file mode 100644 index 000000000000..57d1a02a9fd4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_1.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns(int) { + int a; + (,, a) = ((((((1, 3, (((((2))))))))))); + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning 6321: (79-82): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (157-171): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol new file mode 100644 index 000000000000..81f403603aca --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_different_count_assignment_2.sol @@ -0,0 +1,12 @@ +pragma experimental SMTChecker; +contract C { + function f() public pure returns(int) { + int a; + ((,, a)) = ((((((1, 3, (((((2))))))))))); + assert(a == 2); + assert(a == 3); + } +} +// ---- +// Warning 6321: (79-82): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6328: (159-173): CHC: Assertion violation happens here.\nCounterexample:\n\n\n = 0\n\nTransaction trace:\nconstructor()\nf() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_1.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_1.sol index e238bd62a085..1a0f36319107 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_1.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_1.sol @@ -5,3 +5,5 @@ contract C { ((, a)) = (1, 2); } } +// ---- +// Warning 6321: (80-83): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_2.sol index 30cd2608f754..1807864e6519 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_2.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_2.sol @@ -5,3 +5,5 @@ contract C { (((, a),)) = ((1, 2), 3); } } +// ---- +// Warning 6321: (80-83): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_3.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_3.sol index 84ceb4e3666c..03ce76ea66d7 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_3.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_3.sol @@ -5,3 +5,5 @@ contract C { (((((((, a),)))))) = ((1, 2), 3); } } +// ---- +// Warning 6321: (80-83): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_4.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_4.sol index 8243e938e459..d97216f134d2 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_4.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_4.sol @@ -5,3 +5,5 @@ contract C { ((((((, a)))),)) = ((1, 2), 3); } } +// ---- +// Warning 6321: (80-83): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_5.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_5.sol index 2288a94cab60..b6ac3a5c1c34 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_5.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_5.sol @@ -5,3 +5,5 @@ contract C { ((((((((((((, a))))))),))))) = ((1, 2), 3); } } +// ---- +// Warning 6321: (80-83): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol new file mode 100644 index 000000000000..254e58f7be93 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/tuple_extra_parens_7.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C { + function g() internal pure returns (uint, uint) { + return (2, 3); + } + function f() public { + (address(1).call("")); + (uint x, uint y) = ((g())); + assert(x == 2); + assert(y == 3); + } +} +// ---- diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function.sol b/test/libsolidity/smtCheckerTests/types/tuple_function.sol index 49ed1bb94a0c..f2065845c4ea 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function.sol @@ -14,5 +14,5 @@ contract C } } // ---- -// Warning 6328: (182-196): Assertion violation happens here -// Warning 6328: (200-214): Assertion violation happens here +// Warning 6328: (182-196): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() +// Warning 6328: (200-214): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function_2.sol b/test/libsolidity/smtCheckerTests/types/tuple_function_2.sol index 13dd7b10dffc..25304ae5997f 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function_2.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function_2.sol @@ -14,4 +14,4 @@ contract C } } // ---- -// Warning 6328: (199-213): Assertion violation happens here +// Warning 6328: (199-213): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol index 5d0dc71c7891..7255bea5a5aa 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_function_3.sol @@ -16,5 +16,5 @@ contract C } } // ---- -// Warning 6328: (205-219): Assertion violation happens here -// Warning 6328: (223-237): Assertion violation happens here +// Warning 6328: (205-219): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() +// Warning 6328: (223-237): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() diff --git a/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol b/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol index 1489eb87f20b..2642c979ab66 100644 --- a/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol +++ b/test/libsolidity/smtCheckerTests/types/tuple_return_branch.sol @@ -9,16 +9,10 @@ contract C { function f(uint a) public pure { uint x; S memory y; - if (a > 100) + if (a > 100) { (x, y) = g(); + assert(y.x == 3); + } } } // ---- -// Warning 8115: (112-120): Assertion checker does not yet support the type of this variable. -// Warning 8364: (137-138): Assertion checker does not yet implement type type(struct C.S storage pointer) -// Warning 8364: (137-141): Assertion checker does not yet implement type struct C.S memory -// Warning 4639: (137-141): Assertion checker does not yet implement this expression. -// Warning 8115: (193-203): Assertion checker does not yet support the type of this variable. -// Warning 8364: (227-228): Assertion checker does not yet implement type struct C.S memory -// Warning 4639: (137-141): Assertion checker does not yet implement this expression. -// Warning 6191: (227-228): Assertion checker does not yet implement type struct C.S memory diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol index d98fc7b5c717..22f6c9c07772 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_array_2d.sol @@ -8,3 +8,5 @@ function f() public pure { int[][]; } // Warning 6133: (73-80): Statement has no effect. // Warning 8364: (73-78): Assertion checker does not yet implement type type(int256[] memory) // Warning 8364: (73-80): Assertion checker does not yet implement type type(int256[] memory[] memory) +// Warning 8364: (73-78): Assertion checker does not yet implement type type(int256[] memory) +// Warning 8364: (73-80): Assertion checker does not yet implement type type(int256[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol index 1bd3118973a0..51869fb3fd2b 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_array_3d.sol @@ -9,3 +9,6 @@ function f() public pure { int[][][]; } // Warning 8364: (73-78): Assertion checker does not yet implement type type(int256[] memory) // Warning 8364: (73-80): Assertion checker does not yet implement type type(int256[] memory[] memory) // Warning 8364: (73-82): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) +// Warning 8364: (73-78): Assertion checker does not yet implement type type(int256[] memory) +// Warning 8364: (73-80): Assertion checker does not yet implement type type(int256[] memory[] memory) +// Warning 8364: (73-82): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol index b653f8415928..b34dd2a5626c 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol @@ -9,3 +9,6 @@ function f() public pure { (int[][]); } // Warning 8364: (74-79): Assertion checker does not yet implement type type(int256[] memory) // Warning 8364: (74-81): Assertion checker does not yet implement type type(int256[] memory[] memory) // Warning 8364: (73-82): Assertion checker does not yet implement type type(int256[] memory[] memory) +// Warning 8364: (74-79): Assertion checker does not yet implement type type(int256[] memory) +// Warning 8364: (74-81): Assertion checker does not yet implement type type(int256[] memory[] memory) +// Warning 8364: (73-82): Assertion checker does not yet implement type type(int256[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol index 524b59ace1c7..dfa4a4661998 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol @@ -10,3 +10,7 @@ function f() public pure { (int[][][]); } // Warning 8364: (74-81): Assertion checker does not yet implement type type(int256[] memory[] memory) // Warning 8364: (74-83): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) // Warning 8364: (73-84): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) +// Warning 8364: (74-79): Assertion checker does not yet implement type type(int256[] memory) +// Warning 8364: (74-81): Assertion checker does not yet implement type type(int256[] memory[] memory) +// Warning 8364: (74-83): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) +// Warning 8364: (73-84): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/types/type_interfaceid.sol b/test/libsolidity/smtCheckerTests/types/type_interfaceid.sol new file mode 100644 index 000000000000..ea643bc81d46 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/type_interfaceid.sol @@ -0,0 +1,32 @@ +pragma experimental SMTChecker; + +interface I1 { +} + +interface I2 { + function f() external; +} + +interface I3 { + function f() external; + function g(uint, address) external; +} + +contract C { + function f() public pure { + assert(type(I1).interfaceId == 0); + assert(type(I2).interfaceId != 0); + assert(type(I2).interfaceId == 0x26121ff0); + assert(type(I2).interfaceId != 0); + assert(type(I3).interfaceId == 0x822b51c6); + } + function g() public pure { + assert(type(I1).interfaceId == type(I2).interfaceId); + } + function h() public pure { + assert(type(I2).interfaceId == type(I3).interfaceId); + } +} +// ---- +// Warning 6328: (449-501): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\ng() +// Warning 6328: (536-588): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nh() diff --git a/test/libsolidity/smtCheckerTests/types/type_meta_unsupported.sol b/test/libsolidity/smtCheckerTests/types/type_meta_unsupported.sol new file mode 100644 index 000000000000..2dbb2541906c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/type_meta_unsupported.sol @@ -0,0 +1,22 @@ +pragma experimental SMTChecker; + +contract A { +} + +contract C { + function f() public pure { + assert(bytes(type(C).name).length != 0); + assert(type(A).creationCode.length != 0); + assert(type(A).runtimeCode.length != 0); + } +} +// ---- +// Warning 7507: (105-117): Assertion checker does not yet support this expression. +// Warning 7507: (142-162): Assertion checker does not yet support this expression. +// Warning 7507: (186-205): Assertion checker does not yet support this expression. +// Warning 6328: (92-131): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (135-175): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 6328: (179-218): CHC: Assertion violation happens here.\nCounterexample:\n\n\n\n\nTransaction trace:\nconstructor()\nf() +// Warning 7507: (105-117): Assertion checker does not yet support this expression. +// Warning 7507: (142-162): Assertion checker does not yet support this expression. +// Warning 7507: (186-205): Assertion checker does not yet support this expression. diff --git a/test/libsolidity/smtCheckerTests/types/type_minmax.sol b/test/libsolidity/smtCheckerTests/types/type_minmax.sol new file mode 100644 index 000000000000..08d4e8af1a46 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/type_minmax.sol @@ -0,0 +1,84 @@ +pragma experimental SMTChecker; + +contract C { + function f(uint a) public pure { + assert(a <= type(uint).max); + assert(a >= type(uint).min); + require(a <= type(uint64).max); + assert(a <= type(uint64).max); + assert(a <= type(uint32).max); + } + + function int_min() public pure { + int8 int8_min = type(int8).min; + assert(int8_min == -2**7); + + int16 int16_min = type(int16).min; + assert(int16_min == -2**15); + + int24 int24_min = type(int24).min; + assert(int24_min == -2**23); + + int32 int32_min = type(int32).min; + assert(int32_min == -2**31); + + int64 int64_min = type(int64).min; + assert(int64_min == -2**63); + + int256 int256_min = type(int256).min; + assert(int256_min == -2**255); + } + + function int_max() public pure { + int8 int8_max = type(int8).max; + assert(int8_max == 2**7-1); + + int16 int16_max = type(int16).max; + assert(int16_max == 2**15-1); + + int24 int24_max = type(int24).max; + assert(int24_max == 2**23-1); + + int32 int32_max = type(int32).max; + assert(int32_max == 2**31-1); + + int256 int256_max = type(int256).max; + assert(int256_max == 2**255-1); + } + + function uint_min() public pure { + uint8 uint8_min = type(uint8).min; + assert(uint8_min == 0); + + uint16 uint16_min = type(uint16).min; + assert(uint16_min == 0); + + uint24 uint24_min = type(uint24).min; + assert(uint24_min == 0); + + uint32 uint32_min = type(uint32).min; + assert(uint32_min == 0); + + uint256 uint256_min = type(uint256).min; + assert(uint256_min == 0); + } + + function uint_max() public pure { + uint8 uint8_max = type(uint8).max; + assert(uint8_max == 2**8-1); + + uint16 uint16_max = type(uint16).max; + assert(uint16_max == 2**16-1); + + uint24 uint24_max = type(uint24).max; + assert(uint24_max == 2**24-1); + + uint32 uint32_max = type(uint32).max; + assert(uint32_max == 2**32-1); + + uint256 uint256_max = type(uint256).max; + assert(uint256_max == 2**256-1); + } +} +// ---- +// Warning 6328: (211-240): CHC: Assertion violation happens here.\nCounterexample:\n\na = 4294967296\n\n\nTransaction trace:\nconstructor()\nf(4294967296) diff --git a/test/libsolidity/smtCheckerTests/types/unused_mapping.sol b/test/libsolidity/smtCheckerTests/types/unused_mapping.sol index f12cd41de5fc..01905a36ed1b 100644 --- a/test/libsolidity/smtCheckerTests/types/unused_mapping.sol +++ b/test/libsolidity/smtCheckerTests/types/unused_mapping.sol @@ -12,6 +12,7 @@ contract C { if(x == 0) x = 0; // noop state var read x++; y++; - assert(y == x); + // Commented out because of nondeterminism in Spacer in Z3 4.8.9 + //assert(y == x); } } diff --git a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_1.sol b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_1.sol index 00060c778264..86e9e0d7cd82 100644 --- a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_1.sol +++ b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_1.sol @@ -5,5 +5,4 @@ contract C { } } // ---- -// Warning 6838: (94-100): Condition is always true. -// Warning 4588: (104-112): Assertion checker does not yet implement this type of function call. +// Warning 6838: (94-100): BMC: Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_2.sol b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_2.sol index 9d52fa7c53a6..a09d00cea21a 100644 --- a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_2.sol +++ b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_2.sol @@ -5,5 +5,4 @@ contract C { } } // ---- -// Warning 6838: (109-115): Condition is always false. -// Warning 4588: (119-127): Assertion checker does not yet implement this type of function call. +// Warning 6838: (109-115): BMC: Condition is always false. diff --git a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_3.sol b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_3.sol index ba6b7f0f6dde..709fcb1b4932 100644 --- a/test/libsolidity/smtCheckerTests/verification_target/constant_condition_3.sol +++ b/test/libsolidity/smtCheckerTests/verification_target/constant_condition_3.sol @@ -6,4 +6,3 @@ contract C { } } // ---- -// Warning 4588: (136-144): Assertion checker does not yet implement this type of function call. diff --git a/test/libsolidity/smtCheckerTests/verification_target/simple_assert.sol b/test/libsolidity/smtCheckerTests/verification_target/simple_assert.sol index 4226f85e8ee9..cb54bdf0e8ef 100644 --- a/test/libsolidity/smtCheckerTests/verification_target/simple_assert.sol +++ b/test/libsolidity/smtCheckerTests/verification_target/simple_assert.sol @@ -3,4 +3,4 @@ contract C { function f(uint a) public pure { assert(a == 2); } } // ---- -// Warning 6328: (82-96): Assertion violation happens here +// Warning 6328: (82-96): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\n\n\nTransaction trace:\nconstructor()\nf(0) diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings.sol new file mode 100644 index 000000000000..da81a3bc6682 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings.sol @@ -0,0 +1,4 @@ +pragma abicoder v2; +pragma abicoder v1; +// ---- +// SyntaxError 3845: (34-53): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol new file mode 100644 index 000000000000..855378e9ef60 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol @@ -0,0 +1,4 @@ +pragma abicoder v1; +pragma experimental ABIEncoderV2; +// ---- +// SyntaxError 8273: (20-53): ABI coder v1 has already been selected through "pragma abicoder v1". diff --git a/test/libsolidity/syntaxTests/abiEncoder/invalid_pragma_value.sol b/test/libsolidity/syntaxTests/abiEncoder/invalid_pragma_value.sol new file mode 100644 index 000000000000..cbfa03b3f0e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/invalid_pragma_value.sol @@ -0,0 +1,3 @@ +pragma abicoder something; +// ---- +// SyntaxError 2745: (0-26): Expected either "pragma abicoder v1" or "pragma abicoder v2". diff --git a/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol b/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol new file mode 100644 index 000000000000..0303bcb14900 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol @@ -0,0 +1,4 @@ +pragma experimental ABIEncoderV2; +pragma abicoder v2; +// ---- +// SyntaxError 3845: (34-53): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/select_v1.sol b/test/libsolidity/syntaxTests/abiEncoder/select_v1.sol new file mode 100644 index 000000000000..ecae09ad6d7c --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/select_v1.sol @@ -0,0 +1 @@ +pragma abicoder v1; \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/abiEncoder/selected_twice.sol b/test/libsolidity/syntaxTests/abiEncoder/selected_twice.sol new file mode 100644 index 000000000000..424c2c7f2b89 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/selected_twice.sol @@ -0,0 +1,4 @@ +pragma abicoder v1; +pragma abicoder v1; +// ---- +// SyntaxError 3845: (20-39): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol b/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol new file mode 100644 index 000000000000..a0d0cf2d814a --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol @@ -0,0 +1,2 @@ +pragma abicoder v2; +pragma experimental ABIEncoderV2; \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v1_type.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v1_type.sol new file mode 100644 index 000000000000..f324d2c33e30 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v1_type.sol @@ -0,0 +1,16 @@ +pragma abicoder v1; +struct Item { + uint x; + uint y; +} + +contract D { + Item[][][] public items; + + function test() public view returns (uint) { + // The autogenerated getters to not use ABI encoder. + (uint a, uint b) = this.items(1, 2, 3); + return a + b; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v2_type.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v2_type.sol new file mode 100644 index 000000000000..feaf895e17ee --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v2_type.sol @@ -0,0 +1,18 @@ +pragma abicoder v1; +struct Item { + uint x; + uint y; +} + +contract D { + Item[][][] public items; + + function test() public view returns (uint) { + // The autogenerated getters to not use ABI encoder. + Item memory item = this.items(1, 2, 3); + return item.x + item.y; + } +} +// ---- +// TypeError 7364: (222-260): Different number of components on the left hand side (1) than on the right hand side (2). +// TypeError 9574: (222-260): Type uint256 is not implicitly convertible to expected type struct Item memory. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v1_library_function_accepting_storage_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v1_library_function_accepting_storage_struct.sol new file mode 100644 index 000000000000..b21b7cfaff5e --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v1_library_function_accepting_storage_struct.sol @@ -0,0 +1,22 @@ +==== Source: A ==== +pragma abicoder v2; + +library L { + struct Item { + uint x; + } + + function set(Item storage _item) external view {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + L.Item item; + + function foo() public view { + L.set(item); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_constructor_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_constructor_accepting_struct.sol new file mode 100644 index 000000000000..8ba00da1a566 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_constructor_accepting_struct.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + struct Item { + uint x; + } + + constructor(Item memory _item) {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + function foo() public { + new C(C.Item(5)); + } +} +// ---- +// TypeError 2443: (B:91-100): The type of this parameter, struct C.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_accepting_struct_via_named_argument.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_accepting_struct_via_named_argument.sol new file mode 100644 index 000000000000..417f5682f0c8 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_accepting_struct_via_named_argument.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + struct Item { + uint x; + } + + function set(uint _x, string memory _y, Item memory _item, bool _z) external view {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + function foo() public view { + C(address(0x00)).set({_item: C.Item(50), _z: false, _y: "abc", _x: 30}); + } +} +// ---- +// TypeError 2443: (B:119-129): The type of this parameter, struct C.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_pointer_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_pointer_accepting_struct.sol new file mode 100644 index 000000000000..1b08439cc1ca --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_pointer_accepting_struct.sol @@ -0,0 +1,23 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + struct Item { + uint x; + } + + function get(Item memory _item) external {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + function foo() public { + C c = new C(); + function(C.Item memory) external ptr = c.get; + ptr(C.Item(5)); + } +} +// ---- +// TypeError 2443: (B:166-175): The type of this parameter, struct C.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_dynamic_string_array.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_dynamic_string_array.sol new file mode 100644 index 000000000000..baadf362ea83 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_dynamic_string_array.sol @@ -0,0 +1,17 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + function f() external view returns (string[] memory) {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract D { + function g() public view { + C(address(0x00)).f(); + } +} +// ---- +// TypeError 2428: (B:85-105): The type of return parameter 1, string[], is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct.sol new file mode 100644 index 000000000000..f84a13d49df5 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + struct Item { + uint x; + } + + function get() external view returns(Item memory) {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + function foo() public view { + C(address(0x00)).get(); + } +} +// ---- +// TypeError 2428: (B:90-112): The type of return parameter 1, struct C.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct_with_dynamic_array.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct_with_dynamic_array.sol new file mode 100644 index 000000000000..a67394e0eb52 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct_with_dynamic_array.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + struct Item { + uint[] y; + } + + function get() external view returns(Item memory) {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + function foo() public view { + C(address(0x00)).get(); + } +} +// ---- +// TypeError 2428: (B:90-112): The type of return parameter 1, struct C.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_event_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_event_accepting_struct.sol new file mode 100644 index 000000000000..1269a29eec5d --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_event_accepting_struct.sol @@ -0,0 +1,20 @@ +==== Source: A ==== +pragma abicoder v2; + +library L { + struct Item { + uint x; + } + event E(Item _value); +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + function foo() public { + emit L.E(L.Item(42)); + } +} +// ---- +// TypeError 2443: (B:94-104): The type of this parameter, struct L.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_bound_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_bound_function_returning_struct.sol new file mode 100644 index 000000000000..149aefa96e8d --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_bound_function_returning_struct.sol @@ -0,0 +1,23 @@ +==== Source: A ==== +pragma abicoder v2; + +library L { + struct Item { + uint x; + } + + function f(uint) external view returns (Item memory) {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract D { + using L for uint; + + function test() public { + uint(1).f(); + } +} +// ---- +// TypeError 2428: (B:106-117): The type of return parameter 1, struct L.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_accepting_storage_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_accepting_storage_struct.sol new file mode 100644 index 000000000000..41bc072a5949 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_accepting_storage_struct.sol @@ -0,0 +1,22 @@ +==== Source: A ==== +pragma abicoder v2; + +library L { + struct Item { + uint x; + } + + function get(Item storage _item) external view {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + L.Item item; + + function foo() public view { + L.get(item); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_returning_struct.sol new file mode 100644 index 000000000000..b3d649e2c805 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_returning_struct.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +pragma abicoder v2; + +library L { + struct Item { + uint x; + } + + function get() external view returns(Item memory) {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract Test { + function foo() public view { + L.get(); + } +} +// ---- +// TypeError 2428: (B:90-97): The type of return parameter 1, struct L.Item, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol new file mode 100644 index 000000000000..78d7b0512414 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol @@ -0,0 +1,28 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + modifier validate() { + A(address(0x00)).get(); + _; + } +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract C is B { + function foo() + public + validate() + {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol new file mode 100644 index 000000000000..1ac5d713b104 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol @@ -0,0 +1,35 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + constructor() validate { + A(address(0x00)).get(); + } + + modifier validate() { + A(address(0x00)).get(); + _; + } +} + +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract C is B {} +==== Source: C ==== +pragma abicoder v1; +import "B"; + +contract D is C { + constructor() validate B() validate C() validate {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol new file mode 100644 index 000000000000..073a125be984 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol @@ -0,0 +1,26 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + constructor() { + A(address(0x00)).get(); + } + + function foo() public view { + A(address(0x00)).get(); + } +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract C is B {} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_event.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_event.sol new file mode 100644 index 000000000000..963ed33c70e6 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_event.sol @@ -0,0 +1,16 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Item { + uint x; +} + +contract C { + event Ev(Item); +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract D is C {} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_accepting_struct.sol new file mode 100644 index 000000000000..c37bad41c274 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_accepting_struct.sol @@ -0,0 +1,17 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + struct Item { + uint x; + } + + function get(Item memory) external view {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract D is C {} +// ---- +// TypeError 6594: (B:33-51): Contract "D" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_returning_struct.sol new file mode 100644 index 000000000000..f25754246930 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_returning_struct.sol @@ -0,0 +1,17 @@ +==== Source: A ==== +pragma abicoder v2; + +contract C { + struct Item { + uint x; + } + + function get() external view returns(Item memory) {} +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract D is C {} +// ---- +// TypeError 6594: (B:33-51): Contract "D" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol new file mode 100644 index 000000000000..8aa2f8552c2b --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol @@ -0,0 +1,22 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Item { + uint x; +} + +library L { + event Ev(Item); +} + +contract C { + function foo() public { + emit L.Ev(Item(1)); + } +} +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract D is C {} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol new file mode 100644 index 000000000000..dd102cf51378 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol @@ -0,0 +1,30 @@ +==== Source: A ==== +pragma abicoder v2; + +struct Data { + bool flag; +} + +contract A { + function get() public view returns (Data memory) {} +} + +contract B { + modifier validate() virtual { + A(address(0x00)).get(); + _; + } +} + +==== Source: B ==== +pragma abicoder v1; +import "A"; + +contract C is B { + function foo() public pure validate {} + + modifier validate() override { + _; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol new file mode 100644 index 000000000000..e5df9cc82237 --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol @@ -0,0 +1,55 @@ +==== Source: C ==== +pragma abicoder v1; +import "X"; +import "V1A"; +import "V2A"; +import "V1B"; + +contract C is V1A, V2A, V1B { + function foo() + public + modV1A + modV2A // There should be no error for modV2A (it uses ABIEncoderV2) + modV1B + { + } +} +==== Source: V1A ==== +pragma abicoder v1; +import "X"; + +contract V1A { + modifier modV1A() { + _; + } +} +==== Source: V1B ==== +pragma abicoder v1; +import "X"; + +contract V1B { + modifier modV1B() { + _; + } +} +==== Source: V2A ==== +pragma abicoder v2; +import "X"; + +contract V2A { + modifier modV2A() { + X(address(0x00)).get(); + _; + } +} +==== Source: X ==== +pragma abicoder v2; + +struct Data { + bool flag; +} + +contract X { + function get() public view returns (Data memory) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/abiEncoder/v2_accessing_returned_dynamic_array_with_returndata_support.sol b/test/libsolidity/syntaxTests/abiEncoder/v2_accessing_returned_dynamic_array_with_returndata_support.sol new file mode 100644 index 000000000000..bafff148d34a --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v2_accessing_returned_dynamic_array_with_returndata_support.sol @@ -0,0 +1,14 @@ +pragma abicoder v2; + +contract C { + function get() public view returns (uint[][] memory) {} + + function test() public view returns (bool) { + uint[][] memory x = this.get(); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// Warning 6321: (150-154): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 2072: (166-183): Unused local variable. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v2_accessing_returned_dynamic_array_without_returndata_support.sol b/test/libsolidity/syntaxTests/abiEncoder/v2_accessing_returned_dynamic_array_without_returndata_support.sol new file mode 100644 index 000000000000..7e97b98da16e --- /dev/null +++ b/test/libsolidity/syntaxTests/abiEncoder/v2_accessing_returned_dynamic_array_without_returndata_support.sol @@ -0,0 +1,13 @@ +pragma abicoder v2; + +contract C { + function get() public view returns (uint[][] memory) {} + + function test() public view returns (bool) { + uint[][] memory x = this.get(); + } +} +// ==== +// EVMVersion: uint)[] memory x) public pure {} diff --git a/test/libsolidity/syntaxTests/array/function_mapping_library.sol b/test/libsolidity/syntaxTests/array/function_mapping_library.sol index fb709292a836..f495362c9f44 100644 --- a/test/libsolidity/syntaxTests/array/function_mapping_library.sol +++ b/test/libsolidity/syntaxTests/array/function_mapping_library.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; library L { function f(mapping(uint => uint)[2] memory a) external pure returns (mapping(uint => uint)[2] memory) {} } diff --git a/test/libsolidity/syntaxTests/array/invalid/contract_index_access.sol b/test/libsolidity/syntaxTests/array/invalid/contract_index_access.sol deleted file mode 100644 index efa8679be205..000000000000 --- a/test/libsolidity/syntaxTests/array/invalid/contract_index_access.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract C { - function f() view public { - C[0]; - } -} -// ---- -// TypeError 2876: (52-56): Index access for contracts or libraries is not possible. diff --git a/test/libsolidity/syntaxTests/array/invalid/library_array.sol b/test/libsolidity/syntaxTests/array/invalid/library_array.sol new file mode 100644 index 000000000000..020ea92d156c --- /dev/null +++ b/test/libsolidity/syntaxTests/array/invalid/library_array.sol @@ -0,0 +1,8 @@ +library L {} +contract C { + function f() public { + L[] memory x; + } +} +// ---- +// TypeError 1130: (51-52): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/array/invalid/library_index_access.sol b/test/libsolidity/syntaxTests/array/invalid/library_index_access.sol index d81e36bab255..885a114b02bc 100644 --- a/test/libsolidity/syntaxTests/array/invalid/library_index_access.sol +++ b/test/libsolidity/syntaxTests/array/invalid/library_index_access.sol @@ -4,4 +4,4 @@ library C { } } // ---- -// TypeError 2876: (51-55): Index access for contracts or libraries is not possible. +// TypeError 2876: (51-55): Index access for library types and arrays of libraries are not possible. diff --git a/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_different_base.sol b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_different_base.sol new file mode 100644 index 000000000000..d45a77182b72 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_different_base.sol @@ -0,0 +1,9 @@ +contract C { + bytes1[32] data1; + bytes2[10] data2; + function f() external { + data1 = data2; + } +} +// ---- +// TypeError 7407: (101-106): Type bytes2[10] storage ref is not implicitly convertible to expected type bytes1[32] storage ref. diff --git a/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_different_base_2.sol b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_different_base_2.sol new file mode 100644 index 000000000000..dabeb18e0f47 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_different_base_2.sol @@ -0,0 +1,9 @@ +contract C { + uint64[32] data1; + uint256[10] data2; + function f() external { + data1 = data2; + } +} +// ---- +// TypeError 7407: (102-107): Type uint256[10] storage ref is not implicitly convertible to expected type uint64[32] storage ref. diff --git a/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_dynamic_to_static.sol b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_dynamic_to_static.sol new file mode 100644 index 000000000000..1b3733a0b453 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_dynamic_to_static.sol @@ -0,0 +1,9 @@ +contract C { + uint256[10] data1; + uint256[] data2; + function f() external { + data1 = data2; + } +} +// ---- +// TypeError 7407: (101-106): Type uint256[] storage ref is not implicitly convertible to expected type uint256[10] storage ref. diff --git a/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_static_longer_to_shorter.sol b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_static_longer_to_shorter.sol new file mode 100644 index 000000000000..95185d317af7 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/invalidCopy/storage_to_storage_static_longer_to_shorter.sol @@ -0,0 +1,9 @@ +contract C { + uint256[10] data1; + uint256[18] data2; + function f() external { + data1 = data2; + } +} +// ---- +// TypeError 7407: (103-108): Type uint256[18] storage ref is not implicitly convertible to expected type uint256[10] storage ref. diff --git a/test/libsolidity/syntaxTests/array/length/abi_decode_length_too_large.sol b/test/libsolidity/syntaxTests/array/length/abi_decode_length_too_large.sol index d68ec2d42c7b..06caa0f22947 100644 --- a/test/libsolidity/syntaxTests/array/length/abi_decode_length_too_large.sol +++ b/test/libsolidity/syntaxTests/array/length/abi_decode_length_too_large.sol @@ -1,8 +1,8 @@ // Used to cause ICE contract C { function f() public { - abi.decode("", (byte[999999999])); + abi.decode("", (bytes1[999999999])); } } // ---- -// TypeError 6118: (75-90): Type too large for memory. +// TypeError 6118: (75-92): Type too large for memory. diff --git a/test/libsolidity/syntaxTests/array/length/array_length_cannot_be_constant_function_parameter.sol b/test/libsolidity/syntaxTests/array/length/array_length_cannot_be_constant_function_parameter.sol index c63967c38292..cc979668ccab 100644 --- a/test/libsolidity/syntaxTests/array/length/array_length_cannot_be_constant_function_parameter.sol +++ b/test/libsolidity/syntaxTests/array/length/array_length_cannot_be_constant_function_parameter.sol @@ -4,6 +4,6 @@ contract C { } } // ---- -// DeclarationError 1788: (28-45): The "constant" keyword can only be used for state variables. +// DeclarationError 1788: (28-45): The "constant" keyword can only be used for state variables or variables at file level. // TypeError 5462: (69-72): Invalid array length, expected integer literal or constant expression. // TypeError 6651: (64-75): Data location must be "storage", "memory" or "calldata" for variable, but none was given. diff --git a/test/libsolidity/syntaxTests/array/length/complex_cyclic_constant.sol b/test/libsolidity/syntaxTests/array/length/complex_cyclic_constant.sol index 38018165ce05..0710124a100e 100644 --- a/test/libsolidity/syntaxTests/array/length/complex_cyclic_constant.sol +++ b/test/libsolidity/syntaxTests/array/length/complex_cyclic_constant.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// TypeError 5210: (36-39): Cyclic constant definition (or maximum recursion depth exhausted). +// TypeError 5210: (17-44): Cyclic constant definition (or maximum recursion depth exhausted). diff --git a/test/libsolidity/syntaxTests/array/length/const_cannot_be_fractional.sol b/test/libsolidity/syntaxTests/array/length/const_cannot_be_fractional.sol index ca200cdd09e5..2145d7025511 100644 --- a/test/libsolidity/syntaxTests/array/length/const_cannot_be_fractional.sol +++ b/test/libsolidity/syntaxTests/array/length/const_cannot_be_fractional.sol @@ -3,4 +3,4 @@ contract C { uint[L] ids; } // ---- -// TypeError 3208: (51-52): Array with fractional length specified. +// TypeError 5462: (51-52): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/cyclic_constant.sol b/test/libsolidity/syntaxTests/array/length/cyclic_constant.sol index 08a5f1cf904f..dd089e4d0180 100644 --- a/test/libsolidity/syntaxTests/array/length/cyclic_constant.sol +++ b/test/libsolidity/syntaxTests/array/length/cyclic_constant.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// TypeError 5210: (37-40): Cyclic constant definition (or maximum recursion depth exhausted). +// TypeError 5210: (17-40): Cyclic constant definition (or maximum recursion depth exhausted). diff --git a/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol b/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol index 0b5ceafab64e..6a913d1e3a3f 100644 --- a/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol +++ b/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol @@ -1,7 +1,7 @@ contract C { function a() public pure returns(int[0][500] memory) {} function b() public pure returns(uint[0][500] memory) {} - function c() public pure returns(byte[0][500] memory) {} + function c() public pure returns(bytes1[0][500] memory) {} function d() public pure returns(bytes32[0][500] memory) {} function e() public pure returns(bytes[0][500] memory) {} function e() public pure returns(string[0][500] memory) {} @@ -9,7 +9,7 @@ contract C { // ---- // TypeError 1406: (52-53): Array with zero length specified. // TypeError 1406: (111-112): Array with zero length specified. -// TypeError 1406: (170-171): Array with zero length specified. -// TypeError 1406: (232-233): Array with zero length specified. -// TypeError 1406: (292-293): Array with zero length specified. -// TypeError 1406: (353-354): Array with zero length specified. +// TypeError 1406: (172-173): Array with zero length specified. +// TypeError 1406: (234-235): Array with zero length specified. +// TypeError 1406: (294-295): Array with zero length specified. +// TypeError 1406: (355-356): Array with zero length specified. diff --git a/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol b/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol index cd7b8e47ae66..d268d75dd799 100644 --- a/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol +++ b/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol @@ -1,7 +1,7 @@ contract C { int[0] a; uint[0] b; - byte[0] c; + bytes1[0] c; bytes32[0] d; bytes[0] e; string[0] f; @@ -9,7 +9,7 @@ contract C { // ---- // TypeError 1406: (19-20): Array with zero length specified. // TypeError 1406: (32-33): Array with zero length specified. -// TypeError 1406: (45-46): Array with zero length specified. -// TypeError 1406: (61-62): Array with zero length specified. -// TypeError 1406: (75-76): Array with zero length specified. -// TypeError 1406: (90-91): Array with zero length specified. +// TypeError 1406: (47-48): Array with zero length specified. +// TypeError 1406: (63-64): Array with zero length specified. +// TypeError 1406: (77-78): Array with zero length specified. +// TypeError 1406: (92-93): Array with zero length specified. diff --git a/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol index a837d1b5f0a0..7fd7b531b40f 100644 --- a/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol +++ b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(bytes32[1263941234127518272][500] memory) public pure {} diff --git a/test/libsolidity/syntaxTests/array/library_array.sol b/test/libsolidity/syntaxTests/array/library_array.sol new file mode 100644 index 000000000000..fec5762f8aab --- /dev/null +++ b/test/libsolidity/syntaxTests/array/library_array.sol @@ -0,0 +1,8 @@ +library L {} +contract C { + function f() public pure { + new L[](2); + } +} +// ---- +// TypeError 1130: (63-64): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_memory.sol b/test/libsolidity/syntaxTests/array/nested_calldata_memory.sol index b8361e115613..fb2ab2d94609 100644 --- a/test/libsolidity/syntaxTests/array/nested_calldata_memory.sol +++ b/test/libsolidity/syntaxTests/array/nested_calldata_memory.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct shouldBug { @@ -10,4 +10,3 @@ contract Test { } // ---- -// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_memory2.sol b/test/libsolidity/syntaxTests/array/nested_calldata_memory2.sol index c886c8eee913..ad09877c4ec1 100644 --- a/test/libsolidity/syntaxTests/array/nested_calldata_memory2.sol +++ b/test/libsolidity/syntaxTests/array/nested_calldata_memory2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct shouldBug { @@ -10,4 +10,3 @@ contract Test { } // ---- -// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_memory3.sol b/test/libsolidity/syntaxTests/array/nested_calldata_memory3.sol index d8716d18ebd3..d7f0133fcb23 100644 --- a/test/libsolidity/syntaxTests/array/nested_calldata_memory3.sol +++ b/test/libsolidity/syntaxTests/array/nested_calldata_memory3.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct shouldBug { @@ -10,4 +10,3 @@ contract Test { } // ---- -// UnimplementedFeatureError: Copying nested dynamic calldata arrays to memory is not implemented in the old code generator. diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_storage.sol b/test/libsolidity/syntaxTests/array/nested_calldata_storage.sol index bae1033216d8..e9878fc223e0 100644 --- a/test/libsolidity/syntaxTests/array/nested_calldata_storage.sol +++ b/test/libsolidity/syntaxTests/array/nested_calldata_storage.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { uint[][2] tmp_i; diff --git a/test/libsolidity/syntaxTests/array/nested_calldata_storage2.sol b/test/libsolidity/syntaxTests/array/nested_calldata_storage2.sol index d922d717ffce..2d1c1f5866b1 100644 --- a/test/libsolidity/syntaxTests/array/nested_calldata_storage2.sol +++ b/test/libsolidity/syntaxTests/array/nested_calldata_storage2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { uint[][] tmp_i; diff --git a/test/libsolidity/syntaxTests/array/pop/storage_with_mapping_pop.sol b/test/libsolidity/syntaxTests/array/pop/storage_with_mapping_pop.sol new file mode 100644 index 000000000000..235ce3ba1934 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/pop/storage_with_mapping_pop.sol @@ -0,0 +1,7 @@ +contract C { + mapping(uint=>uint)[] array; + mapping(uint=>uint) map; + function f() public { + array.pop(); + } +} diff --git a/test/libsolidity/syntaxTests/array/push/storage_with_mapping_push.sol b/test/libsolidity/syntaxTests/array/push/storage_with_mapping_push.sol new file mode 100644 index 000000000000..6032b0c2a4f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/push/storage_with_mapping_push.sol @@ -0,0 +1,10 @@ +contract C { + mapping(uint=>uint)[] array; + mapping(uint=>uint) map; + function f() public { + array.push(); + array.push(map); + } +} +// ---- +// TypeError 8871: (131-141): Storage arrays with nested mappings do not support .push(). diff --git a/test/libsolidity/syntaxTests/array/slice/assign_to_storage.sol b/test/libsolidity/syntaxTests/array/slice/assign_to_storage.sol index 12fd827105ca..df259b2940c1 100644 --- a/test/libsolidity/syntaxTests/array/slice/assign_to_storage.sol +++ b/test/libsolidity/syntaxTests/array/slice/assign_to_storage.sol @@ -5,4 +5,3 @@ contract c { } } // ---- -// TypeError 7407: (63-74): Type bytes calldata slice is not implicitly convertible to expected type bytes storage ref. diff --git a/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_convert_to_memory.sol b/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_convert_to_memory.sol index 76a36075488f..62cc33c6592d 100644 --- a/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_convert_to_memory.sol +++ b/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_convert_to_memory.sol @@ -1,7 +1,5 @@ contract C { - function f(bytes calldata x) external { - bytes memory y = x[1:2]; + function f(bytes calldata x) external pure returns (bytes memory) { + return x[1:2]; } -} -// ---- -// TypeError 9574: (65-88): Type bytes calldata slice is not implicitly convertible to expected type bytes memory. +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_forward.sol b/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_forward.sol index ccb70bbbde40..75c42cd4e53c 100644 --- a/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_forward.sol +++ b/test/libsolidity/syntaxTests/array/slice/calldata_dynamic_forward.sol @@ -4,4 +4,3 @@ contract C { } } // ---- -// TypeError 9553: (79-85): Invalid type for argument in function call. Invalid implicit conversion from bytes calldata slice to bytes memory requested. diff --git a/test/libsolidity/syntaxTests/bound/interface_using_for.sol b/test/libsolidity/syntaxTests/bound/interface_using_for.sol new file mode 100644 index 000000000000..727ff2c46ef4 --- /dev/null +++ b/test/libsolidity/syntaxTests/bound/interface_using_for.sol @@ -0,0 +1,10 @@ +library L { + function f() public {} +} + +interface I { + using L for int; + function g() external; +} +// ---- +// TypeError 9088: (60-76): The "using for" directive is not allowed inside interfaces. diff --git a/test/libsolidity/syntaxTests/bound/using_for_library.sol b/test/libsolidity/syntaxTests/bound/using_for_library.sol new file mode 100644 index 000000000000..8166e9c7674d --- /dev/null +++ b/test/libsolidity/syntaxTests/bound/using_for_library.sol @@ -0,0 +1,11 @@ +library L {} +library M {} +contract C { + using L for M; + using M for L; + using L for L; +} +// ---- +// TypeError 1130: (55-56): Invalid use of a library name. +// TypeError 1130: (74-75): Invalid use of a library name. +// TypeError 1130: (93-94): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_1.sol b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_1.sol new file mode 100644 index 000000000000..2a731411a1bb --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_1.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF + /*underflow ‬*/ + } +} +// ---- +// ParserError 8936: (71-83): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_2.sol b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_2.sol new file mode 100644 index 000000000000..a8576fb06a85 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_2.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF PDF + /*underflow ‬‬*/ + } +} +// ---- +// ParserError 8936: (75-87): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_3.sol b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_3.sol new file mode 100644 index 000000000000..b019702261d8 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_3.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO + /*overflow ‮*/ + } +} +// ---- +// ParserError 8936: (71-86): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_4.sol b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_4.sol new file mode 100644 index 000000000000..45f14cd01249 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_4.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO RLO + /*overflow ‮‮*/ + } +} +// ---- +// ParserError 8936: (75-93): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_5.sol b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_5.sol new file mode 100644 index 000000000000..46d201772015 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_5.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure + { + // RLO PDF + /*ok ‮‬*/ + + // RLO RLO PDF PDF + /*ok ‮‮‬‬*/ + + // RLO RLO RLO PDF PDF PDF + /*ok ‮‮‮‬‬‬*/ + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_6.sol b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_6.sol new file mode 100644 index 000000000000..5ad89117c1da --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_6.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF RLO + /*overflow ‬‮*/ + } +} +// ---- +// ParserError 8936: (75-86): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_7.sol b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_7.sol new file mode 100644 index 000000000000..80f40a97f0ed --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/multiline_unicode_direction_override_7.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + /* LRO‭ LRE‪ RLE ‫ PDF‬ RLO‮ PDF ‬ PDF‬ + } +} +// ---- +// ParserError 8936: (52-115): Expected multi-line comment-terminator. diff --git a/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_1.sol b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_1.sol new file mode 100644 index 000000000000..16eaaeddb8f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_1.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF + // underflow ‬ + } +} +// ---- +// ParserError 8936: (71-84): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_2.sol b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_2.sol new file mode 100644 index 000000000000..91f544fb6426 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_2.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF PDF + // underflow ‬‬ + } +} +// ---- +// ParserError 8936: (75-88): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_3.sol b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_3.sol new file mode 100644 index 000000000000..bc9b0cff5398 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_3.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO + // overflow ‮ + } +} +// ---- +// ParserError 8936: (71-86): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_4.sol b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_4.sol new file mode 100644 index 000000000000..907f213bd685 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_4.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO RLO + // overflow ‮‮ + } +} +// ---- +// ParserError 8936: (75-93): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_5.sol b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_5.sol new file mode 100644 index 000000000000..0c13f7fec713 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_5.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure + { + // RLO PDF + // ok ‮‬ + + // RLO RLO PDF PDF + // ok ‮‮‬‬ + + // RLO RLO RLO PDF PDF PDF + // ok ‮‮‮‬‬‬ + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_6.sol b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_6.sol new file mode 100644 index 000000000000..2040d6fb22ac --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/singleline_unicode_direction_override_6.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF RLO + // underflow ‬‮ + } +} +// ---- +// ParserError 8936: (75-88): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/comments/unicode_direction_in_source_1.sol b/test/libsolidity/syntaxTests/comments/unicode_direction_in_source_1.sol new file mode 100644 index 000000000000..f8abd48bfe57 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/unicode_direction_in_source_1.sol @@ -0,0 +1,8 @@ +contract C { + function f(bool b) public pure + { + if ‬(b) { return; } + } +} +// ---- +// ParserError 2314: (65-66): Expected '(' but got 'ILLEGAL' diff --git a/test/libsolidity/syntaxTests/comments/unicode_direction_in_source_2.sol b/test/libsolidity/syntaxTests/comments/unicode_direction_in_source_2.sol new file mode 100644 index 000000000000..464b62729051 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/unicode_direction_in_source_2.sol @@ -0,0 +1,8 @@ +contract C { + function f(bool b) public pure + { + uint a = 10; ‬ + } +} +// ---- +// ParserError 8936: (75-76): Invalid token. diff --git a/test/libsolidity/syntaxTests/comments/unicode_direction_override_1.sol b/test/libsolidity/syntaxTests/comments/unicode_direction_override_1.sol new file mode 100644 index 000000000000..ec3f1052d297 --- /dev/null +++ b/test/libsolidity/syntaxTests/comments/unicode_direction_override_1.sol @@ -0,0 +1,10 @@ +contract TimelockUpgrade { + function confirmUpgrade() external { + uint256 m; + uint256 d; + (/*year*/,/*month‮*/,d/*yad*/,m/*‬‬hour*/,/*minute*/,/*second*/) = BokkyDateTime.timestampToDateTime(block.timestamp); + } +} + +// ---- +// ParserError 8936: (124-135): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/overflow.sol b/test/libsolidity/syntaxTests/constantEvaluator/overflow.sol new file mode 100644 index 000000000000..55bd279019b7 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/overflow.sol @@ -0,0 +1,9 @@ +contract C { + uint8 constant a = 255; + uint16 constant b = a + 2; + function f() public pure { + uint[b] memory x; + } +} +// ---- +// TypeError 2643: (65-70): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/type_reference.sol b/test/libsolidity/syntaxTests/constantEvaluator/type_reference.sol new file mode 100644 index 000000000000..585c005f6229 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/type_reference.sol @@ -0,0 +1,3 @@ +int[L] constant L = 6; +// ---- +// TypeError 5462: (4-5): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/type_reference_in_contract.sol b/test/libsolidity/syntaxTests/constantEvaluator/type_reference_in_contract.sol new file mode 100644 index 000000000000..9073f6ac16eb --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/type_reference_in_contract.sol @@ -0,0 +1,5 @@ +contract C { + int[L] constant L = 6; +} +// ---- +// TypeError 5462: (21-22): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/unary_fine.sol b/test/libsolidity/syntaxTests/constantEvaluator/unary_fine.sol new file mode 100644 index 000000000000..f36c45c0dd32 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/unary_fine.sol @@ -0,0 +1,8 @@ +contract C { + int8 constant a = -7; + function f() public pure { + uint[-a] memory x; + x[0] = 2; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/constantEvaluator/underflow.sol b/test/libsolidity/syntaxTests/constantEvaluator/underflow.sol new file mode 100644 index 000000000000..6ca25911ad2d --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/underflow.sol @@ -0,0 +1,8 @@ +contract C { + uint8 constant a = 0; + function f() public pure { + uint[a - 1] memory x; + } +} +// ---- +// TypeError 2643: (83-88): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/underflow_unary.sol b/test/libsolidity/syntaxTests/constantEvaluator/underflow_unary.sol new file mode 100644 index 000000000000..5887ab19d0bb --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/underflow_unary.sol @@ -0,0 +1,8 @@ +contract C { + int8 constant a = -128; + function f() public pure { + uint[-a] memory x; + } +} +// ---- +// TypeError 3667: (85-87): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/constants/constant_natspec.sol b/test/libsolidity/syntaxTests/constants/constant_natspec.sol new file mode 100644 index 000000000000..31e5dbdaa57c --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/constant_natspec.sol @@ -0,0 +1,3 @@ +/// @dev Documentation +uint constant x = 8; +// ---- diff --git a/test/libsolidity/syntaxTests/constants/constant_natspec_user.sol b/test/libsolidity/syntaxTests/constants/constant_natspec_user.sol new file mode 100644 index 000000000000..feb952860f4b --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/constant_natspec_user.sol @@ -0,0 +1,4 @@ +/// Documentation +uint constant x = 8; +// ---- +// DocstringParsingError 6546: (0-18): Documentation tag @notice not valid for file-level variables. diff --git a/test/libsolidity/syntaxTests/constants/constant_override.sol b/test/libsolidity/syntaxTests/constants/constant_override.sol new file mode 100644 index 000000000000..3aa974012e50 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/constant_override.sol @@ -0,0 +1,3 @@ +uint constant override x = 2; +// ---- +// ParserError 2314: (14-22): Expected identifier but got 'override' diff --git a/test/libsolidity/syntaxTests/constants/constant_unassigned.sol b/test/libsolidity/syntaxTests/constants/constant_unassigned.sol new file mode 100644 index 000000000000..8ee727ed7ffd --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/constant_unassigned.sol @@ -0,0 +1,3 @@ +uint constant x; +// ---- +// TypeError 4266: (0-15): Uninitialized "constant" variable. diff --git a/test/libsolidity/syntaxTests/constants/constant_virtual.sol b/test/libsolidity/syntaxTests/constants/constant_virtual.sol new file mode 100644 index 000000000000..e6f885cdfc07 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/constant_virtual.sol @@ -0,0 +1,3 @@ +uint constant virtual x; +// ---- +// ParserError 2314: (14-21): Expected identifier but got 'virtual' diff --git a/test/libsolidity/syntaxTests/constants/constant_with_visibility.sol b/test/libsolidity/syntaxTests/constants/constant_with_visibility.sol new file mode 100644 index 000000000000..5b7fbf81c990 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/constant_with_visibility.sol @@ -0,0 +1,3 @@ +uint public constant x = 7; +// ---- +// ParserError 2314: (5-11): Expected identifier but got 'public' diff --git a/test/libsolidity/syntaxTests/constants/constant_with_visibility_inverted.sol b/test/libsolidity/syntaxTests/constants/constant_with_visibility_inverted.sol new file mode 100644 index 000000000000..6ac50ca924fc --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/constant_with_visibility_inverted.sol @@ -0,0 +1,3 @@ +uint constant public y = 7; +// ---- +// ParserError 2314: (14-20): Expected identifier but got 'public' diff --git a/test/libsolidity/syntaxTests/constants/cross_file_cyclic.sol b/test/libsolidity/syntaxTests/constants/cross_file_cyclic.sol new file mode 100644 index 000000000000..1843204bdcd1 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cross_file_cyclic.sol @@ -0,0 +1,15 @@ +==== Source: a ==== +import "b"; +uint constant c = d; +==== Source: b ==== +import "a"; +uint constant b = c; +uint constant d = b; +contract C { + uint constant a = b; +} +// ---- +// TypeError 6161: (b:12-31): The value of the constant b has a cyclic dependency via c. +// TypeError 6161: (b:33-52): The value of the constant d has a cyclic dependency via b. +// TypeError 6161: (b:71-90): The value of the constant a has a cyclic dependency via b. +// TypeError 6161: (a:12-31): The value of the constant c has a cyclic dependency via d. diff --git a/test/libsolidity/syntaxTests/constants/cross_file_cyclic_modules.sol b/test/libsolidity/syntaxTests/constants/cross_file_cyclic_modules.sol new file mode 100644 index 000000000000..07dd156b8b31 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cross_file_cyclic_modules.sol @@ -0,0 +1,15 @@ +==== Source: a ==== +import "b"; +uint constant c = d; +==== Source: b ==== +import "a" as M; +uint constant b = M.c; +uint constant d = b; +contract C { + uint constant a = b; +} +// ---- +// TypeError 6161: (b:17-38): The value of the constant b has a cyclic dependency via c. +// TypeError 6161: (b:40-59): The value of the constant d has a cyclic dependency via b. +// TypeError 6161: (b:78-97): The value of the constant a has a cyclic dependency via b. +// TypeError 6161: (a:12-31): The value of the constant c has a cyclic dependency via d. diff --git a/test/libsolidity/syntaxTests/constants/file_level_memory.sol b/test/libsolidity/syntaxTests/constants/file_level_memory.sol new file mode 100644 index 000000000000..ebaf6a410477 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/file_level_memory.sol @@ -0,0 +1,3 @@ +uint[] memory constant x = 2; +// ---- +// ParserError 2314: (7-13): Expected identifier but got 'memory' diff --git a/test/libsolidity/syntaxTests/constants/file_level_memory_inverted.sol b/test/libsolidity/syntaxTests/constants/file_level_memory_inverted.sol new file mode 100644 index 000000000000..95b48d60f3b2 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/file_level_memory_inverted.sol @@ -0,0 +1,3 @@ +uint[] constant memory x = 2; +// ---- +// ParserError 2314: (16-22): Expected identifier but got 'memory' diff --git a/test/libsolidity/syntaxTests/constants/immutable_at_file_level.sol b/test/libsolidity/syntaxTests/constants/immutable_at_file_level.sol new file mode 100644 index 000000000000..01b8ef7c8469 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/immutable_at_file_level.sol @@ -0,0 +1,4 @@ +uint immutable x = 7; +// ---- +// DeclarationError 8342: (0-20): Only constant variables are allowed at file level. +// DeclarationError 8297: (0-20): The "immutable" keyword can only be used for state variables. diff --git a/test/libsolidity/syntaxTests/constants/mapping_constant.sol b/test/libsolidity/syntaxTests/constants/mapping_constant.sol new file mode 100644 index 000000000000..1872ad21f84d --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/mapping_constant.sol @@ -0,0 +1,3 @@ +mapping(uint => uint) constant b = b; +// ---- +// TypeError 9259: (0-36): Constants of non-value type not yet implemented. diff --git a/test/libsolidity/syntaxTests/constants/name_clash_via_import.sol b/test/libsolidity/syntaxTests/constants/name_clash_via_import.sol new file mode 100644 index 000000000000..847c4d52fbde --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/name_clash_via_import.sol @@ -0,0 +1,7 @@ +==== Source: a ==== +uint constant c = 7; +==== Source: b ==== +import {c as d} from "a"; +uint constant d = 7; +// ---- +// DeclarationError 2333: (b:26-45): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/constants/non_constant.sol b/test/libsolidity/syntaxTests/constants/non_constant.sol new file mode 100644 index 000000000000..2851d1e10dd8 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/non_constant.sol @@ -0,0 +1,3 @@ +uint x = 7; +// ---- +// DeclarationError 8342: (0-10): Only constant variables are allowed at file level. diff --git a/test/libsolidity/syntaxTests/constants/redefinition_cross_file.sol b/test/libsolidity/syntaxTests/constants/redefinition_cross_file.sol new file mode 100644 index 000000000000..5ce740377393 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/redefinition_cross_file.sol @@ -0,0 +1,8 @@ +==== Source: a ==== +import "b"; +uint constant c = 7; +==== Source: b ==== +import "a"; +uint constant c = 7; +// ---- +// DeclarationError 2333: (b:12-31): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/constants/redefinition_public_constant.sol b/test/libsolidity/syntaxTests/constants/redefinition_public_constant.sol new file mode 100644 index 000000000000..bbb71b5ac8d2 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/redefinition_public_constant.sol @@ -0,0 +1,4 @@ +uint constant c = 7; +uint constant c = 8; +// ---- +// DeclarationError 2333: (21-40): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/constants/struct_constant.sol b/test/libsolidity/syntaxTests/constants/struct_constant.sol new file mode 100644 index 000000000000..2ccb69bbfc5e --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/struct_constant.sol @@ -0,0 +1,5 @@ +struct S { uint x; } +S constant s; +// ---- +// TypeError 9259: (21-33): Constants of non-value type not yet implemented. +// TypeError 4266: (21-33): Uninitialized "constant" variable. diff --git a/test/libsolidity/syntaxTests/constructor/constructor_visibility.sol b/test/libsolidity/syntaxTests/constructor/constructor_visibility.sol index 9213f4189ae0..0175e22f7e91 100644 --- a/test/libsolidity/syntaxTests/constructor/constructor_visibility.sol +++ b/test/libsolidity/syntaxTests/constructor/constructor_visibility.sol @@ -2,12 +2,12 @@ contract A { constructor(string memory) { } } contract B is A { function f() pure public { - A x = A(0); // convert from address + A x = A(address(0)); // convert from address string memory y = "ab"; A(y); // call as a function is invalid x; } } // ---- -// TypeError 3656: (124-294): Contract "B" should be marked as abstract. -// TypeError 9640: (243-247): Explicit type conversion not allowed from "string memory" to "contract A". +// TypeError 3656: (124-303): Contract "B" should be marked as abstract. +// TypeError 9640: (252-256): Explicit type conversion not allowed from "string memory" to "contract A". diff --git a/test/libsolidity/syntaxTests/constructor/msg_value_non_payable.sol b/test/libsolidity/syntaxTests/constructor/msg_value_non_payable.sol new file mode 100644 index 000000000000..8392cdf6385a --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/msg_value_non_payable.sol @@ -0,0 +1,8 @@ +contract C { + uint256 value; + constructor() { + value = msg.value; + } +} +// ---- +// TypeError 5887: (68-77): "msg.value" and "callvalue()" can only be used in payable constructors. Make the constructor "payable" to avoid this error. diff --git a/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol b/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol index 13b0d02af425..8e0c323b8c33 100644 --- a/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol +++ b/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol @@ -1,5 +1,6 @@ +pragma abicoder v1; contract C { constructor(uint[][][] memory t) {} } // ---- -// TypeError 4957: (26-45): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. Alternatively, make the contract abstract and supply the constructor arguments from a derived contract. +// TypeError 4957: (46-65): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. Alternatively, make the contract abstract and supply the constructor arguments from a derived contract. diff --git a/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol b/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol index 7fc802247f4f..2ca22790c128 100644 --- a/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol +++ b/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol @@ -1,3 +1,4 @@ +pragma abicoder v1; abstract contract C { constructor(uint[][][] memory t) {} } diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol index ec83c596e1f6..5a4173ab6d28 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol @@ -17,3 +17,4 @@ contract C { } } // ---- +// Warning 6321: (399-407): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/conversion/convert_to_super_empty.sol b/test/libsolidity/syntaxTests/conversion/convert_to_super_empty.sol new file mode 100644 index 000000000000..a36882c7ec16 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/convert_to_super_empty.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + super().x; + } +} +// ---- +// TypeError 1744: (52-59): Cannot convert to the super type. diff --git a/test/libsolidity/syntaxTests/conversion/convert_to_super_nonempty.sol b/test/libsolidity/syntaxTests/conversion/convert_to_super_nonempty.sol new file mode 100644 index 000000000000..0cbfe6fcb332 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/convert_to_super_nonempty.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + super(this).f(); + } +} +// ---- +// TypeError 1744: (52-63): Cannot convert to the super type. diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_array_of_string_literals_to_calldata_string.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_array_of_string_literals_to_calldata_string.sol new file mode 100644 index 000000000000..b9f95df66280 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_array_of_string_literals_to_calldata_string.sol @@ -0,0 +1,9 @@ +pragma abicoder v2; + +contract C { + function f() public pure returns(string[5] calldata) { + return ["h", "e", "l", "l", "o"]; + } +} +// ---- +// TypeError 6359: (122-147): Return argument type string memory[5] memory is not implicitly convertible to expected type (type of first return variable) string calldata[5] calldata. diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_string_literal_to_calldata_string.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_string_literal_to_calldata_string.sol new file mode 100644 index 000000000000..166ebffb0b53 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_string_literal_to_calldata_string.sol @@ -0,0 +1,17 @@ +contract C { + function f1() public pure returns(string calldata) { + return "hello"; + } + + function f2() public pure returns(string calldata) { + return unicode"hello"; + } + + function f3() public pure returns(bytes calldata) { + return hex"68656c6c6f"; + } +} +// ---- +// TypeError 6359: (85-92): Return argument type literal_string "hello" is not implicitly convertible to expected type (type of first return variable) string calldata. +// TypeError 6359: (173-187): Return argument type literal_string "hello" is not implicitly convertible to expected type (type of first return variable) string calldata. +// TypeError 6359: (267-282): Return argument type literal_string "hello" is not implicitly convertible to expected type (type of first return variable) bytes calldata. diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_string_literal_to_calldata_string_in_function_parameter.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_string_literal_to_calldata_string_in_function_parameter.sol new file mode 100644 index 000000000000..bf58cba42935 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_string_literal_to_calldata_string_in_function_parameter.sol @@ -0,0 +1,14 @@ +contract C { + function g(string calldata _s) public {} + function h(bytes calldata _b) public {} + + function f() public { + g("hello"); + g(unicode"hello"); + h(hex"68656c6c6f"); + } +} +// ---- +// TypeError 9553: (139-146): Invalid type for argument in function call. Invalid implicit conversion from literal_string "hello" to string calldata requested. +// TypeError 9553: (159-173): Invalid type for argument in function call. Invalid implicit conversion from literal_string "hello" to string calldata requested. +// TypeError 9553: (186-201): Invalid type for argument in function call. Invalid implicit conversion from literal_string "hello" to bytes calldata requested. diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_comparison.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_comparison.sol new file mode 100644 index 000000000000..d07594b09fb4 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_comparison.sol @@ -0,0 +1,27 @@ +contract D {} + +contract C { + C c; + D d; + + function foo() public { + // Current instance of the current contract vs super + super != this; + this != super; + + // Different instance of the current contract vs super + super != c; + c != super; + + // Instance of an unrelated contract vs super + super != d; + d != super; + } +} +// ---- +// TypeError 2271: (144-157): Operator != not compatible with types type(contract super C) and contract C +// TypeError 2271: (167-180): Operator != not compatible with types contract C and type(contract super C) +// TypeError 2271: (254-264): Operator != not compatible with types type(contract super C) and contract C +// TypeError 2271: (274-284): Operator != not compatible with types contract C and type(contract super C) +// TypeError 2271: (349-359): Operator != not compatible with types type(contract super C) and contract D +// TypeError 2271: (369-379): Operator != not compatible with types contract D and type(contract super C) diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_operators.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_operators.sol new file mode 100644 index 000000000000..8a803a233389 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_operators.sol @@ -0,0 +1,56 @@ +contract C { + function foo() public { + super << this; + super >> this; + super ^ this; + super | this; + super & this; + + super * this; + super / this; + super % this; + super - this; + super + this; + super ** this; + + super == this; + super != this; + super >= this; + super <= this; + super < this; + super > this; + + super || this; + super && this; + + super -= this; + super += this; + + true ? super : this; + } +} +// ---- +// TypeError 2271: (49-62): Operator << not compatible with types type(contract super C) and contract C +// TypeError 2271: (72-85): Operator >> not compatible with types type(contract super C) and contract C +// TypeError 2271: (95-107): Operator ^ not compatible with types type(contract super C) and contract C +// TypeError 2271: (117-129): Operator | not compatible with types type(contract super C) and contract C +// TypeError 2271: (139-151): Operator & not compatible with types type(contract super C) and contract C +// TypeError 2271: (162-174): Operator * not compatible with types type(contract super C) and contract C +// TypeError 2271: (184-196): Operator / not compatible with types type(contract super C) and contract C +// TypeError 2271: (206-218): Operator % not compatible with types type(contract super C) and contract C +// TypeError 2271: (228-240): Operator - not compatible with types type(contract super C) and contract C +// TypeError 2271: (250-262): Operator + not compatible with types type(contract super C) and contract C +// TypeError 2271: (272-285): Operator ** not compatible with types type(contract super C) and contract C +// TypeError 2271: (296-309): Operator == not compatible with types type(contract super C) and contract C +// TypeError 2271: (319-332): Operator != not compatible with types type(contract super C) and contract C +// TypeError 2271: (342-355): Operator >= not compatible with types type(contract super C) and contract C +// TypeError 2271: (365-378): Operator <= not compatible with types type(contract super C) and contract C +// TypeError 2271: (388-400): Operator < not compatible with types type(contract super C) and contract C +// TypeError 2271: (410-422): Operator > not compatible with types type(contract super C) and contract C +// TypeError 2271: (433-446): Operator || not compatible with types type(contract super C) and contract C +// TypeError 2271: (456-469): Operator && not compatible with types type(contract super C) and contract C +// TypeError 4247: (480-485): Expression has to be an lvalue. +// TypeError 7366: (480-493): Operator -= not compatible with types type(contract super C) and contract C +// TypeError 4247: (503-508): Expression has to be an lvalue. +// TypeError 7366: (503-516): Operator += not compatible with types type(contract super C) and contract C +// TypeError 1080: (527-546): True expression's type type(contract super C) does not match false expression's type contract C. diff --git a/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol index e2af196ee90f..749a9b0d9daf 100644 --- a/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol +++ b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_from_super.sol @@ -12,4 +12,4 @@ contract B is S } } // ---- -// TypeError 9640: (129-137): Explicit type conversion not allowed from "contract super B" to "contract S". +// TypeError 9640: (129-137): Explicit type conversion not allowed from "type(contract super B)" to "contract S". diff --git a/test/libsolidity/syntaxTests/dataLocations/function_return_parameters_with_data_location_fine.sol b/test/libsolidity/syntaxTests/dataLocations/function_return_parameters_with_data_location_fine.sol index ea019198dd99..6972c7a270e7 100644 --- a/test/libsolidity/syntaxTests/dataLocations/function_return_parameters_with_data_location_fine.sol +++ b/test/libsolidity/syntaxTests/dataLocations/function_return_parameters_with_data_location_fine.sol @@ -4,3 +4,6 @@ contract C { function h() public pure returns(uint[] memory) {} function i() external pure returns(uint[] memory) {} } +// ---- +// Warning 6321: (51-64): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (134-147): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/deprecated_functions.sol b/test/libsolidity/syntaxTests/deprecated_functions.sol index fed56d3053e7..239d22ab5742 100644 --- a/test/libsolidity/syntaxTests/deprecated_functions.sol +++ b/test/libsolidity/syntaxTests/deprecated_functions.sol @@ -4,7 +4,7 @@ contract test { x; } function g() public { - suicide(0x0000000000000000000000000000000000000001); + suicide(payable(0x0000000000000000000000000000000000000001)); } } // ---- diff --git a/test/libsolidity/syntaxTests/duplicateFunctions/illegal_names_exception.sol b/test/libsolidity/syntaxTests/duplicateFunctions/illegal_names_exception.sol new file mode 100644 index 000000000000..e7541fe233d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/duplicateFunctions/illegal_names_exception.sol @@ -0,0 +1,12 @@ +// Exception for the rule about illegal names. +contract C { + function this() public { + } + function super() public { + } + function _() public { + } +} +// ---- +// Warning 2319: (61-88): This declaration shadows a builtin symbol. +// Warning 2319: (90-118): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/duplicateFunctions/illegal_names_functions.sol b/test/libsolidity/syntaxTests/duplicateFunctions/illegal_names_functions.sol new file mode 100644 index 000000000000..e6c7134850f6 --- /dev/null +++ b/test/libsolidity/syntaxTests/duplicateFunctions/illegal_names_functions.sol @@ -0,0 +1,17 @@ +contract C { + function _() internal returns(uint) { + return 1; + } + + function super() internal { + } + + function this() internal { + } +} +// ---- +// DeclarationError 3726: (17-78): The name "_" is reserved. +// DeclarationError 3726: (84-117): The name "super" is reserved. +// DeclarationError 3726: (123-155): The name "this" is reserved. +// Warning 2319: (84-117): This declaration shadows a builtin symbol. +// Warning 2319: (123-155): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/duplicate_contract.sol b/test/libsolidity/syntaxTests/duplicate_contract.sol new file mode 100644 index 000000000000..39e5273071b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/duplicate_contract.sol @@ -0,0 +1,4 @@ +contract X {} +contract X {} +// ---- +// DeclarationError 2333: (14-27): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/enums/enum_with_too_many_members.sol b/test/libsolidity/syntaxTests/enums/enum_with_too_many_members.sol new file mode 100644 index 000000000000..c4eced557670 --- /dev/null +++ b/test/libsolidity/syntaxTests/enums/enum_with_too_many_members.sol @@ -0,0 +1,30 @@ +enum E { + E000, E001, E002, E003, E004, E005, E006, E007, E008, E009, + E010, E011, E012, E013, E014, E015, E016, E017, E018, E019, + E020, E021, E022, E023, E024, E025, E026, E027, E028, E029, + E030, E031, E032, E033, E034, E035, E036, E037, E038, E039, + E040, E041, E042, E043, E044, E045, E046, E047, E048, E049, + E050, E051, E052, E053, E054, E055, E056, E057, E058, E059, + E060, E061, E062, E063, E064, E065, E066, E067, E068, E069, + E070, E071, E072, E073, E074, E075, E076, E077, E078, E079, + E080, E081, E082, E083, E084, E085, E086, E087, E088, E089, + E090, E091, E092, E093, E094, E095, E096, E097, E098, E099, + E100, E101, E102, E103, E104, E105, E106, E107, E108, E109, + E110, E111, E112, E113, E114, E115, E116, E117, E118, E119, + E120, E121, E122, E123, E124, E125, E126, E127, E128, E129, + E130, E131, E132, E133, E134, E135, E136, E137, E138, E139, + E140, E141, E142, E143, E144, E145, E146, E147, E148, E149, + E150, E151, E152, E153, E154, E155, E156, E157, E158, E159, + E160, E161, E162, E163, E164, E165, E166, E167, E168, E169, + E170, E171, E172, E173, E174, E175, E176, E177, E178, E179, + E180, E181, E182, E183, E184, E185, E186, E187, E188, E189, + E190, E191, E192, E193, E194, E195, E196, E197, E198, E199, + E200, E201, E202, E203, E204, E205, E206, E207, E208, E209, + E210, E211, E212, E213, E214, E215, E216, E217, E218, E219, + E220, E221, E222, E223, E224, E225, E226, E227, E228, E229, + E230, E231, E232, E233, E234, E235, E236, E237, E238, E239, + E240, E241, E242, E243, E244, E245, E246, E247, E248, E249, + E250, E251, E252, E253, E254, E255, E256 +} +// ---- +// DeclarationError 1611: (0-1655): Enum with more than 256 members is not allowed. diff --git a/test/libsolidity/syntaxTests/enums/illegal_names.sol b/test/libsolidity/syntaxTests/enums/illegal_names.sol new file mode 100644 index 000000000000..399d91ed1252 --- /dev/null +++ b/test/libsolidity/syntaxTests/enums/illegal_names.sol @@ -0,0 +1,31 @@ +enum this { + a +} +enum super { + b +} +enum _ { + c +} + +enum E { + this, + super, + _ +} + +contract C { + this a; + super b; + _ c; + E e; +} +// ---- +// DeclarationError 3726: (0-19): The name "this" is reserved. +// DeclarationError 3726: (20-40): The name "super" is reserved. +// DeclarationError 3726: (41-57): The name "_" is reserved. +// DeclarationError 3726: (72-76): The name "this" is reserved. +// DeclarationError 3726: (82-87): The name "super" is reserved. +// DeclarationError 3726: (93-94): The name "_" is reserved. +// Warning 2319: (0-19): This declaration shadows a builtin symbol. +// Warning 2319: (20-40): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/enums/literal_conversion.sol b/test/libsolidity/syntaxTests/enums/literal_conversion.sol new file mode 100644 index 000000000000..4c1487b7e48b --- /dev/null +++ b/test/libsolidity/syntaxTests/enums/literal_conversion.sol @@ -0,0 +1,10 @@ +contract C { + enum Test { One, Two } + function f() public pure { + Test a = Test(0); + Test b = Test(1); + Test c = Test(type(uint).max); + a; b; c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/enums/literal_conversion_error.sol b/test/libsolidity/syntaxTests/enums/literal_conversion_error.sol new file mode 100644 index 000000000000..c6d220f617b3 --- /dev/null +++ b/test/libsolidity/syntaxTests/enums/literal_conversion_error.sol @@ -0,0 +1,16 @@ +contract C { + enum Test { One, Two } + function f() public { + Test(-1); + Test(2); + Test(13); + Test(5/3); + Test(0.5); + } +} +// ---- +// TypeError 9640: (74-82): Explicit type conversion not allowed from "int_const -1" to "enum C.Test". +// TypeError 9640: (92-99): Explicit type conversion not allowed from "int_const 2" to "enum C.Test". +// TypeError 9640: (109-117): Explicit type conversion not allowed from "int_const 13" to "enum C.Test". +// TypeError 9640: (127-136): Explicit type conversion not allowed from "rational_const 5 / 3" to "enum C.Test". +// TypeError 9640: (146-155): Explicit type conversion not allowed from "rational_const 1 / 2" to "enum C.Test". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/082_anonymous_event_four_indexed.sol b/test/libsolidity/syntaxTests/events/anonymous_event_four_indexed.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/082_anonymous_event_four_indexed.sol rename to test/libsolidity/syntaxTests/events/anonymous_event_four_indexed.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/083_anonymous_event_too_many_indexed.sol b/test/libsolidity/syntaxTests/events/anonymous_event_too_many_indexed.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/083_anonymous_event_too_many_indexed.sol rename to test/libsolidity/syntaxTests/events/anonymous_event_too_many_indexed.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/087_double_event_declaration.sol b/test/libsolidity/syntaxTests/events/double_event_declaration.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/087_double_event_declaration.sol rename to test/libsolidity/syntaxTests/events/double_event_declaration.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/088_double_event_declaration_ignores_anonymous.sol b/test/libsolidity/syntaxTests/events/double_event_declaration_ignores_anonymous.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/088_double_event_declaration_ignores_anonymous.sol rename to test/libsolidity/syntaxTests/events/double_event_declaration_ignores_anonymous.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/089_double_event_declaration_ignores_indexed.sol b/test/libsolidity/syntaxTests/events/double_event_declaration_ignores_indexed.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/089_double_event_declaration_ignores_indexed.sol rename to test/libsolidity/syntaxTests/events/double_event_declaration_ignores_indexed.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/080_event.sol b/test/libsolidity/syntaxTests/events/event.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/080_event.sol rename to test/libsolidity/syntaxTests/events/event.sol diff --git a/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol index 6c208be87581..558e2f4337e9 100644 --- a/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract c { event E(uint[] indexed); } diff --git a/test/libsolidity/syntaxTests/events/event_array_v2.sol b/test/libsolidity/syntaxTests/events/event_array_v2.sol index 9a3cc3966214..6f0a1936d107 100644 --- a/test/libsolidity/syntaxTests/events/event_array_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_array_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract c { event E(uint[]); } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/090_event_call.sol b/test/libsolidity/syntaxTests/events/event_call.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/090_event_call.sol rename to test/libsolidity/syntaxTests/events/event_call.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/561_event_emit_complex.sol b/test/libsolidity/syntaxTests/events/event_emit_complex.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/561_event_emit_complex.sol rename to test/libsolidity/syntaxTests/events/event_emit_complex.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/562_event_emit_foreign_class.sol b/test/libsolidity/syntaxTests/events/event_emit_foreign_class.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/562_event_emit_foreign_class.sol rename to test/libsolidity/syntaxTests/events/event_emit_foreign_class.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/560_event_emit_simple.sol b/test/libsolidity/syntaxTests/events/event_emit_simple.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/560_event_emit_simple.sol rename to test/libsolidity/syntaxTests/events/event_emit_simple.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/091_event_function_inheritance_clash.sol b/test/libsolidity/syntaxTests/events/event_function_inheritance_clash.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/091_event_function_inheritance_clash.sol rename to test/libsolidity/syntaxTests/events/event_function_inheritance_clash.sol diff --git a/test/libsolidity/syntaxTests/events/event_function_type_indexed.sol b/test/libsolidity/syntaxTests/events/event_function_type_indexed.sol new file mode 100644 index 000000000000..cc0a573a91c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_function_type_indexed.sol @@ -0,0 +1,6 @@ +contract C { + event Test(function() external indexed); + function f() public { + emit Test(this.f); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/094_event_inheritance.sol b/test/libsolidity/syntaxTests/events/event_inheritance.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/094_event_inheritance.sol rename to test/libsolidity/syntaxTests/events/event_inheritance.sol diff --git a/test/libsolidity/syntaxTests/events/event_library_function.sol b/test/libsolidity/syntaxTests/events/event_library_function.sol new file mode 100644 index 000000000000..382786faa465 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_library_function.sol @@ -0,0 +1,35 @@ +library L { + function f() public { + int x = 1; + } +} + +contract C { + event Test(function() external indexed); + + function g() public { + Test(L.f); + } +} + +contract D { + event Test(function() external); + + function f() public { + Test(L.f); + } +} + +contract E { + event Test(function() external indexed); + + using L for D; + + function k() public { + Test(D.f); + } +} +// ---- +// TypeError 9553: (140-143): Invalid type for argument in function call. Invalid implicit conversion from function () to function () external requested. Special functions can not be converted to function types. +// TypeError 9553: (230-233): Invalid type for argument in function call. Invalid implicit conversion from function () to function () external requested. Special functions can not be converted to function types. +// TypeError 9553: (345-348): Invalid type for argument in function call. Invalid implicit conversion from function D.f() to function () external requested. Special functions can not be converted to function types. diff --git a/test/libsolidity/syntaxTests/events/event_named_arguments_in_any_order.sol b/test/libsolidity/syntaxTests/events/event_named_arguments_in_any_order.sol new file mode 100644 index 000000000000..372659e898d3 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_named_arguments_in_any_order.sol @@ -0,0 +1,13 @@ +contract C { + event e(uint u, string s, bool b); + + function call() public { + emit e({s: "abc", u: 1, b: true}); + emit e({s: "abc", b: true, u: 1}); + emit e({u: 1, s: "abc", b: true}); + emit e({b: true, s: "abc", u: 1}); + emit e({u: 1, b: true, s: "abc"}); + emit e({b: true, u: 1, s: "abc"}); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/events/event_nested_array.sol b/test/libsolidity/syntaxTests/events/event_nested_array.sol index 6d76a6dcd38c..2491dae6e824 100644 --- a/test/libsolidity/syntaxTests/events/event_nested_array.sol +++ b/test/libsolidity/syntaxTests/events/event_nested_array.sol @@ -1,5 +1,6 @@ +pragma abicoder v1; contract c { event E(uint[][]); } // ---- -// TypeError 3061: (25-33): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 3061: (45-53): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol b/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol index 4e6f10d2add7..ee868a7152ad 100644 --- a/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol +++ b/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol @@ -1,6 +1,7 @@ +pragma abicoder v1; contract c { struct S { uint x; uint[][] arr; } event E(S); } // ---- -// TypeError 3061: (61-62): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 3061: (81-82): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol index 3f8a7c62650f..0643fdec29df 100644 --- a/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract c { event E(uint[][] indexed); } diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol index 1ec22f9e8ad7..a314a164f8bc 100644 --- a/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_nested_array_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract c { event E(uint[][]); } diff --git a/test/libsolidity/syntaxTests/events/event_overload_named_arguments_ambiguous.sol b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_ambiguous.sol new file mode 100644 index 000000000000..28ee39e152ab --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_ambiguous.sol @@ -0,0 +1,10 @@ +contract C { + event e(uint u, string s); + event e(string s, uint u); + + function call() public { + emit e({u: 2, s: "abc"}); + } +} +// ---- +// TypeError 4487: (118-119): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/events/event_overload_named_arguments_ambiguous_implicit_conversion.sol b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_ambiguous_implicit_conversion.sol new file mode 100644 index 000000000000..2c7884a9dfc0 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_ambiguous_implicit_conversion.sol @@ -0,0 +1,10 @@ +contract C { + event e(uint u, string s); + event e(bytes s, int u); + + function call() public { + emit e({u: 2, s: "abc"}); + } +} +// ---- +// TypeError 4487: (116-117): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/events/event_overload_named_arguments_in_any_order.sol b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_in_any_order.sol new file mode 100644 index 000000000000..e1d4eb34399e --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_in_any_order.sol @@ -0,0 +1,14 @@ +contract C { + event e(uint u, string s, bool b); + event e(uint u, uint s, uint b); + + function call() public { + emit e({s: "abc", u: 1, b: true}); + emit e({s: "abc", b: true, u: 1}); + emit e({u: 1, s: "abc", b: true}); + emit e({b: true, s: "abc", u: 1}); + emit e({u: 1, b: true, s: "abc"}); + emit e({b: true, u: 1, s: "abc"}); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/events/event_overload_named_arguments_wrong_types.sol b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_wrong_types.sol new file mode 100644 index 000000000000..abee5dff1210 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/event_overload_named_arguments_wrong_types.sol @@ -0,0 +1,10 @@ +contract C { + event e(uint u, string s); + event e(string s, uint u); + + function call() public { + emit e({s: 2, u: "abc"}); + } +} +// ---- +// TypeError 9322: (118-119): No matching declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/587_event_param_type_outside_storage.sol b/test/libsolidity/syntaxTests/events/event_param_type_outside_storage.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/587_event_param_type_outside_storage.sol rename to test/libsolidity/syntaxTests/events/event_param_type_outside_storage.sol diff --git a/test/libsolidity/syntaxTests/events/event_struct.sol b/test/libsolidity/syntaxTests/events/event_struct.sol index 52a01c5e9d17..ac6355fe0744 100644 --- a/test/libsolidity/syntaxTests/events/event_struct.sol +++ b/test/libsolidity/syntaxTests/events/event_struct.sol @@ -1,6 +1,7 @@ +pragma abicoder v1; contract c { struct S { uint a ; } event E(S); } // ---- -// TypeError 3061: (51-52): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 3061: (71-72): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol index 19676b5cc673..7e3393400c79 100644 --- a/test/libsolidity/syntaxTests/events/event_struct_indexed.sol +++ b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol @@ -1,6 +1,7 @@ +pragma abicoder v1; contract c { struct S { uint a ; } event E(S indexed); } // ---- -// TypeError 3061: (51-60): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 3061: (71-80): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol index 2a874831347a..b67b37b856e7 100644 --- a/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract c { struct S { uint a ; } event E(S indexed); diff --git a/test/libsolidity/syntaxTests/events/event_struct_v2.sol b/test/libsolidity/syntaxTests/events/event_struct_v2.sol index 4f4839acc235..53e538d1a266 100644 --- a/test/libsolidity/syntaxTests/events/event_struct_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_struct_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract c { struct S { uint a ; } event E(S); diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/081_event_too_many_indexed.sol b/test/libsolidity/syntaxTests/events/event_too_many_indexed.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/081_event_too_many_indexed.sol rename to test/libsolidity/syntaxTests/events/event_too_many_indexed.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/563_event_without_emit_deprecated.sol b/test/libsolidity/syntaxTests/events/event_without_emit_deprecated.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/563_event_without_emit_deprecated.sol rename to test/libsolidity/syntaxTests/events/event_without_emit_deprecated.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/084_events_with_same_name.sol b/test/libsolidity/syntaxTests/events/events_with_same_name.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/084_events_with_same_name.sol rename to test/libsolidity/syntaxTests/events/events_with_same_name.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/086_events_with_same_name_different_types.sol b/test/libsolidity/syntaxTests/events/events_with_same_name_different_types.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/086_events_with_same_name_different_types.sol rename to test/libsolidity/syntaxTests/events/events_with_same_name_different_types.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/085_events_with_same_name_unnamed_arguments.sol b/test/libsolidity/syntaxTests/events/events_with_same_name_unnamed_arguments.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/085_events_with_same_name_unnamed_arguments.sol rename to test/libsolidity/syntaxTests/events/events_with_same_name_unnamed_arguments.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/093_function_event_in_contract_clash.sol b/test/libsolidity/syntaxTests/events/function_event_in_contract_clash.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/093_function_event_in_contract_clash.sol rename to test/libsolidity/syntaxTests/events/function_event_in_contract_clash.sol diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/092_function_event_inheritance_clash.sol b/test/libsolidity/syntaxTests/events/function_event_inheritance_clash.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/092_function_event_inheritance_clash.sol rename to test/libsolidity/syntaxTests/events/function_event_inheritance_clash.sol diff --git a/test/libsolidity/syntaxTests/events/illegal_names_exception.sol b/test/libsolidity/syntaxTests/events/illegal_names_exception.sol new file mode 100644 index 000000000000..c45db8d7d123 --- /dev/null +++ b/test/libsolidity/syntaxTests/events/illegal_names_exception.sol @@ -0,0 +1,9 @@ +// Exception for the illegal name list. External interface events +contract C { + event this(); + event super(); + event _(); +} +// ---- +// Warning 2319: (80-93): This declaration shadows a builtin symbol. +// Warning 2319: (95-109): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/095_multiple_events_argument_clash.sol b/test/libsolidity/syntaxTests/events/multiple_events_argument_clash.sol similarity index 100% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/095_multiple_events_argument_clash.sol rename to test/libsolidity/syntaxTests/events/multiple_events_argument_clash.sol diff --git a/test/libsolidity/syntaxTests/fallback/arguments.sol b/test/libsolidity/syntaxTests/fallback/arguments.sol index 806874feb6bd..c796161d2a4f 100644 --- a/test/libsolidity/syntaxTests/fallback/arguments.sol +++ b/test/libsolidity/syntaxTests/fallback/arguments.sol @@ -2,4 +2,4 @@ contract C { fallback(uint256) external {} } // ---- -// TypeError 3978: (25-34): Fallback function cannot take parameters. +// TypeError 5570: (44-44): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns.sol b/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns.sol new file mode 100644 index 000000000000..625c17f98430 --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns.sol @@ -0,0 +1,6 @@ +contract C { + fallback(bytes calldata _input) external returns (bytes memory _output) {} + fallback() external {} +} +// ---- +// DeclarationError 7301: (96-118): Only one fallback function is allowed. diff --git a/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns_inheritance.sol b/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns_inheritance.sol new file mode 100644 index 000000000000..818006a11563 --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns_inheritance.sol @@ -0,0 +1,9 @@ +contract C { + fallback(bytes calldata _input) external returns (bytes memory _output) {} +} +contract D is C { + fallback() external {} +} +// ---- +// TypeError 9456: (116-138): Overriding function is missing "override" specifier. +// TypeError 4334: (17-91): Trying to override non-virtual function. Did you forget to add "virtual"? diff --git a/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns_override.sol b/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns_override.sol new file mode 100644 index 000000000000..32638c08200f --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/fallback_duplicate_returns_override.sol @@ -0,0 +1,7 @@ +contract C { + fallback(bytes calldata _input) external virtual returns (bytes memory _output) {} +} +contract D is C { + fallback() external override {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/fallback/fallback_wrong_data_location.sol b/test/libsolidity/syntaxTests/fallback/fallback_wrong_data_location.sol new file mode 100644 index 000000000000..5ddefdf3d424 --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/fallback_wrong_data_location.sol @@ -0,0 +1,13 @@ +contract D { + fallback(bytes memory) external returns (bytes memory) {} +} +contract E { + fallback(bytes memory) external returns (bytes calldata) {} +} +contract F { + fallback(bytes calldata) external returns (bytes calldata) {} +} +// ---- +// TypeError 5570: (57-71): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". +// TypeError 5570: (134-150): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". +// TypeError 5570: (215-231): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/fallback/inheritance_multi_base.sol b/test/libsolidity/syntaxTests/fallback/inheritance_multi_base.sol new file mode 100644 index 000000000000..330ff156ce8a --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/inheritance_multi_base.sol @@ -0,0 +1,20 @@ +contract A { + fallback (bytes calldata _input) external returns (bytes memory) { + return _input; + } +} +contract B { + fallback (bytes calldata _input) external returns (bytes memory) { + return "xyz"; + } +} +contract C is B, A { + function f() public returns (bool, bytes memory) { + (bool success, bytes memory retval) = address(this).call("abc"); + return (success, retval); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError 6480: (229-420): Derived contract must override function "". Two or more base classes define function with same name and parameter types. diff --git a/test/libsolidity/syntaxTests/fallback/no_input_no_output.sol b/test/libsolidity/syntaxTests/fallback/no_input_no_output.sol new file mode 100644 index 000000000000..868671d2a8b3 --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/no_input_no_output.sol @@ -0,0 +1,9 @@ +contract C { + fallback() external returns (bytes memory _output) {} +} +contract D { + fallback(bytes calldata _input) external {} +} +// ---- +// TypeError 5570: (45-67): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". +// TypeError 5570: (131-131): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/fallback/return_value_number.sol b/test/libsolidity/syntaxTests/fallback/return_value_number.sol index c41b837a8cc2..e5d358c73887 100644 --- a/test/libsolidity/syntaxTests/fallback/return_value_number.sol +++ b/test/libsolidity/syntaxTests/fallback/return_value_number.sol @@ -2,4 +2,4 @@ contract C { fallback() external returns (bytes memory, bytes memory) {} } // ---- -// TypeError 5570: (45-73): Fallback function can only have a single "bytes memory" return value. +// TypeError 5570: (45-73): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/fallback/return_value_type.sol b/test/libsolidity/syntaxTests/fallback/return_value_type.sol index a6557139ded7..01363ebe4589 100644 --- a/test/libsolidity/syntaxTests/fallback/return_value_type.sol +++ b/test/libsolidity/syntaxTests/fallback/return_value_type.sol @@ -2,4 +2,4 @@ contract C { fallback() external returns (uint256) {} } // ---- -// TypeError 5570: (45-54): Fallback function can only have a single "bytes memory" return value. +// TypeError 5570: (45-54): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol b/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol index 3b90b5ad2568..88f181f3fd20 100644 --- a/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol +++ b/test/libsolidity/syntaxTests/fallback/return_value_unsupported.sol @@ -2,4 +2,4 @@ contract C { fallback() external returns (bytes memory) {} } // ---- -// TypeError 6151: (45-59): Return values for fallback functions are not yet implemented. +// TypeError 5570: (45-59): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/fallback/returns.sol b/test/libsolidity/syntaxTests/fallback/returns.sol new file mode 100644 index 000000000000..a4d45c522278 --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/returns.sol @@ -0,0 +1,4 @@ +contract C { + fallback(bytes calldata _input) external returns (bytes memory _output) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol new file mode 100644 index 000000000000..749a98d48c11 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure {} +} +function fun() { + C.f(); +} +// ---- +// TypeError 3419: (68-73): Cannot call function via contract type name. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol new file mode 100644 index 000000000000..e44356dc3e6d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol @@ -0,0 +1,3 @@ +constructor() {} +// ---- +// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_different_integer_types.sol b/test/libsolidity/syntaxTests/freeFunctions/free_different_integer_types.sol new file mode 100644 index 000000000000..20d9596bca87 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_different_integer_types.sol @@ -0,0 +1,4 @@ +function f(uint24) {} +function f(uint16) {} +function f(int24) {} +function f(bool) {} diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol new file mode 100644 index 000000000000..e813e58ee93a --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol @@ -0,0 +1,3 @@ +fallback(){} +// ---- +// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol new file mode 100644 index 000000000000..cfe5c3ffc6a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol @@ -0,0 +1,4 @@ +function fun() someModifier { +} +// ---- +// DeclarationError 7920: (15-27): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_namesake_different_parameter_types.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_namesake_different_parameter_types.sol new file mode 100644 index 000000000000..bde560ccb5cb --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_namesake_different_parameter_types.sol @@ -0,0 +1,11 @@ +function g() pure returns (uint) { return 1; } +function g() pure returns (string memory) { return "1"; } +contract C { + function foo() public pure returns (uint) { + string memory s = g(); + return 100/g(); + } +} +// ---- +// DeclarationError 1686: (0-46): Function with same name and parameter types defined twice. +// TypeError 9574: (168-189): Type uint256 is not implicitly convertible to expected type string memory. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol new file mode 100644 index 000000000000..7d45986b668e --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol @@ -0,0 +1,9 @@ +contract C { + modifier someModifier() { _; } +} + +function fun() C.someModifier { + +} +// ---- +// SyntaxError 5811: (49-83): Free functions cannot have modifiers. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol new file mode 100644 index 000000000000..bd1b2bd875d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol @@ -0,0 +1,9 @@ +function f() {} +contract C { + function f() public {} + function g() public { + f(); + } +} +// ---- +// Warning 2519: (31-53): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol new file mode 100644 index 000000000000..36a0e0e0c0fb --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol @@ -0,0 +1,5 @@ +function fun1() public { } +function fun2() internal { } +// ---- +// SyntaxError 4126: (0-26): Free functions cannot have visibility. +// SyntaxError 4126: (27-55): Free functions cannot have visibility. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol new file mode 100644 index 000000000000..2fec9db1d07b --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol @@ -0,0 +1,3 @@ +function f(); +// ---- +// TypeError 4668: (0-13): Free functions must be implemented. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol new file mode 100644 index 000000000000..4862e8e63bba --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol @@ -0,0 +1,4 @@ +function fun(uint256, uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_identical.sol b/test/libsolidity/syntaxTests/freeFunctions/free_identical.sol new file mode 100644 index 000000000000..58d5a5b5bf98 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_identical.sol @@ -0,0 +1,4 @@ +function f() pure returns (uint) { return 1337; } +function f() pure returns (uint) { return 42; } +// ---- +// DeclarationError 1686: (0-49): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_identical_multiple.sol b/test/libsolidity/syntaxTests/freeFunctions/free_identical_multiple.sol new file mode 100644 index 000000000000..2eb8b6b4b40f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_identical_multiple.sol @@ -0,0 +1,5 @@ +function f() pure returns (uint) { return 1337; } +function f() pure returns (uint) { return 42; } +function f() pure returns (uint) { return 1; } +// ---- +// DeclarationError 1686: (0-49): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol new file mode 100644 index 000000000000..dafa91ccbca1 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol @@ -0,0 +1,8 @@ +function f() { + uint x = 2; + x; +} +function g(uint[] storage x) pure { x[0] = 1; } +// ---- +// Warning 2018: (0-39): Function state mutability can be restricted to pure +// TypeError 8961: (76-80): Function declared as pure, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_namesake_contract_function.sol b/test/libsolidity/syntaxTests/freeFunctions/free_namesake_contract_function.sol new file mode 100644 index 000000000000..4212154cc4fe --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_namesake_contract_function.sol @@ -0,0 +1,8 @@ +function f() pure returns (uint) { return 1337; } +contract C { + function f() public pure returns (uint) { + return f(); + } +} +// ---- +// Warning 2519: (65-126): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol new file mode 100644 index 000000000000..440b9449f35f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol @@ -0,0 +1,9 @@ +function f(uint) returns (bytes memory) {} +function f(uint[] memory x) returns (bytes memory) { return f(x[0]); } +function g(uint8) {} +function g(uint16) {} +function t() { + g(2); +} +// ---- +// TypeError 4487: (176-177): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_override.sol b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol new file mode 100644 index 000000000000..da54a2fa62cf --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol @@ -0,0 +1,4 @@ +function fun() override { +} +// ---- +// SyntaxError 1750: (0-27): Free functions cannot override. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol new file mode 100644 index 000000000000..563c118f0d33 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol @@ -0,0 +1,4 @@ +function fun() payable { +} +// ---- +// TypeError 9559: (0-26): Free functions cannot be payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol new file mode 100644 index 000000000000..d1e790d7c4ed --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol @@ -0,0 +1,3 @@ +receive() {} +// ---- +// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_redefinition.sol b/test/libsolidity/syntaxTests/freeFunctions/free_redefinition.sol new file mode 100644 index 000000000000..91c53aff12de --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_redefinition.sol @@ -0,0 +1,9 @@ +function f() pure returns (uint) { return 1337; } +function f() view returns (uint) { return 42; } +contract C { + function g() public pure virtual returns (uint) { + return f(); + } +} +// ---- +// DeclarationError 1686: (0-49): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol new file mode 100644 index 000000000000..7d356a9a7dcd --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol @@ -0,0 +1,4 @@ +struct S { uint x; } +function fun(S storage) { +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol new file mode 100644 index 000000000000..a4261be1fb1b --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol @@ -0,0 +1,4 @@ +function fun() virtual { +} +// ---- +// SyntaxError 4493: (0-26): Free functions cannot be virtual. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol new file mode 100644 index 000000000000..3b16749ae92d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol @@ -0,0 +1,4 @@ +contract C {} +function C() {} +// ---- +// DeclarationError 2333: (14-29): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol new file mode 100644 index 000000000000..b029c0d90dde --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol @@ -0,0 +1,6 @@ +contract C { + struct S { uint x; } +} +function f() returns (uint) { S storage t; } +// ---- +// DeclarationError 7920: (70-71): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol new file mode 100644 index 000000000000..7e2dafe8a2c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol @@ -0,0 +1,6 @@ +function fun() { + fun{gas: 1}(); + fun{value: 1}(); +} +// ---- +// TypeError 2193: (21-32): Function call options can only be set on external function calls or contract creations. diff --git a/test/libsolidity/syntaxTests/freeFunctions/illegal_names.sol b/test/libsolidity/syntaxTests/freeFunctions/illegal_names.sol new file mode 100644 index 000000000000..aed22b225b61 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/illegal_names.sol @@ -0,0 +1,17 @@ +function this() {} +function super() {} +function _() {} + +contract C { + function test() public { + this(); + super(); + _(); + } +} +// ---- +// DeclarationError 3726: (0-18): The name "this" is reserved. +// DeclarationError 3726: (19-38): The name "super" is reserved. +// DeclarationError 3726: (39-54): The name "_" is reserved. +// Warning 2319: (0-18): This declaration shadows a builtin symbol. +// Warning 2319: (19-38): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol new file mode 100644 index 000000000000..2d9a3011b655 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol @@ -0,0 +1,8 @@ +function f() returns (uint) { C.S storage t; t.x; } + +contract C { + struct S { uint x; } +} +// ---- +// Warning 6321: (22-26): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// TypeError 3464: (45-46): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol new file mode 100644 index 000000000000..c41693e97fca --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol @@ -0,0 +1,3 @@ +function f(S storage g) view returns (uint) { S storage t = g; return t.x; } +struct S { uint x; } +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol new file mode 100644 index 000000000000..ff6061c65f8e --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + super; +} +// ---- +// DeclarationError 7576: (33-38): Undeclared identifier. "super" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol new file mode 100644 index 000000000000..cdea708d45d2 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + this; +} +// ---- +// DeclarationError 7576: (33-37): Undeclared identifier. "this" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/functionCalls/calloptions_on_staticcall.sol b/test/libsolidity/syntaxTests/functionCalls/calloptions_on_staticcall.sol new file mode 100644 index 000000000000..e128f03f1f21 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/calloptions_on_staticcall.sol @@ -0,0 +1,10 @@ +contract C { + function foo() pure internal { + address(10).staticcall{value: 7, gas: 3}(""); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError 2842: (56-96): Cannot set option "value" for staticcall. +// Warning 9302: (56-100): Return value of low-level calls not used. diff --git a/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol b/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol index 8185304d1521..4356efcf95d5 100644 --- a/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol +++ b/test/libsolidity/syntaxTests/functionCalls/calloptions_repeated.sol @@ -11,9 +11,8 @@ contract C { // ==== // EVMVersion: >=constantinople // ---- -// TypeError 9886: (78-110): Option "gas" has already been set. -// TypeError 9886: (120-154): Option "gas" has already been set. -// TypeError 9886: (164-198): Option "value" has already been set. -// TypeError 9886: (208-249): Option "value" has already been set. -// TypeError 9886: (208-249): Option "gas" has already been set. -// TypeError 9886: (259-286): Option "salt" has already been set. +// TypeError 1645: (78-110): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (120-154): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (164-198): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (208-249): Function call options have already been set, you have to combine them into a single {...}-option. +// TypeError 1645: (259-286): Function call options have already been set, you have to combine them into a single {...}-option. diff --git a/test/libsolidity/syntaxTests/functionCalls/msg_value_non_payable.sol b/test/libsolidity/syntaxTests/functionCalls/msg_value_non_payable.sol new file mode 100644 index 000000000000..281e748823e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/msg_value_non_payable.sol @@ -0,0 +1,7 @@ +contract C { + function get() public view returns(uint256) { + return msg.value; + } +} +// ---- +// TypeError 5887: (78-87): "msg.value" and "callvalue()" can only be used in payable public functions. Make the function "payable" or use an internal function to avoid this error. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_duplicate_parameter.sol similarity index 80% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol rename to test/libsolidity/syntaxTests/functionCalls/named_arguments_duplicate_parameter.sol index 98587ac60082..17ee8dc4508f 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_duplicate_parameter.sol @@ -8,4 +8,5 @@ contract test { } // ---- // Warning 2519: (31-37): This declaration shadows an existing declaration. +// Warning 2519: (39-45): This declaration shadows an existing declaration. // TypeError 6995: (159-160): Duplicate named argument "a". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/101_empty_in_named_args.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_empty.sol similarity index 82% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/101_empty_in_named_args.sol rename to test/libsolidity/syntaxTests/functionCalls/named_arguments_empty.sol index f00ae0227125..0c4da5c7317d 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/101_empty_in_named_args.sol +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_empty.sol @@ -8,4 +8,5 @@ contract test { } // ---- // Warning 2519: (31-37): This declaration shadows an existing declaration. +// Warning 2519: (39-45): This declaration shadows an existing declaration. // TypeError 6160: (153-158): Wrong argument count for function call: 0 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_in_any_order.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_in_any_order.sol new file mode 100644 index 000000000000..695bd5ac50f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_in_any_order.sol @@ -0,0 +1,13 @@ +contract C { + function f(uint u, string memory s, bool b) internal {} + + function call() public { + f({s: "abc", u: 1, b: true}); + f({s: "abc", b: true, u: 1}); + f({u: 1, s: "abc", b: true}); + f({b: true, s: "abc", u: 1}); + f({u: 1, b: true, s: "abc"}); + f({b: true, u: 1, s: "abc"}); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_invalid_name.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_invalid_name.sol new file mode 100644 index 000000000000..0c19ff416c09 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_invalid_name.sol @@ -0,0 +1,13 @@ +contract test { + function f(uint a, bool b, bytes memory c, uint d, bool e) public returns (uint r) { + if (b && !e) + r = a + d; + else + r = c.length; + } + function g() public returns (uint r) { + r = f({c: "abc", x: 1, e: 2, a: 11, b: 12}); + } +} +// ---- +// TypeError 4974: (249-288): Named argument "x" does not match function declaration. diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_ambiguous.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_ambiguous.sol new file mode 100644 index 000000000000..85acad30615b --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_ambiguous.sol @@ -0,0 +1,11 @@ +contract C { + function f(uint x, string memory y, bool z) internal {} + function f(string memory y, uint x, bool z) internal {} + function f(bool z, string memory y, uint x) internal {} + + function call() internal { + f({x: 1, y: "abc", z: true}); + } +} +// ---- +// TypeError 4487: (233-234): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_ambiguous_implicit_conversion.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_ambiguous_implicit_conversion.sol new file mode 100644 index 000000000000..4e44c54e1f51 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_ambiguous_implicit_conversion.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint x, string memory y) internal {} + function f(bytes memory y, int x) internal {} + + function call() internal { + f({x: 1, y: "abc"}); + } +} +// ---- +// TypeError 4487: (155-156): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_right_names_wrong_order.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_right_names_wrong_order.sol new file mode 100644 index 000000000000..fc2b413abbb1 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_right_names_wrong_order.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint x, string memory y, bool z) internal {} + function f(uint x, uint y, uint z) internal {} + + function call() internal { + f({y: 1, x: "abc", z: true}); + } +} +// ---- +// TypeError 9322: (164-165): No matching declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_wrong_names.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_wrong_names.sol new file mode 100644 index 000000000000..951de8678e4d --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_failing_wrong_names.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint x, string memory y, bool z) internal {} + function f(uint x, uint y, uint z) internal {} + + function call() internal { + f({a: 1, b: "abc", c: true}); + } +} +// ---- +// TypeError 9322: (164-165): No matching declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_in_any_order.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_in_any_order.sol new file mode 100644 index 000000000000..e6ba976d0298 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_overload_in_any_order.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint u, string memory s, bool b) internal {} + function f(uint u, uint s, uint b) internal {} + + function call() public { + f({s: "abc", u: 1, b: true}); + f({s: "abc", b: true, u: 1}); + f({u: 1, s: "abc", b: true}); + f({b: true, s: "abc", u: 1}); + f({u: 1, b: true, s: "abc"}); + f({b: true, u: 1, s: "abc"}); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/100_error_count_in_named_args.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_wrong_count.sol similarity index 82% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/100_error_count_in_named_args.sol rename to test/libsolidity/syntaxTests/functionCalls/named_arguments_wrong_count.sol index 5e8c4f40fb62..5acc2b44437c 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/100_error_count_in_named_args.sol +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_wrong_count.sol @@ -8,4 +8,5 @@ contract test { } // ---- // Warning 2519: (31-37): This declaration shadows an existing declaration. +// Warning 2519: (39-45): This declaration shadows an existing declaration. // TypeError 6160: (153-162): Wrong argument count for function call: 1 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/functionCalls/new_library.sol b/test/libsolidity/syntaxTests/functionCalls/new_library.sol new file mode 100644 index 000000000000..da410f73b361 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/new_library.sol @@ -0,0 +1,8 @@ +library L {} +contract C { + function f() public pure { + new L(); + } +} +// ---- +// TypeError 1130: (63-64): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/functionCalls/staticcall_on_homestead.sol b/test/libsolidity/syntaxTests/functionCalls/staticcall_on_homestead.sol new file mode 100644 index 000000000000..dc5797c912b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/staticcall_on_homestead.sol @@ -0,0 +1,10 @@ +contract C { + function f() public { + (bool success, ) = address(10).staticcall{gas: 3}(""); + success; + } +} +// ==== +// EVMVersion: X) public m; } // ---- -// TypeError 2763: (88-118): The following types are only supported for getters in ABIEncoderV2: struct C.Y memory. Either remove "public" or use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 2763: (108-138): The following types are only supported for getters in ABI coder v2: struct C.Y memory. Either remove "public" or use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/globalFunctions/log_deprecate.sol b/test/libsolidity/syntaxTests/globalFunctions/log_deprecate.sol new file mode 100644 index 000000000000..4418c619890b --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/log_deprecate.sol @@ -0,0 +1,15 @@ +contract C { + function f() public { + log0; + log1; + log2; + log3; + log4; + } +} +// ---- +// DeclarationError 7576: (38-42): Undeclared identifier. +// DeclarationError 7576: (46-50): Undeclared identifier. +// DeclarationError 7576: (54-58): Undeclared identifier. +// DeclarationError 7576: (62-66): Undeclared identifier. +// DeclarationError 7576: (70-74): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/iceRegressionTests/large_struct_array.sol b/test/libsolidity/syntaxTests/iceRegressionTests/large_struct_array.sol index 0f6f49f637ad..411518710665 100644 --- a/test/libsolidity/syntaxTests/iceRegressionTests/large_struct_array.sol +++ b/test/libsolidity/syntaxTests/iceRegressionTests/large_struct_array.sol @@ -1,5 +1,5 @@ // Used to cause ICE because of a too strict assert -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; T[222222222222222222222222222] sub; } struct T { uint[] x; } diff --git a/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol b/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol index 15118322fa48..761e60a1a873 100644 --- a/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol +++ b/test/libsolidity/syntaxTests/iceRegressionTests/oversized_var.sol @@ -4,11 +4,17 @@ contract b { } c d; + function e() public view { c storage x = d; - x.a[0]; + x.a[0]; + function()[3**44] storage fs; } } // ---- -// Warning 3408: (66-69): Variable "d" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 2332: (110-111): Type "b.c" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (66-67): Type struct b.c covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (66-67): Type uint256[14474011154664524427946373126085988481658748083205070504932198000989141204992] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (111-112): Type struct b.c covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (111-112): Type uint256[14474011154664524427946373126085988481658748083205070504932198000989141204992] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (152-169): Type function ()[984770902183611232881] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 2072: (152-180): Unused local variable. diff --git a/test/libsolidity/syntaxTests/immutable/complex.sol b/test/libsolidity/syntaxTests/immutable/complex.sol new file mode 100644 index 000000000000..b624430ea741 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/complex.sol @@ -0,0 +1,8 @@ +contract A { + int immutable a; + constructor() { a = 5; } + function f() public { a += 7; } +} + +// ---- +// TypeError 1581: (83-84): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol index b4808b844525..052a1b59746a 100644 --- a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol +++ b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 4599: (86-87): Immutable variables must be initialized unconditionally, not in an if statement. +// TypeError 4599: (86-87): Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol index f91e98333285..e95bf57403fb 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol @@ -9,4 +9,4 @@ contract C { } } // ---- -// TypeError 1581: (119-120): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (119-120): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol index 3f24614563d2..0a966f7c37b3 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol @@ -9,4 +9,4 @@ contract C { function f(uint a) internal pure {} } // ---- -// TypeError 1581: (59-60): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (59-60): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol index 14f47364a626..5970ab247adf 100644 --- a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// TypeError 1581: (102-103): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (102-103): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/decrement.sol b/test/libsolidity/syntaxTests/immutable/decrement.sol index 3c34dd3b7696..5717f05c81fb 100644 --- a/test/libsolidity/syntaxTests/immutable/decrement.sol +++ b/test/libsolidity/syntaxTests/immutable/decrement.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { x--; } } // ---- -// TypeError 7733: (67-68): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (63-64): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/delete.sol b/test/libsolidity/syntaxTests/immutable/delete.sol index 58e9235f3ce9..541fceb53fd5 100644 --- a/test/libsolidity/syntaxTests/immutable/delete.sol +++ b/test/libsolidity/syntaxTests/immutable/delete.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { delete x; } } // ---- -// TypeError 7733: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (70-71): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol b/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol new file mode 100644 index 000000000000..859a30a77c90 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/delete_and_initialize.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x = 3; + constructor() { + delete x; + } +} +// ---- +// TypeError 1574: (74-75): Immutable state variable already initialized. +// TypeError 7733: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol index 629d80f6692d..cc175d0831d5 100644 --- a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol @@ -10,5 +10,4 @@ contract C is B(C.f) { function f() internal returns(uint) { return x = 2; } } // ---- -// TypeError 1581: (200-201): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (200-201): Immutable state variable already initialized. +// TypeError 1581: (200-201): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/illegal_names.sol b/test/libsolidity/syntaxTests/immutable/illegal_names.sol new file mode 100644 index 000000000000..a80083ed8ba5 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/illegal_names.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable super; + uint immutable _; + uint immutable this; +} +// ---- +// DeclarationError 3726: (17-37): The name "super" is reserved. +// DeclarationError 3726: (43-59): The name "_" is reserved. +// DeclarationError 3726: (65-84): The name "this" is reserved. +// Warning 2319: (17-37): This declaration shadows a builtin symbol. +// Warning 2319: (65-84): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/immutable/increment.sol b/test/libsolidity/syntaxTests/immutable/increment.sol index 00c20422ca6a..11959ebc778f 100644 --- a/test/libsolidity/syntaxTests/immutable/increment.sol +++ b/test/libsolidity/syntaxTests/immutable/increment.sol @@ -1,8 +1,8 @@ contract C { - uint immutable x = 3; + uint immutable x; constructor() { x++; } } // ---- -// TypeError 7733: (67-68): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (63-64): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/increment_decrement.sol b/test/libsolidity/syntaxTests/immutable/increment_decrement.sol new file mode 100644 index 000000000000..ccfaf1920cad --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/increment_decrement.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + uint immutable y; + constructor() { + ++x; + --y; + } +} +// ---- +// TypeError 7733: (77-78): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. +// TypeError 7733: (86-87): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol index 695319378e9c..bc084400e60b 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol @@ -11,4 +11,4 @@ contract C is B { constructor() B(y = 3) { } } // ---- -// TypeError 1581: (148-149): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (148-149): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol index 8e3d671454b9..503f182d6cd2 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol @@ -10,4 +10,4 @@ contract C is B(C.y = 3) { uint immutable y; } // ---- -// TypeError 1581: (104-107): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (104-107): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol index 4f431474b3ed..748d9da55bc2 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol @@ -8,5 +8,4 @@ contract C is B { } } // ---- -// TypeError 7484: (88-89): Immutable variables must be initialized in the constructor of the contract they are defined in. -// TypeError 1574: (88-89): Immutable state variable already initialized. +// TypeError 7484: (88-89): Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in. diff --git a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol index b831f0f0b88b..d8c8ca47fe5e 100644 --- a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol +++ b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 6672: (88-89): Immutable variables can only be initialized once, not in a while statement. +// TypeError 6672: (88-89): Cannot write to immutable here: Immutable variables cannot be initialized inside a loop. diff --git a/test/libsolidity/syntaxTests/immutable/unary.sol b/test/libsolidity/syntaxTests/immutable/unary.sol new file mode 100644 index 000000000000..f3c75c09b632 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/unary.sol @@ -0,0 +1,8 @@ +contract A { + int immutable a; + constructor() { a = 5; } + function f() public { --a; } +} + +// ---- +// TypeError 1581: (85-86): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol index 2b5614650666..8eac18c7a173 100644 --- a/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol +++ b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol @@ -1,7 +1,7 @@ contract C { uint immutable x = 1; - function readX() internal view returns(uint) { + function readX() internal pure returns(uint) { return x + 3; } } diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol new file mode 100644 index 000000000000..73fe5980189c --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_already.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable z = 2; + uint immutable x = z = y = 3; + uint immutable y = 5; +} +// ---- +// TypeError 1581: (62-63): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError 1581: (66-67): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol new file mode 100644 index 000000000000..2c98ffd791c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/variable_declaration_value.sol @@ -0,0 +1,5 @@ +contract C { + int immutable x = x = 5; +} +// ---- +// TypeError 1581: (35-36): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol index 05d366ead4bb..4b2b91a15f41 100644 --- a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol @@ -6,5 +6,4 @@ contract C { } } // ---- -// TypeError 1581: (76-77): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (76-77): Immutable state variable already initialized. +// TypeError 1581: (76-77): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol index cdfd47443bf3..d450a37d2076 100644 --- a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol @@ -8,5 +8,4 @@ contract C { } } // ---- -// TypeError 1581: (111-112): Immutable variables can only be initialized inline or assigned directly in the constructor. -// TypeError 1574: (111-112): Immutable state variable already initialized. +// TypeError 1581: (111-112): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/imports/importing_free_functions.sol b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol new file mode 100644 index 000000000000..02bc3673e829 --- /dev/null +++ b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol @@ -0,0 +1,14 @@ +==== Source: a ==== +function f(uint x) pure returns (uint) { return x * 3; } +==== Source: b ==== +import "a" as A; +function g(uint x) pure returns (uint) { return A.f(x) * 3; } +==== Source: c ==== +import "b" as B; +contract C { + function f() public pure { + B.g(2); + B.A.f(3); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_match.sol b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_match.sol index 01ac5fdc30e5..c4eca74fbc85 100644 --- a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_match.sol +++ b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_match.sol @@ -1,5 +1,5 @@ ==== Source: A.sol ==== -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract A { @@ -8,12 +8,12 @@ contract A function f(S memory _s) public returns (S memory,S memory) { } } ==== Source: B.sol ==== -pragma experimental ABIEncoderV2; +pragma abicoder v2; import "./A.sol"; contract B is A { } ==== Source: C.sol ==== -pragma experimental ABIEncoderV2; +pragma abicoder v2; import "./B.sol"; contract C is B { } diff --git a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol index 214de78d15ba..891f57e8d334 100644 --- a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol +++ b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol @@ -1,5 +1,5 @@ ==== Source: A.sol ==== -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract A { @@ -8,12 +8,13 @@ contract A function f(S memory _s) public returns (S memory,S memory) { } } ==== Source: B.sol ==== -pragma experimental ABIEncoderV2; +pragma abicoder v2; import "./A.sol"; contract B is A { } ==== Source: C.sol ==== +pragma abicoder v1; import "./B.sol"; contract C is B { } // ---- -// TypeError 6594: (C.sol:18-37): Contract "C" does not use ABIEncoderV2 but wants to inherit from a contract which uses types that require it. Use "pragma experimental ABIEncoderV2;" for the inheriting contract as well to enable the feature. +// TypeError 6594: (C.sol:38-57): Contract "C" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol index 07b9cdefe6c7..c92221f3accd 100644 --- a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol +++ b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol @@ -1,5 +1,5 @@ ==== Source: A.sol ==== -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract A { @@ -8,10 +8,12 @@ contract A function f(S memory _s) public returns (S memory,S memory) { } } ==== Source: B.sol ==== +pragma abicoder v1; import "./A.sol"; contract B is A { } ==== Source: C.sol ==== +pragma abicoder v1; import "./B.sol"; contract C is B { } // ---- -// TypeError 6594: (B.sol:18-37): Contract "B" does not use ABIEncoderV2 but wants to inherit from a contract which uses types that require it. Use "pragma experimental ABIEncoderV2;" for the inheriting contract as well to enable the feature. +// TypeError 6594: (B.sol:38-57): Contract "B" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/imports/simple_alias.sol b/test/libsolidity/syntaxTests/imports/simple_alias.sol index 988fa7ae3863..361b27d7026f 100644 --- a/test/libsolidity/syntaxTests/imports/simple_alias.sol +++ b/test/libsolidity/syntaxTests/imports/simple_alias.sol @@ -1,4 +1,4 @@ ==== Source: a ==== contract A {} ==== Source: dir/a/b/c ==== -import "../../.././a" as x; contract B is x.A { fallback() external { x.A r = x.A(20); r; } } +import "../../.././a" as x; contract B is x.A { fallback() external { x.A r = x.A(address(20)); r; } } diff --git a/test/libsolidity/syntaxTests/imports/transitive.sol b/test/libsolidity/syntaxTests/imports/transitive.sol index e6a5771e997b..eab02b6f6431 100644 --- a/test/libsolidity/syntaxTests/imports/transitive.sol +++ b/test/libsolidity/syntaxTests/imports/transitive.sol @@ -4,7 +4,7 @@ struct S { uint[2] mS; } import "a" as A; struct T { A.S[2] mT; } ==== Source: c ==== -pragma experimental ABIEncoderV2; +pragma abicoder v2; import "b" as B; contract C { function f(B.T memory y, B.A.S memory z) public pure returns (uint, uint) { diff --git a/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol b/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol index ace6dbddade7..b0d4dbb107c1 100644 --- a/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol +++ b/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol @@ -5,7 +5,7 @@ contract C { } } // ---- -// TypeError 7407: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. -// TypeError 7407: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError 7407: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. +// TypeError 7407: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. // TypeError 7407: (81-90): Type rational_const 9485...(73 digits omitted)...5712 / 5 is not implicitly convertible to expected type uint256. // TypeError 6318: (65-91): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol b/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol index e7701e7ab054..0f18ec4d28b9 100644 --- a/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol +++ b/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol @@ -5,7 +5,7 @@ contract C { } } // ---- -// TypeError 7407: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. -// TypeError 7407: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. -// TypeError 7407: (81-90): Type int_const -189...(75 digits omitted)...1423 is not implicitly convertible to expected type uint256. +// TypeError 7407: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. +// TypeError 7407: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. +// TypeError 7407: (81-90): Type int_const -189...(75 digits omitted)...1423 is not implicitly convertible to expected type uint256. Cannot implicitly convert signed literal to unsigned type. // TypeError 6318: (65-91): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/array_negative_index.sol b/test/libsolidity/syntaxTests/indexing/array_negative_index.sol index 17ea84088bce..810ad3b73780 100644 --- a/test/libsolidity/syntaxTests/indexing/array_negative_index.sol +++ b/test/libsolidity/syntaxTests/indexing/array_negative_index.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// TypeError 7407: (67-69): Type int_const -1 is not implicitly convertible to expected type uint256. +// TypeError 7407: (67-69): Type int_const -1 is not implicitly convertible to expected type uint256. Cannot implicitly convert signed literal to unsigned type. diff --git a/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol b/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol index 086cd42a4670..c1a62ffb4337 100644 --- a/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol +++ b/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// TypeError 7407: (67-178): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. +// TypeError 7407: (67-178): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol index aeb5fc873ae2..4d6e8545e880 100644 --- a/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol @@ -5,5 +5,5 @@ contract C { } } // ---- -// TypeError 7407: (58-60): Type int_const -1 is not implicitly convertible to expected type uint256. +// TypeError 7407: (58-60): Type int_const -1 is not implicitly convertible to expected type uint256. Cannot implicitly convert signed literal to unsigned type. // TypeError 6318: (56-61): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol index 2b874eab2333..c73064245593 100644 --- a/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol @@ -5,5 +5,5 @@ contract C { } } // ---- -// TypeError 7407: (58-169): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. +// TypeError 7407: (58-169): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. // TypeError 6318: (56-170): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/index_range_access_assert.sol b/test/libsolidity/syntaxTests/indexing/index_range_access_assert.sol new file mode 100644 index 000000000000..acce33091923 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/index_range_access_assert.sol @@ -0,0 +1,5 @@ +// Used to trigger assert +contract s{} +function f() {s[:][];} +// ---- +// TypeError 1760: (53-57): Types cannot be sliced. diff --git a/test/libsolidity/syntaxTests/indexing/struct_array_noninteger_index.sol b/test/libsolidity/syntaxTests/indexing/struct_array_noninteger_index.sol index 93f81abbde02..93222ec939f4 100644 --- a/test/libsolidity/syntaxTests/indexing/struct_array_noninteger_index.sol +++ b/test/libsolidity/syntaxTests/indexing/struct_array_noninteger_index.sol @@ -1,10 +1,10 @@ contract test { - struct s { uint a; uint b;} - function f() pure public returns (byte) { + struct s { uint a; uint b;} + function f() pure public returns (bytes1) { s[75555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555]; s[7]; } } // ---- -// TypeError 7407: (101-241): Type int_const 7555...(132 digits omitted)...5555 is not implicitly convertible to expected type uint256. +// TypeError 7407: (106-246): Type int_const 7555...(132 digits omitted)...5555 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. diff --git a/test/libsolidity/syntaxTests/inheritance/base_not_contract.sol b/test/libsolidity/syntaxTests/inheritance/base_not_contract.sol new file mode 100644 index 000000000000..8d12b6e0747f --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/base_not_contract.sol @@ -0,0 +1,5 @@ +function fun() {} + +contract C is fun {} +// ---- +// TypeError 8758: (33-36): Contract expected. diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol index e422ac280b9c..e7783672463d 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; interface I { struct S { int a; } function f(S calldata) external pure; diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol index 4ae4403bb50d..65afb8cc6134 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract A { uint dummy; struct S { int a; } diff --git a/test/libsolidity/syntaxTests/inheritance/override/override_library.sol b/test/libsolidity/syntaxTests/inheritance/override/override_library.sol new file mode 100644 index 000000000000..1a8828fb2755 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/override_library.sol @@ -0,0 +1,6 @@ +library L {} +contract C { + function f() public override (L) {} +} +// ---- +// TypeError 1130: (57-58): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol index d02284aeff95..f3e886552e5d 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_mapping_to_dynamic_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; abstract contract C { struct S { diff --git a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol index 4e6e02598729..3a0fce375df0 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/public_var_override_struct_with_memory_element.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; abstract contract C { struct S { diff --git a/test/libsolidity/syntaxTests/inheritance/super_on_external.sol b/test/libsolidity/syntaxTests/inheritance/super_on_external.sol index 1030beab4e18..1fd7483c2811 100644 --- a/test/libsolidity/syntaxTests/inheritance/super_on_external.sol +++ b/test/libsolidity/syntaxTests/inheritance/super_on_external.sol @@ -7,4 +7,4 @@ contract B is A { } } // ---- -// TypeError 9582: (123-130): Member "f" not found or not visible after argument-dependent lookup in contract super B. +// TypeError 9582: (123-130): Member "f" not found or not visible after argument-dependent lookup in type(contract super B). diff --git a/test/libsolidity/syntaxTests/inheritance/virtual/modifier_virtual_err.sol b/test/libsolidity/syntaxTests/inheritance/virtual/modifier_virtual_err.sol new file mode 100644 index 000000000000..85824c86b776 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/virtual/modifier_virtual_err.sol @@ -0,0 +1,7 @@ +library test { + modifier m virtual; + function f() m public { + } +} +// ---- +// TypeError 3275: (19-38): Modifiers in a library cannot be virtual. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype3.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype3.sol new file mode 100644 index 000000000000..014a4dd455cd --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype3.sol @@ -0,0 +1,11 @@ +function ff() {} + +contract C { + function f() public pure { + assembly { + let x := ff + } + } +} +// ---- +// DeclarationError 2025: (90-92): Access to functions is not allowed in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_opcode_like.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_opcode_like.sol new file mode 100644 index 000000000000..cb675ab29128 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_opcode_like.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + uint mload; + assembly { + let x := mload + } + } +} +// ---- +// ParserError 7104: (104-109): Builtin function "mload" must be called. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_opcode_like.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_opcode_like.sol new file mode 100644 index 000000000000..51028e50c52b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_opcode_like.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + uint mload; + assembly { + mload := 1 + } + } +} +// ---- +// ParserError 6272: (101-103): Cannot assign to builtin function "mload". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/evm_byzantium_on_homestead.sol b/test/libsolidity/syntaxTests/inlineAssembly/evm_byzantium_on_homestead.sol index bbb32299f3d0..4c652511687b 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/evm_byzantium_on_homestead.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/evm_byzantium_on_homestead.sol @@ -14,7 +14,7 @@ contract C { // ==== // EVMVersion: =homestead // ---- -// TypeError 7756: (86-100): The "returndatasize" instruction is only available for Byzantium-compatible VMs (you are currently compiling for "homestead"). +// TypeError 4778: (86-100): The "returndatasize" instruction is only available for Byzantium-compatible VMs (you are currently compiling for "homestead"). // DeclarationError 3812: (77-102): Variable count mismatch: 1 variables and 0 values. // TypeError 7756: (115-129): The "returndatacopy" instruction is only available for Byzantium-compatible VMs (you are currently compiling for "homestead"). // TypeError 1503: (245-255): The "staticcall" instruction is only available for Byzantium-compatible VMs (you are currently compiling for "homestead"). diff --git a/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople.sol b/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople.sol index 75d2f2895e76..2aff4351ae8b 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople.sol @@ -11,6 +11,11 @@ contract C { ret := create2(0, 0, 0, 0) } } + function h() view external returns (bytes32 ret) { + assembly { + ret := extcodehash(address()) + } + } } // ==== // EVMVersion: >=constantinople diff --git a/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople_on_byzantium.sol b/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople_on_byzantium.sol index 1e6d85bf1df0..781cdc3f71a6 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople_on_byzantium.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/evm_constantinople_on_byzantium.sol @@ -11,15 +11,22 @@ contract C { ret := create2(0, 0, 0, 0) } } + function h() view external returns (bytes32 ret) { + assembly { + ret := extcodehash(address()) + } + } } // ==== // EVMVersion: =byzantium // ---- // TypeError 6612: (103-106): The "shl" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). // DeclarationError 8678: (96-116): Variable count does not match number of values (1 vs. 0) -// TypeError 6612: (136-139): The "shr" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). +// TypeError 7458: (136-139): The "shr" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). // DeclarationError 8678: (129-147): Variable count does not match number of values (1 vs. 0) -// TypeError 6612: (167-170): The "sar" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). +// TypeError 2054: (167-170): The "sar" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). // DeclarationError 8678: (160-178): Variable count does not match number of values (1 vs. 0) // TypeError 6166: (283-290): The "create2" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). // DeclarationError 8678: (276-302): Variable count does not match number of values (1 vs. 0) +// TypeError 7110: (412-423): The "extcodehash" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). +// DeclarationError 8678: (405-434): Variable count does not match number of values (1 vs. 0) diff --git a/test/libsolidity/syntaxTests/inlineAssembly/evm_istanbul.sol b/test/libsolidity/syntaxTests/inlineAssembly/evm_istanbul.sol index 5e6334749cf1..f28f3ca0d4d9 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/evm_istanbul.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/evm_istanbul.sol @@ -1,5 +1,5 @@ contract C { - function f() pure external returns (uint id) { + function f() view external returns (uint id) { assembly { id := chainid() } diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol new file mode 100644 index 000000000000..c842060ff4cb --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_definition.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + assembly { + function f (a, b , c ) -> y,x,z { + } + } + } +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol new file mode 100644 index 000000000000..d3c56f3e2ea1 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_definition_whitespace.sol @@ -0,0 +1,10 @@ +contract C { + function f() pure public { + assembly { + function f (a, b , c ) - > y,x,z { + } + } + } +} +// ---- +// ParserError 2314: (87-88): Expected '{' but got '-' diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol new file mode 100644 index 000000000000..b51dd8604731 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_assignment.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + let x := hex"0011" + } + } +} +// ---- +// ParserError 3772: (72-81): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol new file mode 100644 index 000000000000..2f4f69174e01 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_expression.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + pop(hex"2233") + } + } +} +// ---- +// ParserError 3772: (67-76): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol new file mode 100644 index 000000000000..de64567d7b5b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/hex_switch_case.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + switch codesize() + case hex"00" {} + case hex"1122" {} + } + } +} +// ---- +// ParserError 3772: (92-99): Hex literals are not valid in this context. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/bare_instructions_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/bare_instructions_disallowed.sol index a55586af964c..87884efeecde 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/bare_instructions_disallowed.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/bare_instructions_disallowed.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// ParserError 2314: (95-98): Expected '(' but got identifier +// ParserError 6913: (95-98): Call or assignment expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array.sol new file mode 100644 index 000000000000..5262b68ecc5e --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint[] calldata bytesAsCalldata) external { + assembly { + let x := bytesAsCalldata + } + } +} +// ---- +// TypeError 1397: (112-127): Call data elements cannot be accessed directly. Use ".offset" and ".length" to access the calldata offset and length of this array and then use "calldatacopy". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array_offset.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array_offset.sol new file mode 100644 index 000000000000..b6a2844f7144 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_array_offset.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint[] calldata bytesAsCalldata) external pure { + assembly { + let x := bytesAsCalldata.offset + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_slot.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_slot.sol new file mode 100644 index 000000000000..4af7fa91e371 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_slot.sol @@ -0,0 +1,9 @@ +contract C { + function f(bytes calldata bytesAsCalldata) external { + assembly { + let x := bytesAsCalldata.slot + } + } +} +// ---- +// TypeError 1536: (111-131): Calldata variables only support ".offset" and ".length". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol index a7d9da8df032..07562ad272e0 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/calldata_variables.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 2370: (111-126): Call data elements cannot be accessed directly. Copy to a local variable first or use "calldataload" or "calldatacopy" with manually determined offsets and sizes. +// TypeError 1397: (111-126): Call data elements cannot be accessed directly. Use ".offset" and ".length" to access the calldata offset and length of this array and then use "calldatacopy". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol new file mode 100644 index 000000000000..9d9b35e284e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/dup_disallowed.sol @@ -0,0 +1,43 @@ +contract C { + function f() pure public { + assembly { + dup0() + dup1() + dup2() + dup3() + dup4() + dup5() + dup6() + dup7() + dup8() + dup9() + dup10() + dup11() + dup12() + dup13() + dup14() + dup15() + dup16() + dup32() + } + } +} +// ---- +// DeclarationError 4619: (75-79): Function not found. +// DeclarationError 4619: (94-98): Function not found. +// DeclarationError 4619: (113-117): Function not found. +// DeclarationError 4619: (132-136): Function not found. +// DeclarationError 4619: (151-155): Function not found. +// DeclarationError 4619: (170-174): Function not found. +// DeclarationError 4619: (189-193): Function not found. +// DeclarationError 4619: (208-212): Function not found. +// DeclarationError 4619: (227-231): Function not found. +// DeclarationError 4619: (246-250): Function not found. +// DeclarationError 4619: (265-270): Function not found. +// DeclarationError 4619: (285-290): Function not found. +// DeclarationError 4619: (305-310): Function not found. +// DeclarationError 4619: (325-330): Function not found. +// DeclarationError 4619: (345-350): Function not found. +// DeclarationError 4619: (365-370): Function not found. +// DeclarationError 4619: (385-390): Function not found. +// DeclarationError 4619: (405-410): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/illegal_names.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/illegal_names.sol new file mode 100644 index 000000000000..6c41b22964ab --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/illegal_names.sol @@ -0,0 +1,55 @@ +contract C { + function f() public { + // reserved function names + assembly { + function this() { + } + function super() { + } + function _() { + } + } + + // reserved names as function argument + assembly { + function a(this) { + } + function b(super) { + } + function c(_) { + } + } + + // reserved names as function return parameter + assembly { + function d() -> this { + } + function g() -> super { + } + function c() -> _ { + } + } + + // reserved names as variable declaration + assembly { + let this := 1 + let super := 1 + let _ := 1 + } + } +} +// ---- +// DeclarationError 4113: (105-136): The identifier name "this" is reserved. +// DeclarationError 4113: (149-181): The identifier name "super" is reserved. +// DeclarationError 4113: (194-222): The identifier name "_" is reserved. +// DeclarationError 4113: (323-327): The identifier name "this" is reserved. +// DeclarationError 4113: (368-373): The identifier name "super" is reserved. +// DeclarationError 4113: (414-415): The identifier name "_" is reserved. +// DeclarationError 4113: (546-550): The identifier name "this" is reserved. +// DeclarationError 4113: (595-600): The identifier name "super" is reserved. +// DeclarationError 4113: (645-646): The identifier name "_" is reserved. +// DeclarationError 4113: (759-763): The identifier name "this" is reserved. +// DeclarationError 3859: (759-763): This declaration shadows a declaration outside the inline assembly block. +// DeclarationError 4113: (785-790): The identifier name "super" is reserved. +// DeclarationError 3859: (785-790): This declaration shadows a declaration outside the inline assembly block. +// DeclarationError 4113: (812-813): The identifier name "_" is reserved. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol index b894fc6b834a..1ba49bc39364 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// ParserError 1856: (72-73): Literal or identifier expected. +// ParserError 1465: (72-73): Illegal token: Octal numbers not allowed. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol new file mode 100644 index 000000000000..7da213776557 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/jumpdest_disallowed.sol @@ -0,0 +1,9 @@ +contract C { + function f() pure public { + assembly { + jumpdest() + } + } +} +// ---- +// DeclarationError 4619: (75-83): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol new file mode 100644 index 000000000000..72e3fcd985d3 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/push_disallowed.sol @@ -0,0 +1,73 @@ +contract C { + function f() pure public { + assembly { + push0() + push1() + push2() + push3() + push4() + push5() + push6() + push7() + push8() + push9() + push10() + push11() + push12() + push13() + push14() + push15() + push16() + push17() + push18() + push19() + push20() + push21() + push22() + push23() + push24() + push25() + push26() + push27() + push28() + push29() + push30() + push31() + push32() + } + } +} +// ---- +// DeclarationError 4619: (75-80): Function not found. +// DeclarationError 4619: (95-100): Function not found. +// DeclarationError 4619: (115-120): Function not found. +// DeclarationError 4619: (135-140): Function not found. +// DeclarationError 4619: (155-160): Function not found. +// DeclarationError 4619: (175-180): Function not found. +// DeclarationError 4619: (195-200): Function not found. +// DeclarationError 4619: (215-220): Function not found. +// DeclarationError 4619: (235-240): Function not found. +// DeclarationError 4619: (255-260): Function not found. +// DeclarationError 4619: (275-281): Function not found. +// DeclarationError 4619: (296-302): Function not found. +// DeclarationError 4619: (317-323): Function not found. +// DeclarationError 4619: (338-344): Function not found. +// DeclarationError 4619: (359-365): Function not found. +// DeclarationError 4619: (380-386): Function not found. +// DeclarationError 4619: (401-407): Function not found. +// DeclarationError 4619: (422-428): Function not found. +// DeclarationError 4619: (443-449): Function not found. +// DeclarationError 4619: (464-470): Function not found. +// DeclarationError 4619: (485-491): Function not found. +// DeclarationError 4619: (506-512): Function not found. +// DeclarationError 4619: (527-533): Function not found. +// DeclarationError 4619: (548-554): Function not found. +// DeclarationError 4619: (569-575): Function not found. +// DeclarationError 4619: (590-596): Function not found. +// DeclarationError 4619: (611-617): Function not found. +// DeclarationError 4619: (632-638): Function not found. +// DeclarationError 4619: (653-659): Function not found. +// DeclarationError 4619: (674-680): Function not found. +// DeclarationError 4619: (695-701): Function not found. +// DeclarationError 4619: (716-722): Function not found. +// DeclarationError 4619: (737-743): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol index f833d1740cb8..b01f1ee21fbe 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment.sol @@ -7,4 +7,4 @@ contract test { } } // ---- -// TypeError 1408: (89-90): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes. +// TypeError 1408: (89-90): Only local variables are supported. To access storage variables, use the ".slot" and ".offset" suffixes. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol index a86fdc14b78d..64cfc862362a 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_assignment_in_modifier.sol @@ -10,4 +10,4 @@ contract test { } } // ---- -// TypeError 1408: (80-81): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes. +// TypeError 1408: (80-81): Only local variables are supported. To access storage variables, use the ".slot" and ".offset" suffixes. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_nonslot.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_nonslot.sol new file mode 100644 index 000000000000..ba0ef195ca98 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/storage_nonslot.sol @@ -0,0 +1,12 @@ +contract test { + uint x = 1; + function f() public { + assembly { + let t := x.length + x.length := 2 + } + } +} +// ---- +// TypeError 4656: (98-106): State variables only support ".slot" and ".offset". +// TypeError 4656: (119-127): State variables only support ".slot" and ".offset". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol new file mode 100644 index 000000000000..278398218b63 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/swap_disallowed.sol @@ -0,0 +1,43 @@ +contract C { + function f() pure public { + assembly { + swap0() + swap1() + swap2() + swap3() + swap4() + swap5() + swap6() + swap7() + swap8() + swap9() + swap10() + swap11() + swap12() + swap13() + swap14() + swap15() + swap16() + swap32() + } + } +} +// ---- +// DeclarationError 4619: (75-80): Function not found. +// DeclarationError 4619: (95-100): Function not found. +// DeclarationError 4619: (115-120): Function not found. +// DeclarationError 4619: (135-140): Function not found. +// DeclarationError 4619: (155-160): Function not found. +// DeclarationError 4619: (175-180): Function not found. +// DeclarationError 4619: (195-200): Function not found. +// DeclarationError 4619: (215-220): Function not found. +// DeclarationError 4619: (235-240): Function not found. +// DeclarationError 4619: (255-260): Function not found. +// DeclarationError 4619: (275-281): Function not found. +// DeclarationError 4619: (296-302): Function not found. +// DeclarationError 4619: (317-323): Function not found. +// DeclarationError 4619: (338-344): Function not found. +// DeclarationError 4619: (359-365): Function not found. +// DeclarationError 4619: (380-386): Function not found. +// DeclarationError 4619: (401-407): Function not found. +// DeclarationError 4619: (422-428): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_negative_stack.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_negative_stack.sol index 3f92beec2af8..80dd3a58755c 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_negative_stack.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_negative_stack.sol @@ -6,4 +6,4 @@ contract test { } } // ---- -// ParserError 2314: (85-86): Expected '(' but got '}' +// ParserError 6913: (85-86): Call or assignment expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol index 9f51fe572ca6..b1f8ba435ec9 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/unbalanced_two_stack_load.sol @@ -5,4 +5,4 @@ contract c { } } // ---- -// TypeError 1408: (75-76): Only local variables are supported. To access storage variables, use the .slot and .offset suffixes. +// TypeError 1408: (75-76): Only local variables are supported. To access storage variables, use the ".slot" and ".offset" suffixes. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/linkersymbol_function.sol b/test/libsolidity/syntaxTests/inlineAssembly/linkersymbol_function.sol index 9d1d6c4f376c..7434ddfd5845 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/linkersymbol_function.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/linkersymbol_function.sol @@ -8,3 +8,4 @@ contract C { } } // ---- +// DeclarationError 5017: (63-90): The identifier "linkersymbol" is reserved and can not be used. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers.sol b/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers.sol new file mode 100644 index 000000000000..6660887d02ef --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { + let linkersymbol := 1 + let datacopy := 1 + let swap16 := 1 + } + } +} +// ---- +// DeclarationError 5017: (67-79): The identifier "linkersymbol" is reserved and can not be used. +// DeclarationError 5017: (95-103): The identifier "datacopy" is reserved and can not be used. +// DeclarationError 5017: (119-125): The identifier "swap16" is reserved and can not be used. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers_byzantium.sol b/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers_byzantium.sol new file mode 100644 index 000000000000..b6cdf4540d77 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers_byzantium.sol @@ -0,0 +1,16 @@ +contract C { + function f() public pure { + assembly { + let shl := 1 + } + assembly { + pop(shl(1, 2)) + } + } +} +// ==== +// EVMVersion: =byzantium +// ---- +// DeclarationError 5017: (67-70): The identifier "shl" is reserved and can not be used. +// TypeError 6612: (107-110): The "shl" instruction is only available for Constantinople-compatible VMs (you are currently compiling for "byzantium"). +// TypeError 3950: (107-116): Expected expression to evaluate to one value, but got 0 values instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers_constantinople.sol b/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers_constantinople.sol new file mode 100644 index 000000000000..cea698fd1f37 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/reserved_identifiers_constantinople.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure { + assembly { + let shl := 1 + } + assembly { + pop(shl(1, 2)) + } + } +} +// ==== +// EVMVersion: >=constantinople +// ---- +// ParserError 5568: (67-70): Cannot use builtin function name "shl" as identifier name. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/shadowing/variable_by_opcode.sol b/test/libsolidity/syntaxTests/inlineAssembly/shadowing/variable_by_opcode.sol new file mode 100644 index 000000000000..a18abe90bb0f --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/shadowing/variable_by_opcode.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure { + uint mload; + } + function g() public pure { + uint mload; + assembly { + } + } +} +// ---- +// Warning 8261: (109-119): Variable is shadowed in inline assembly by an instruction of the same name +// Warning 2072: (52-62): Unused local variable. +// Warning 2072: (109-119): Unused local variable. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol new file mode 100644 index 000000000000..5467d045a814 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/solidity_keywords.sol @@ -0,0 +1,113 @@ +contract C { + function f() view public { + assembly { + // These are keywords of Solidity -- a copy from liblangutil/Token.h. + let abstract := 1 + let anonymous := 1 + let as := 1 + let assembly := 1 + // break is Yul keyword + let catch := 1 + let constant := 1 + let constructor := 1 + // continue is Yul keyword + let contract := 1 + let do := 1 + let else := 1 + let enum := 1 + let emit := 1 + let event := 1 + let external := 1 + let fallback := 1 + // for is a Yul keyword + // function is a Yul keyword + // hex is a Yul keyword + // if is a Yul keyword + let indexed := 1 + let interface := 1 + let internal := 1 + let immutable := 1 + let import := 1 + let is := 1 + let library := 1 + let mapping := 1 + let memory := 1 + let modifier := 1 + let new := 1 + let override := 1 + let payable := 1 + let public := 1 + let pragma := 1 + let private := 1 + let pure := 1 + let receive := 1 + // return is a builtin in EVMDialect + return(0, 0) + let returns := 1 + let storage := 1 + let calldata := 1 + let struct := 1 + let throw := 1 + let try := 1 + // type shadows the Solidity function + let unicode := 1 + let using := 1 + let view := 1 + let virtual := 1 + let while := 1 + let wei := 1 + let gwei := 1 + let ether := 1 + let seconds := 1 + let minutes := 1 + let hours := 1 + let days := 1 + let weeks := 1 + let years := 1 + let int := 1 + let uint := 1 + let bytes := 1 + // byte is a builtin in EVMDialect + pop(byte(1, 1)) + let string := 1 + // address is a builtin in EVMDialect + pop(address()) + let bool := 1 + let fixed := 1 + let ufixed := 1 + let after := 1 + let alias := 1 + let apply := 1 + let auto := 1 + // case is a Yul keyword + let copyof := 1 + // default is a Yul keyword + let define := 1 + let final := 1 + let implements := 1 + let in := 1 + let inline := 1 + // let is a Yul keyword + let macro := 1 + let match := 1 + let mutable := 1 + let null := 1 + let of := 1 + let partial := 1 + let promise := 1 + let reference := 1 + let relocatable := 1 + let sealed := 1 + let sizeof := 1 + let static := 1 + let supports := 1 + // switch is a Yul keyword + let typedef := 1 + let typeof := 1 + let unchecked := 1 + let var := 1 + } + } +} +// ---- +// Warning 5740: (955-2168): Unreachable code. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol index 91ea7aea4d91..47f898019eff 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// TypeError 9068: (118-119): You have to use the .slot or .offset suffix to access storage reference variables. +// TypeError 9068: (118-119): You have to use the ".slot" or ".offset" suffix to access storage reference variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol index 4b50fbd6ea45..d2ca4d7cbfb0 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_old.sol @@ -9,5 +9,5 @@ contract C { } } // ---- -// DeclarationError 9467: (118-124): Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables. -// DeclarationError 9467: (142-150): Identifier not found. Use ``.slot`` and ``.offset`` to access storage variables. +// DeclarationError 9467: (118-124): Identifier not found. Use ".slot" and ".offset" to access storage variables. +// DeclarationError 9467: (142-150): Identifier not found. Use ".slot" and ".offset" to access storage variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol index b5ac0b1d8a10..66bf81e6f5fc 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// TypeError 7944: (84-90): The suffixes .offset and .slot can only be used on storage variables. +// TypeError 7944: (84-90): The suffixes ".offset", ".slot" and ".length" can only be used with variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol index d85bc8c280cc..a319c597c737 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol @@ -9,5 +9,5 @@ contract C { } } // ---- -// TypeError 3622: (117-123): The suffixes .offset and .slot can only be used on storage variables. -// TypeError 3622: (141-149): The suffixes .offset and .slot can only be used on storage variables. +// TypeError 3622: (117-123): The suffix ".slot" is not supported by this variable or type. +// TypeError 3622: (141-149): The suffix ".offset" is not supported by this variable or type. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul b/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul new file mode 100644 index 000000000000..d068a72e36c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/string_literal_switch_case.yul @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + switch codesize() + case "1" {} + case "2" {} + } + } +} diff --git a/test/libsolidity/syntaxTests/inlineAssembly/two_stack_slot_access.sol b/test/libsolidity/syntaxTests/inlineAssembly/two_stack_slot_access.sol new file mode 100644 index 000000000000..5b71b61f906a --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/two_stack_slot_access.sol @@ -0,0 +1,10 @@ +contract C { + function f() pure external { + function() external two_stack_slots; + assembly { + let x := two_stack_slots + } + } +} +// ---- +// TypeError 9857: (132-147): Only types that use one stack slot are supported. diff --git a/test/libsolidity/syntaxTests/largeTypes/large_storage_array_mapping.sol b/test/libsolidity/syntaxTests/largeTypes/large_storage_array_mapping.sol index 2dfc50a415f2..08f8a46b3291 100644 --- a/test/libsolidity/syntaxTests/largeTypes/large_storage_array_mapping.sol +++ b/test/libsolidity/syntaxTests/largeTypes/large_storage_array_mapping.sol @@ -2,4 +2,4 @@ contract C { mapping(uint => uint[2**100]) x; } // ---- -// Warning 7325: (17-46): Type "uint256[1267650600228229401496703205376]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (17-46): Type uint256[1267650600228229401496703205376] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/largeTypes/large_storage_array_simple.sol b/test/libsolidity/syntaxTests/largeTypes/large_storage_array_simple.sol index 3f7499ca2ed9..3fc91da4d1d8 100644 --- a/test/libsolidity/syntaxTests/largeTypes/large_storage_array_simple.sol +++ b/test/libsolidity/syntaxTests/largeTypes/large_storage_array_simple.sol @@ -2,4 +2,4 @@ contract C { uint[2**64] x; } // ---- -// Warning 3408: (17-30): Variable "x" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (17-28): Type uint256[18446744073709551616] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/largeTypes/large_storage_arrays_struct.sol b/test/libsolidity/syntaxTests/largeTypes/large_storage_arrays_struct.sol index 285b99deb305..e2488adc0b62 100644 --- a/test/libsolidity/syntaxTests/largeTypes/large_storage_arrays_struct.sol +++ b/test/libsolidity/syntaxTests/largeTypes/large_storage_arrays_struct.sol @@ -3,4 +3,4 @@ contract C { S[2**20] x; } // ---- -// Warning 3408: (64-74): Variable "x" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (64-72): Type struct C.S[1048576] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/largeTypes/large_storage_structs.sol b/test/libsolidity/syntaxTests/largeTypes/large_storage_structs.sol index a29401d948ef..cbc912a35008 100644 --- a/test/libsolidity/syntaxTests/largeTypes/large_storage_structs.sol +++ b/test/libsolidity/syntaxTests/largeTypes/large_storage_structs.sol @@ -55,14 +55,20 @@ contract C { Q4 q4; } // ---- -// Warning 3408: (106-111): Variable "s0" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 3408: (171-176): Variable "s1" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 7325: (341-343): Type "C.P[103]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 7325: (341-343): Type "C.P[104]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 3408: (505-510): Variable "q0" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 7325: (505-507): Type "uint256[100000000000000000002]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 7325: (505-507): Type "uint256[100000000000000000004]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 3408: (576-581): Variable "q1" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 7325: (647-649): Type "uint256[100000000000000000006]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 3408: (715-720): Variable "q3" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 7325: (783-785): Type "uint256[100000000000000000008]" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (106-108): Type struct C.S0 covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (106-108): Type struct C.P[101] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (171-173): Type struct C.S1 covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (171-173): Type struct C.P[102] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (341-343): Type struct C.P[103] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (341-343): Type struct C.P[104] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (505-507): Type struct C.Q0 covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (505-507): Type uint256[1][][100000000000000000001] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (505-507): Type uint256[][100000000000000000003] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (505-507): Type uint256[100000000000000000004] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (505-507): Type uint256[100000000000000000002] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (576-578): Type struct C.Q1 covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (576-578): Type uint256[1][][100000000000000000005] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (647-649): Type uint256[100000000000000000006] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (715-717): Type struct C.Q3 covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (715-717): Type uint256[][100000000000000000007] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (783-785): Type uint256[100000000000000000008] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/largeTypes/oversized_array.sol b/test/libsolidity/syntaxTests/largeTypes/oversized_array.sol index 123773403a2c..4bf6c86be0eb 100644 --- a/test/libsolidity/syntaxTests/largeTypes/oversized_array.sol +++ b/test/libsolidity/syntaxTests/largeTypes/oversized_array.sol @@ -4,5 +4,5 @@ contract C { uint[2**255][2] a; } // ---- -// TypeError 7676: (60-97): Contract too large for storage. +// TypeError 7676: (60-97): Contract requires too much storage. // TypeError 1534: (77-94): Type too large for storage. diff --git a/test/libsolidity/syntaxTests/largeTypes/oversized_contract.sol b/test/libsolidity/syntaxTests/largeTypes/oversized_contract.sol index 44c7274e6468..f6c82a8235fc 100644 --- a/test/libsolidity/syntaxTests/largeTypes/oversized_contract.sol +++ b/test/libsolidity/syntaxTests/largeTypes/oversized_contract.sol @@ -5,6 +5,4 @@ contract C { uint[2**255] b; } // ---- -// TypeError 7676: (60-114): Contract too large for storage. -// Warning 3408: (77-91): Variable "a" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 3408: (97-111): Variable "b" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// TypeError 7676: (60-114): Contract requires too much storage. diff --git a/test/libsolidity/syntaxTests/largeTypes/oversized_contract_inheritance.sol b/test/libsolidity/syntaxTests/largeTypes/oversized_contract_inheritance.sol index 24073d4c0316..a181e3a58134 100644 --- a/test/libsolidity/syntaxTests/largeTypes/oversized_contract_inheritance.sol +++ b/test/libsolidity/syntaxTests/largeTypes/oversized_contract_inheritance.sol @@ -7,6 +7,4 @@ contract D is C { uint[2**255] b; } // ---- -// TypeError 7676: (95-134): Contract too large for storage. -// Warning 3408: (77-91): Variable "a" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. -// Warning 3408: (117-131): Variable "b" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// TypeError 7676: (95-134): Contract requires too much storage. diff --git a/test/libsolidity/syntaxTests/largeTypes/oversized_struct.sol b/test/libsolidity/syntaxTests/largeTypes/oversized_struct.sol index 757ec9e4afc8..d1c8187568d6 100644 --- a/test/libsolidity/syntaxTests/largeTypes/oversized_struct.sol +++ b/test/libsolidity/syntaxTests/largeTypes/oversized_struct.sol @@ -8,5 +8,5 @@ contract C { S s; } // ---- -// TypeError 7676: (60-152): Contract too large for storage. +// TypeError 7676: (60-152): Contract requires too much storage. // TypeError 1534: (146-149): Type too large for storage. diff --git a/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol b/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol index b32815028923..836a49b034e7 100644 --- a/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol +++ b/test/libsolidity/syntaxTests/largeTypes/storage_parameter.sol @@ -3,4 +3,5 @@ contract C { function f(S storage) internal {} } // ---- -// Warning 2332: (64-65): Type "C.S" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (64-65): Type struct C.S covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (64-65): Type uint256[57896044618658097711785492504343953926634992332820282019728792003956564819968] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_1.sol b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_1.sol new file mode 100644 index 000000000000..5feb89256e3c --- /dev/null +++ b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_1.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF + bytes memory s = unicode"underflow ‬"; + } +} +// ---- +// ParserError 8936: (88-106): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_2.sol b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_2.sol new file mode 100644 index 000000000000..409bc4795330 --- /dev/null +++ b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_2.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF PDF + bytes memory m = unicode"underflow ‬‬"; + } +} +// ---- +// ParserError 8936: (92-110): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_3.sol b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_3.sol new file mode 100644 index 000000000000..6641ca3fcab8 --- /dev/null +++ b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_3.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO + bytes memory m = unicode"overflow ‮"; + } +} +// ---- +// ParserError 8936: (88-108): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_4.sol b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_4.sol new file mode 100644 index 000000000000..a6d6e664c2cc --- /dev/null +++ b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_4.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // RLO RLO + bytes memory m = unicode"overflow ‮‮"; + } +} +// ---- +// ParserError 8936: (92-115): Mismatching directional override markers in comment or string literal. diff --git a/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_5.sol b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_5.sol new file mode 100644 index 000000000000..5aa21c5bbc4c --- /dev/null +++ b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_5.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure + { + // RLO PDF + bytes memory m = unicode" ok ‮‬"; + + // RLO RLO PDF PDF + m = unicode" ok ‮‮‬‬"; + + // RLO RLO RLO PDF PDF PDF + m = unicode" ok ‮‮‮‬‬‬"; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_6.sol b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_6.sol new file mode 100644 index 000000000000..70e7b8043487 --- /dev/null +++ b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_6.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure + { + // PDF RLO + bytes memory m = unicode" underflow ‬‮"; + } +} +// ---- +// ParserError 8936: (92-111): Unicode direction override underflow in comment or string literal. diff --git a/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_7.sol b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_7.sol new file mode 100644 index 000000000000..f10d823a6bb4 --- /dev/null +++ b/test/libsolidity/syntaxTests/literals/unicode_string_direction_override_7.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure + { + // LRO PDF RLO PDF + bytes memory m = unicode"‭ ok ‬‮‬"; + + // lre rle pdf pdf + m = unicode"lre‪ rle‫ pdf‬ pdf‬"; + // lre lro pdf pdf + m = unicode"lre‪ lro‭ pdf‬ pdf‬"; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/lvalues/calldata_member_access.sol b/test/libsolidity/syntaxTests/lvalues/calldata_member_access.sol index 42c3a3f97be0..dc6e9290a29a 100644 --- a/test/libsolidity/syntaxTests/lvalues/calldata_member_access.sol +++ b/test/libsolidity/syntaxTests/lvalues/calldata_member_access.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint256 x; } function f(S calldata s) external pure { diff --git a/test/libsolidity/syntaxTests/lvalues/lvalue_not_set.sol b/test/libsolidity/syntaxTests/lvalues/lvalue_not_set.sol new file mode 100644 index 000000000000..ab514ebc5115 --- /dev/null +++ b/test/libsolidity/syntaxTests/lvalues/lvalue_not_set.sol @@ -0,0 +1,11 @@ +contract C { + function foo(uint x) public + { + // Used to cause an ICE + uint p = new uint[] = x; + } +} +// ---- +// TypeError 4247: (100-110): Expression has to be an lvalue. +// TypeError 7407: (113-114): Type uint256 is not implicitly convertible to expected type function (uint256) pure returns (uint256[] memory). +// TypeError 9574: (91-114): Type function (uint256) pure returns (uint256[] memory) is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/memberLookup/constructor_as_potential_library_member.sol b/test/libsolidity/syntaxTests/memberLookup/constructor_as_potential_library_member.sol new file mode 100644 index 000000000000..71b63faa55ff --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/constructor_as_potential_library_member.sol @@ -0,0 +1,4 @@ +library L{ constructor() { L.x; } } +// ---- +// TypeError 7634: (11-33): Constructor cannot be defined in libraries. +// TypeError 9582: (27-30): Member "x" not found or not visible after argument-dependent lookup in type(library L). diff --git a/test/libsolidity/syntaxTests/memberLookup/msg_sender_non_payable_send.sol b/test/libsolidity/syntaxTests/memberLookup/msg_sender_non_payable_send.sol new file mode 100644 index 000000000000..661544ad7f58 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/msg_sender_non_payable_send.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + (msg.sender).send(10); + } +} +// ---- +// TypeError 9862: (47-64): "send" and "transfer" are only available for objects of type "address payable", not "address". diff --git a/test/libsolidity/syntaxTests/memberLookup/msg_sender_non_payable_transfer.sol b/test/libsolidity/syntaxTests/memberLookup/msg_sender_non_payable_transfer.sol new file mode 100644 index 000000000000..f44e0489f110 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/msg_sender_non_payable_transfer.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + (msg.sender).transfer(10); + } +} +// ---- +// TypeError 9862: (47-68): "send" and "transfer" are only available for objects of type "address payable", not "address". diff --git a/test/libsolidity/syntaxTests/memberLookup/tx_origin_non_payable_send.sol b/test/libsolidity/syntaxTests/memberLookup/tx_origin_non_payable_send.sol new file mode 100644 index 000000000000..b96568395339 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/tx_origin_non_payable_send.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + (tx.origin).send(10); + } +} +// ---- +// TypeError 9862: (47-63): "send" and "transfer" are only available for objects of type "address payable", not "address". diff --git a/test/libsolidity/syntaxTests/memberLookup/tx_origin_non_payable_transfer.sol b/test/libsolidity/syntaxTests/memberLookup/tx_origin_non_payable_transfer.sol new file mode 100644 index 000000000000..34f60ebae933 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/tx_origin_non_payable_transfer.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + (tx.origin).transfer(10); + } +} +// ---- +// TypeError 9862: (47-67): "send" and "transfer" are only available for objects of type "address payable", not "address". diff --git a/test/libsolidity/syntaxTests/memberLookup/unused_module_member_reference.sol b/test/libsolidity/syntaxTests/memberLookup/unused_module_member_reference.sol new file mode 100644 index 000000000000..5fb2e5645a84 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/unused_module_member_reference.sol @@ -0,0 +1,15 @@ +==== Source: s1.sol ==== +import "s1.sol" as A; + +library L { + function f() internal pure {} +} + +contract C +{ + function test() public pure { + A.L; + } +} +// ---- +// Warning 6133: (s1.sol:127-130): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol new file mode 100644 index 000000000000..e7b3a6845b9f --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol @@ -0,0 +1,13 @@ +contract Other { + function f(uint) public pure returns (uint) {} +} +contract SuperTest is Other { + function creationSuper() public pure returns (bytes memory) { + return type(super).creationCode; + } + function runtimeOther() public pure returns (bytes memory) { + return type(super).runtimeCode; + } +} +// ---- +// TypeError 4259: (177-182): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract super SuperTest) provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol b/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol new file mode 100644 index 000000000000..328870447b67 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol @@ -0,0 +1,17 @@ +interface ERC165 { + /// @notice Query if a contract implements an interface + /// @param interfaceID The interface identifier, as specified in ERC-165 + /// @dev Interface identification is specified in ERC-165. This function + /// uses less than 30,000 gas. + /// @return `true` if the contract implements `interfaceID` and + /// `interfaceID` is not 0xffffffff, `false` otherwise + function supportsInterface(bytes4 interfaceID) external view returns (bool); +} + +abstract contract Test is ERC165 { + function hello() public pure returns (bytes4 data){ + return type(super).interfaceID; + } +} +// ---- +// TypeError 4259: (592-597): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract super Test) provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol index baa4a228698e..91cd21e49c5a 100644 --- a/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol +++ b/test/libsolidity/syntaxTests/metaTypes/name_other_contract.sol @@ -2,6 +2,20 @@ contract Test { function f() public pure returns (string memory) { return type(C).name; } + function g() public pure returns (string memory) { + return type(A).name; + } + function h() public pure returns (string memory) { + return type(I).name; + } +} + +abstract contract A { + function f() virtual public pure; +} + +interface I { + function f() external pure; } contract C { diff --git a/test/libsolidity/syntaxTests/metaTypes/super_name.sol b/test/libsolidity/syntaxTests/metaTypes/super_name.sol new file mode 100644 index 000000000000..120c60dd991d --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/super_name.sol @@ -0,0 +1,53 @@ +pragma experimental ABIEncoderV2; + +function compareStrings(string memory s1, string memory s2) returns (bool) { + return keccak256(abi.encodePacked(s1)) == keccak256(abi.encodePacked(s2)); +} + +contract A { + string[] r; + function f() public virtual returns (bool) { + r.push(""); + + return false; + } +} + + +contract B is A { + function f() public virtual override returns (bool) { + super.f(); + r.push(type(super).name); + + return false; + } +} + + +contract C is A { + function f() public virtual override returns (bool) { + super.f(); + r.push(type(super).name); + + return false; + } +} + + +contract D is B, C { + function f() public override(B, C) returns (bool) { + super.f(); + r.push(type(super).name); + // Order of calls: D.f, C.f, B.f, A.f + // r contains "", "A", "B", "C" + assert(r.length == 4); + assert(compareStrings(r[0], "")); + assert(compareStrings(r[1], "A")); + assert(compareStrings(r[2], "B")); + assert(compareStrings(r[3], "C")); + + return true; + } +} +// ---- +// TypeError 4259: (440-445): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract super B) provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol index bd434fc26609..53b2d819e57d 100644 --- a/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol +++ b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol @@ -4,4 +4,5 @@ contract Test { } } // ---- +// Warning 6321: (54-66): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. // Warning 6133: (78-88): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol index 4c21e80547a8..c28d2fe05f28 100644 --- a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol +++ b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol @@ -5,4 +5,3 @@ contract Test { } // ---- // TypeError 4259: (65-75): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract Test) provided. -// TypeError 4259: (60-76): Invalid type for argument in the function call. A contract type or an integer type is required, but tuple() provided. diff --git a/test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol b/test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol index 0ba307b8f6ff..32ae4045b87a 100644 --- a/test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol +++ b/test/libsolidity/syntaxTests/missing_functions_duplicate_bug.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Ownable { address private _owner; diff --git a/test/libsolidity/syntaxTests/modifiers/access_in_library.sol b/test/libsolidity/syntaxTests/modifiers/access_in_library.sol new file mode 100644 index 000000000000..052c60bdedbb --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/access_in_library.sol @@ -0,0 +1,9 @@ +library L { + modifier m() { _; } +} +contract C { + function f() L.m public { + } +} +// ---- +// TypeError 9428: (68-71): Can only use modifiers defined in the current contract or in base contracts. diff --git a/test/libsolidity/syntaxTests/modifiers/cross_contract_access.sol b/test/libsolidity/syntaxTests/modifiers/cross_contract_access.sol new file mode 100644 index 000000000000..430b8afe1ffc --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/cross_contract_access.sol @@ -0,0 +1,9 @@ +contract C { + modifier m() { _; } +} +contract D { + function f() C.m public { + } +} +// ---- +// TypeError 9428: (69-72): Can only use modifiers defined in the current contract or in base contracts. diff --git a/test/libsolidity/syntaxTests/modifiers/cross_contract_base.sol b/test/libsolidity/syntaxTests/modifiers/cross_contract_base.sol new file mode 100644 index 000000000000..391feb859cb4 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/cross_contract_base.sol @@ -0,0 +1,8 @@ +contract C { + modifier m() { _; } +} +contract D is C { + function f() C.m public { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/modifiers/cross_contract_unrelated.sol b/test/libsolidity/syntaxTests/modifiers/cross_contract_unrelated.sol new file mode 100644 index 000000000000..7ca8b9ca21c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/cross_contract_unrelated.sol @@ -0,0 +1,11 @@ +contract A {} +contract C is A { + modifier m() { _; } +} +contract D is A { + function f() C.m public { + } +} +contract T is D, C {} +// ---- +// TypeError 9428: (93-96): Can only use modifiers defined in the current contract or in base contracts. diff --git a/test/libsolidity/syntaxTests/modifiers/illegal_name.sol b/test/libsolidity/syntaxTests/modifiers/illegal_name.sol new file mode 100644 index 000000000000..e29122b74a42 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/illegal_name.sol @@ -0,0 +1,11 @@ +contract C { + modifier this { _; } + modifier super { _; } + modifier _ { _; } +} +// ---- +// DeclarationError 3726: (14-34): The name "this" is reserved. +// DeclarationError 3726: (36-57): The name "super" is reserved. +// DeclarationError 3726: (59-76): The name "_" is reserved. +// Warning 2319: (14-34): This declaration shadows a builtin symbol. +// Warning 2319: (36-57): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/modifiers/library_via_using.sol b/test/libsolidity/syntaxTests/modifiers/library_via_using.sol new file mode 100644 index 000000000000..bac0b33da729 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/library_via_using.sol @@ -0,0 +1,10 @@ +library L { + modifier m() { _; } +} +contract C { + using L for *; + function f() L.m public { + } +} +// ---- +// TypeError 9428: (87-90): Can only use modifiers defined in the current contract or in base contracts. diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_overrides_variable.sol b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_variable.sol new file mode 100644 index 000000000000..1f170601f351 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_variable.sol @@ -0,0 +1,5 @@ +contract A { modifier mod(uint a) { _; } } +contract B is A { uint public mod; } +// ---- +// DeclarationError 9097: (61-76): Identifier already declared. +// TypeError 1456: (61-76): Override changes modifier to public state variable. diff --git a/test/libsolidity/syntaxTests/multiSource/alias_shadows_another_alias.sol b/test/libsolidity/syntaxTests/multiSource/alias_shadows_another_alias.sol new file mode 100644 index 000000000000..55a7c29a699e --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/alias_shadows_another_alias.sol @@ -0,0 +1,19 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +function g() pure returns (uint) { return 42; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +==== Source: s3.sol ==== +// imports f()->1337 as g() +import "s2.sol"; +// imports f()->1337 as f() and +// g()->42 as g +import {f as f, g as g} from "s1.sol"; +contract C { + function foo() public pure returns (uint) { + // calls f()->1337 / f()->1337 + return f() / g(); + } +} +// ---- +// DeclarationError 1686: (s1.sol:0-49): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/multiSource/alias_shadows_function.sol b/test/libsolidity/syntaxTests/multiSource/alias_shadows_function.sol new file mode 100644 index 000000000000..d59e9d3d7eac --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/alias_shadows_function.sol @@ -0,0 +1,19 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +function g() pure returns (uint) { return 42; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +==== Source: s3.sol ==== +// imports f()->1337 as g() +import "s2.sol"; +// imports f()->1337 as f() and +// g()->42 as g +import "s1.sol"; +contract C { + function foo() public pure returns (uint) { + // calls f()->1337 / f()->1337 + return f() / g(); + } +} +// ---- +// DeclarationError 1686: (s1.sol:0-49): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/multiSource/circular_import.sol b/test/libsolidity/syntaxTests/multiSource/circular_import.sol new file mode 100644 index 000000000000..b853e211b56c --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/circular_import.sol @@ -0,0 +1,6 @@ +==== Source: s1.sol ==== +import {f as g} from "s2.sol"; +function f() pure returns (uint) { return 1; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +function f() pure returns (uint) { return 2; } diff --git a/test/libsolidity/syntaxTests/multiSource/circular_import_2.sol b/test/libsolidity/syntaxTests/multiSource/circular_import_2.sol new file mode 100644 index 000000000000..63b75aa4a81a --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/circular_import_2.sol @@ -0,0 +1,7 @@ +==== Source: s1.sol ==== +import {f as g, g as h} from "s2.sol"; +function f() pure returns (uint) { return h() - g(); } +==== Source: s2.sol ==== +import {f as h} from "s1.sol"; +function f() pure returns (uint) { return 2; } +function g() pure returns (uint) { return 4; } diff --git a/test/libsolidity/syntaxTests/multiSource/circular_import_3.sol b/test/libsolidity/syntaxTests/multiSource/circular_import_3.sol new file mode 100644 index 000000000000..2a8e7a47a60f --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/circular_import_3.sol @@ -0,0 +1,14 @@ +==== Source: s1.sol ==== +import {f as g, g as h} from "s2.sol"; +function f() pure returns (uint) { return h() - g(); } +==== Source: s2.sol ==== +import {f as h} from "s1.sol"; +function f() pure returns (uint) { return 2; } +function g() pure returns (uint) { return 4; } +==== Source: s3.sol ==== +import "s1.sol"; +import "s2.sol"; +// ---- +// DeclarationError 1686: (s1.sol:39-93): Function with same name and parameter types defined twice. +// DeclarationError 1686: (s2.sol:31-77): Function with same name and parameter types defined twice. +// DeclarationError 1686: (s2.sol:78-124): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/multiSource/circular_import_4.sol b/test/libsolidity/syntaxTests/multiSource/circular_import_4.sol new file mode 100644 index 000000000000..88bbcb6c0107 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/circular_import_4.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +import {f as g, g as h} from "s2.sol"; +function f() pure returns (uint) { return h() - g(); } +==== Source: s2.sol ==== +import {f as h} from "s1.sol"; +function f() pure returns (uint) { return 2; } +function g() pure returns (uint) { return 4; } +==== Source: s3.sol ==== +import "s1.sol"; diff --git a/test/libsolidity/syntaxTests/multiSource/circular_import_5.sol b/test/libsolidity/syntaxTests/multiSource/circular_import_5.sol new file mode 100644 index 000000000000..5cd3e988478a --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/circular_import_5.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +import {f as g, g as h} from "s2.sol"; +function f() pure returns (uint) { return h() - g(); } +==== Source: s2.sol ==== +import {f as h} from "s1.sol"; +function f() pure returns (uint) { return 2; } +function g() pure returns (uint) { return 4; } +==== Source: s3.sol ==== +import "s2.sol"; diff --git a/test/libsolidity/syntaxTests/multiSource/duplicate_import_statement.sol b/test/libsolidity/syntaxTests/multiSource/duplicate_import_statement.sol new file mode 100644 index 000000000000..948cc2becd52 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/duplicate_import_statement.sol @@ -0,0 +1,10 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +==== Source: s2.sol ==== +import {f as f} from "s1.sol"; +import {f as f} from "s1.sol"; +contract C { + function g() public pure returns (uint) { + return f(); + } +} diff --git a/test/libsolidity/syntaxTests/multiSource/free_different_interger_types.sol b/test/libsolidity/syntaxTests/multiSource/free_different_interger_types.sol new file mode 100644 index 000000000000..a8f47c9667f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/free_different_interger_types.sol @@ -0,0 +1,10 @@ +==== Source: s1.sol ==== +function f(uint) pure returns (uint) { return 24; } +function g() pure returns (bool) { return true; } +==== Source: s2.sol ==== +import {f as g, g as g} from "s1.sol"; +contract C { + function foo() public pure returns (uint, bool) { + return (g(2), g()); + } +} diff --git a/test/libsolidity/syntaxTests/multiSource/free_function_alias_different_parameter_types.sol b/test/libsolidity/syntaxTests/multiSource/free_function_alias_different_parameter_types.sol new file mode 100644 index 000000000000..2ff23c6bd454 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/free_function_alias_different_parameter_types.sol @@ -0,0 +1,19 @@ +==== Source: s1.sol ==== +function f() pure returns (uint16) { return 1337; } +function g() pure returns (uint8) { return 42; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +==== Source: s3.sol ==== +// imports f(uint16)->1337 as g(uint16) +import "s2.sol"; +// imports f(uint16)->1337 as f(uint16) and +// g(uint8)->42 as g(uint8) +import {f as f, g as g} from "s1.sol"; +contract C { + function foo() public pure returns (uint) { + // calls f()->1337 / f()->1337 + return f() / g(); + } +} +// ---- +// DeclarationError 1686: (s1.sol:0-51): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/multiSource/free_function_redefinition_base_derived.sol b/test/libsolidity/syntaxTests/multiSource/free_function_redefinition_base_derived.sol new file mode 100644 index 000000000000..0bb58b5d0517 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/free_function_redefinition_base_derived.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C {} +==== Source: s2.sol ==== +import "s1.sol"; +function f() pure returns (uint) { return 42; } +contract D is C {} +// ---- +// DeclarationError 1686: (s2.sol:17-64): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/multiSource/free_function_redefinition_transitive.sol b/test/libsolidity/syntaxTests/multiSource/free_function_redefinition_transitive.sol new file mode 100644 index 000000000000..944ae21631de --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/free_function_redefinition_transitive.sol @@ -0,0 +1,12 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C {} +==== Source: s2.sol ==== +import "s1.sol"; +contract D is C {} +==== Source: s3.sol ==== +import "s2.sol"; +function f() pure returns (uint) { return 42; } +contract E is D {} +// ---- +// DeclarationError 1686: (s3.sol:17-64): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/multiSource/free_function_resolution_override_virtual.sol b/test/libsolidity/syntaxTests/multiSource/free_function_resolution_override_virtual.sol new file mode 100644 index 000000000000..a3d3ccd9e805 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/free_function_resolution_override_virtual.sol @@ -0,0 +1,19 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +contract C { + function f() public pure virtual returns (uint) { + return f(); + } +} +==== Source: s2.sol ==== +import "s1.sol"; +function f() pure returns (uint) { return 42; } +contract D is C { + function f() public pure override returns (uint) { + return f(); + } +} +// ---- +// Warning 2519: (s1.sol:65-134): This declaration shadows an existing declaration. +// Warning 2519: (s2.sol:85-155): This declaration shadows an existing declaration. +// DeclarationError 1686: (s2.sol:17-64): Function with same name and parameter types defined twice. diff --git a/test/libsolidity/syntaxTests/multiSource/import_contract_function_error.sol b/test/libsolidity/syntaxTests/multiSource/import_contract_function_error.sol new file mode 100644 index 000000000000..c7b5601e3de4 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/import_contract_function_error.sol @@ -0,0 +1,10 @@ +==== Source: s1.sol ==== +contract C { + function f() public pure returns (uint) { + return 1337; + } +} +==== Source: s2.sol ==== +import {C.f as g} from "s1.sol"; +// ---- +// ParserError 2314: (s2.sol:9-10): Expected '}' but got '.' diff --git a/test/libsolidity/syntaxTests/multiSource/imported_free_function.sol b/test/libsolidity/syntaxTests/multiSource/imported_free_function.sol new file mode 100644 index 000000000000..afd511978c00 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/imported_free_function.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +==== Source: s2.sol ==== +import "s1.sol"; +contract C { + function g() public pure returns (uint) { + return f(); + } +} diff --git a/test/libsolidity/syntaxTests/multiSource/multiple_imports_same_function.sol b/test/libsolidity/syntaxTests/multiSource/multiple_imports_same_function.sol new file mode 100644 index 000000000000..a0dea2133296 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/multiple_imports_same_function.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +==== Source: s2.sol ==== +import {f as f, f as f, f as f} from "s1.sol"; +contract C { + function g() public pure returns (uint) { + return f(); + } +} diff --git a/test/libsolidity/syntaxTests/multiSource/reimport_imported_function.sol b/test/libsolidity/syntaxTests/multiSource/reimport_imported_function.sol new file mode 100644 index 000000000000..ce29d5a5556e --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/reimport_imported_function.sol @@ -0,0 +1,11 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +==== Source: s2.sol ==== +import {f as g} from "s1.sol"; +==== Source: s3.sol ==== +import {g as h} from "s2.sol"; +contract C { + function foo() public pure returns (uint) { + return h(); + } +} diff --git a/test/libsolidity/syntaxTests/multiSource/two_imports_same_function.sol b/test/libsolidity/syntaxTests/multiSource/two_imports_same_function.sol new file mode 100644 index 000000000000..0a05ea98d88b --- /dev/null +++ b/test/libsolidity/syntaxTests/multiSource/two_imports_same_function.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +function f() pure returns (uint) { return 1337; } +==== Source: s2.sol ==== +import {f as f, f as f} from "s1.sol"; +contract C { + function g() public pure returns (uint) { + return f(); + } +} diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol index 7db98577e7be..28c4cec5ce5a 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol @@ -4,3 +4,5 @@ contract C { a; } } +// ---- +// Warning 6321: (46-50): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol index ba6e99163e2f..b1ec065ac018 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol @@ -9,3 +9,7 @@ contract C { function g() internal pure returns (uint, uint, uint, D.S[20] storage x, uint) { x = x; } } // ---- +// Warning 6321: (176-180): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (182-186): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (188-192): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (213-217): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol index a2fcce182cd5..e6a0dbe80d86 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol @@ -10,3 +10,8 @@ contract C { function h() internal pure returns (bytes memory, string storage s) { s = s; } } // ---- +// Warning 6321: (51-55): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (57-61): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (63-67): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (69-73): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (250-262): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/018_forward_function_reference.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/018_forward_function_reference.sol index fd9ab7ed3540..d4c9a1882033 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/018_forward_function_reference.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/018_forward_function_reference.sol @@ -1,10 +1,12 @@ contract First { function fun() public returns (bool) { - return Second(1).fun(1, true, 3) > 0; + return Second(address(1)).fun(1, true, 3) > 0; } } contract Second { function fun(uint, bool, uint) public returns (uint) { - if (First(2).fun() == true) return 1; + if (First(address(2)).fun() == true) return 1; } } +// ---- +// Warning 6321: (192-196): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/039_functions_with_identical_structs_in_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/039_functions_with_identical_structs_in_interface.sol index 38fe146e894a..f750fae391fb 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/039_functions_with_identical_structs_in_interface.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/039_functions_with_identical_structs_in_interface.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S1 { int i; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/040_functions_with_different_structs_in_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/040_functions_with_different_structs_in_interface.sol index 291dd0800008..9bc0471bc4bc 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/040_functions_with_different_structs_in_interface.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/040_functions_with_different_structs_in_interface.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S1 { function() external a; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/041_functions_with_structs_of_non_external_types_in_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/041_functions_with_structs_of_non_external_types_in_interface.sol index 77f8dd2474b7..6dff9d8b2bb3 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/041_functions_with_structs_of_non_external_types_in_interface.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/041_functions_with_structs_of_non_external_types_in_interface.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { function() internal a; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_structs_of_non_external_types_in_interface_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_structs_of_non_external_types_in_interface_2.sol index 541dd0a043b1..297f809fd6dc 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_structs_of_non_external_types_in_interface_2.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_structs_of_non_external_types_in_interface_2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { mapping(uint => uint) a; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_stucts_of_non_external_types_in_interface_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_stucts_of_non_external_types_in_interface_2.sol index 541dd0a043b1..297f809fd6dc 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_stucts_of_non_external_types_in_interface_2.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_stucts_of_non_external_types_in_interface_2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { mapping(uint => uint) a; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_structs_of_non_external_types_in_interface_nested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_structs_of_non_external_types_in_interface_nested.sol index d2c96d4bc8d0..78660bdae485 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_structs_of_non_external_types_in_interface_nested.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_structs_of_non_external_types_in_interface_nested.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct T { mapping(uint => uint) a; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_stucts_of_non_external_types_in_interface_nested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_stucts_of_non_external_types_in_interface_nested.sol index d2c96d4bc8d0..78660bdae485 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_stucts_of_non_external_types_in_interface_nested.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_stucts_of_non_external_types_in_interface_nested.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct T { mapping(uint => uint) a; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/044_returning_multi_dimensional_arrays_new_abi.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/044_returning_multi_dimensional_arrays_new_abi.sol index aa41880b10bf..c266406d24e4 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/044_returning_multi_dimensional_arrays_new_abi.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/044_returning_multi_dimensional_arrays_new_abi.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f() public pure returns (string[][] memory) {} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol index bc92882a2de3..c9075a4cd3bf 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol @@ -1,5 +1,6 @@ +pragma abicoder v1; contract C { function f() public pure returns (string[][] memory) {} } // ---- -// TypeError 4957: (51-68): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 4957: (71-88): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol index d0e6ec0b6dc0..ee8c7f18d3c4 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol @@ -1,5 +1,6 @@ +pragma abicoder v1; contract C { function f() public pure returns (uint[][2] memory) {} } // ---- -// TypeError 4957: (51-67): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 4957: (71-87): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/047_returning_arrays_in_structs_new_abi.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/047_returning_arrays_in_structs_new_abi.sol index c130d62d97be..5b761aad2478 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/047_returning_arrays_in_structs_new_abi.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/047_returning_arrays_in_structs_new_abi.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { string[] s; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol index 7670da2b0250..77c77831e01d 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol @@ -1,6 +1,7 @@ +pragma abicoder v1; contract C { struct S { string[] s; } function f() public pure returns (S memory x) {} } // ---- -// TypeError 4957: (80-90): This type is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError 4957: (100-110): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol index 0eb051b7546b..f817fe28650e 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol @@ -1,4 +1,4 @@ -contract A { function f() public virtual { uint8 x = C(0).g(); } } +contract A { function f() public virtual { uint8 x = C(address(0)).g(); } } contract B { function f() public virtual {} function g() public returns (uint8) {} } contract C is A, B { function f() public override (A, B) { A.f(); } } // ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/063_implicit_derived_to_base_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/063_implicit_derived_to_base_conversion.sol index 627029634a09..d055b56adc2f 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/063_implicit_derived_to_base_conversion.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/063_implicit_derived_to_base_conversion.sol @@ -1,7 +1,7 @@ contract A { } contract B is A { - function f() public { A a = B(1); } + function f() public { A a = B(address(1)); } } // ---- // Warning 2072: (59-62): Unused local variable. -// Warning 2018: (37-72): Function state mutability can be restricted to pure +// Warning 2018: (37-81): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/064_implicit_base_to_derived_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/064_implicit_base_to_derived_conversion.sol index 5876633c359c..3ef4ca3c13b4 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/064_implicit_base_to_derived_conversion.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/064_implicit_base_to_derived_conversion.sol @@ -1,6 +1,6 @@ contract A { } contract B is A { - function f() public { B b = A(1); } + function f() public { B b = A(address(1)); } } // ---- -// TypeError 9574: (59-69): Type contract A is not implicitly convertible to expected type contract B. +// TypeError 9574: (59-78): Type contract A is not implicitly convertible to expected type contract B. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/065_super_excludes_current_contract.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/065_super_excludes_current_contract.sol index 62d4d7b3440e..d94f5bcc6d64 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/065_super_excludes_current_contract.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/065_super_excludes_current_contract.sol @@ -8,4 +8,4 @@ contract B is A { } } // ---- -// TypeError 9582: (95-102): Member "f" not found or not visible after argument-dependent lookup in contract super B. +// TypeError 9582: (95-102): Member "f" not found or not visible after argument-dependent lookup in type(contract super B). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol index 8beac9f1aa10..6ed769c88d1a 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol @@ -3,4 +3,4 @@ contract C { fallback(uint a) external { x = 2; } } // ---- -// TypeError 3978: (37-45): Fallback function cannot take parameters. +// TypeError 5570: (55-55): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol index 3ec82b390488..268a7e69b09f 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol @@ -2,4 +2,4 @@ contract C { fallback() external returns (uint) { } } // ---- -// TypeError 5570: (45-51): Fallback function can only have a single "bytes memory" return value. +// TypeError 5570: (45-51): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol index 4f89e69e0333..2f6ef6b7aa6b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol @@ -2,6 +2,6 @@ contract c { function f() public {} } contract d { - function g() public { c(0).f(); } + function g() public { c(address(0)).f(); } } // ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/097_access_to_internal_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/097_access_to_internal_function.sol index 13ba97f5a6aa..028947d63d02 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/097_access_to_internal_function.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/097_access_to_internal_function.sol @@ -2,7 +2,7 @@ contract c { function f() internal {} } contract d { - function g() public { c(0).f(); } + function g() public { c(address(0)).f(); } } // ---- -// TypeError 9582: (83-89): Member "f" not found or not visible after argument-dependent lookup in contract c. +// TypeError 9582: (83-98): Member "f" not found or not visible after argument-dependent lookup in contract c. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/098_access_to_default_state_variable_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/098_access_to_default_state_variable_visibility.sol index 5c14f2e66acb..efdedc4f1317 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/098_access_to_default_state_variable_visibility.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/098_access_to_default_state_variable_visibility.sol @@ -2,7 +2,7 @@ contract c { uint a; } contract d { - function g() public { c(0).a(); } + function g() public { c(address(0)).a(); } } // ---- -// TypeError 9582: (66-72): Member "a" not found or not visible after argument-dependent lookup in contract c. +// TypeError 9582: (66-81): Member "a" not found or not visible after argument-dependent lookup in contract c. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/099_access_to_internal_state_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/099_access_to_internal_state_variable.sol index b95949f7f79e..7aa77c13de3f 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/099_access_to_internal_state_variable.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/099_access_to_internal_state_variable.sol @@ -2,7 +2,7 @@ contract c { uint public a; } contract d { - function g() public { c(0).a(); } + function g() public { c(address(0)).a(); } } // ---- -// Warning 2018: (51-84): Function state mutability can be restricted to view +// Warning 2018: (51-93): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/103_invalid_parameter_names_in_named_args.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/103_invalid_parameter_names_in_named_args.sol deleted file mode 100644 index b3b8c727ca8e..000000000000 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/103_invalid_parameter_names_in_named_args.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract test { - function a(uint a, uint b) public returns (uint r) { - r = a + b; - } - function b() public returns (uint r) { - r = a({a: 1, c: 2}); - } -} -// ---- -// Warning 2519: (31-37): This declaration shadows an existing declaration. -// TypeError 4974: (153-168): Named argument "c" does not match function declaration. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/105_constant_input_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/105_constant_input_parameter.sol index 154b68496a72..aaf6f620a699 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/105_constant_input_parameter.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/105_constant_input_parameter.sol @@ -2,4 +2,4 @@ contract test { function f(uint[] memory constant a) public { } } // ---- -// DeclarationError 1788: (31-55): The "constant" keyword can only be used for state variables. +// DeclarationError 1788: (31-55): The "constant" keyword can only be used for state variables or variables at file level. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/111_overflow_caused_by_ether_units.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/111_overflow_caused_by_ether_units.sol index e0ac9fa5db3c..6cdda2514bd6 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/111_overflow_caused_by_ether_units.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/111_overflow_caused_by_ether_units.sol @@ -5,4 +5,4 @@ contract c { uint256 a; } // ---- -// TypeError 7407: (45-111): Type int_const 1157...(70 digits omitted)...0000 is not implicitly convertible to expected type uint256. +// TypeError 7407: (45-111): Type int_const 1157...(70 digits omitted)...0000 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/141_multiple_scopes_suggestions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/141_multiple_scopes_suggestions.sol index e71f3bd1709e..db870e19c602 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/141_multiple_scopes_suggestions.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/141_multiple_scopes_suggestions.sol @@ -6,4 +6,4 @@ contract c { } } // ---- -// DeclarationError 7576: (101-105): Undeclared identifier. Did you mean "log8", "log9", "log0", "log1", "log2", "log3" or "log4"? +// DeclarationError 7576: (101-105): Undeclared identifier. Did you mean "log8" or "log9"? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/158_storage_variable_initialization_with_incorrect_type_int.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/158_storage_variable_initialization_with_incorrect_type_int.sol index f2c36a00a9d5..0d0baf4002c6 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/158_storage_variable_initialization_with_incorrect_type_int.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/158_storage_variable_initialization_with_incorrect_type_int.sol @@ -2,4 +2,4 @@ contract c { uint8 a = 1000; } // ---- -// TypeError 7407: (27-31): Type int_const 1000 is not implicitly convertible to expected type uint8. +// TypeError 7407: (27-31): Type int_const 1000 is not implicitly convertible to expected type uint8. Literal is too large to fit in uint8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/160_test_byte_is_alias_of_byte1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/160_test_byte_is_alias_of_byte1.sol index ced9fac34d70..4ab89a3a212a 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/160_test_byte_is_alias_of_byte1.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/160_test_byte_is_alias_of_byte1.sol @@ -1,7 +1,7 @@ contract c { bytes arr; - function f() public { byte a = arr[0];} + function f() public { bytes1 a = arr[0];} } // ---- -// Warning 2072: (54-60): Unused local variable. -// Warning 2018: (32-71): Function state mutability can be restricted to view +// Warning 2072: (54-62): Unused local variable. +// Warning 2018: (32-73): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/168_assignment_to_const_var_involving_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/168_assignment_to_const_var_involving_conversion.sol index fb31e199d958..e9ba66fe13d4 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/168_assignment_to_const_var_involving_conversion.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/168_assignment_to_const_var_involving_conversion.sol @@ -1,3 +1,3 @@ contract C { - C constant x = C(0x123); + C constant x = C(address(0x123)); } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/190_negative_integers_to_signed_out_of_bound.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/190_negative_integers_to_signed_out_of_bound.sol index f56a4fcd451a..a4d84f2da352 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/190_negative_integers_to_signed_out_of_bound.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/190_negative_integers_to_signed_out_of_bound.sol @@ -2,4 +2,4 @@ contract test { int8 public i = -129; } // ---- -// TypeError 7407: (36-40): Type int_const -129 is not implicitly convertible to expected type int8. +// TypeError 7407: (36-40): Type int_const -129 is not implicitly convertible to expected type int8. Literal is too large to fit in int8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/192_positive_integers_to_signed_out_of_bound.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/192_positive_integers_to_signed_out_of_bound.sol index f62c51cdfaed..0bc4699b3cb5 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/192_positive_integers_to_signed_out_of_bound.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/192_positive_integers_to_signed_out_of_bound.sol @@ -2,4 +2,4 @@ contract test { int8 public j = 128; } // ---- -// TypeError 7407: (36-39): Type int_const 128 is not implicitly convertible to expected type int8. +// TypeError 7407: (36-39): Type int_const 128 is not implicitly convertible to expected type int8. Literal is too large to fit in int8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/194_negative_integers_to_unsigned.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/194_negative_integers_to_unsigned.sol index 0d04e87ea9b7..0dac77a7397c 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/194_negative_integers_to_unsigned.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/194_negative_integers_to_unsigned.sol @@ -2,4 +2,4 @@ contract test { uint8 public x = -1; } // ---- -// TypeError 7407: (37-39): Type int_const -1 is not implicitly convertible to expected type uint8. +// TypeError 7407: (37-39): Type int_const -1 is not implicitly convertible to expected type uint8. Cannot implicitly convert signed literal to unsigned type. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/195_positive_integers_to_unsigned_out_of_bound.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/195_positive_integers_to_unsigned_out_of_bound.sol index 0b5942cfb058..dbc43d7dd4b0 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/195_positive_integers_to_unsigned_out_of_bound.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/195_positive_integers_to_unsigned_out_of_bound.sol @@ -2,4 +2,4 @@ contract test { uint8 public x = 700; } // ---- -// TypeError 7407: (37-40): Type int_const 700 is not implicitly convertible to expected type uint8. +// TypeError 7407: (37-40): Type int_const 700 is not implicitly convertible to expected type uint8. Literal is too large to fit in uint8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol index 17508ed8d870..188dda39c681 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol @@ -1,8 +1,10 @@ contract test { function f() public { int x = 3; int y = 4; x ** y; } function h() public { uint8 x = 3; int16 y = 4; x ** y; } + function i() public { int16 x = 4; x ** -3; } } // ---- // TypeError 2271: (64-70): Operator ** not compatible with types int256 and int256. Exponentiation power is not allowed to be a signed integer type. // TypeError 2271: (126-132): Operator ** not compatible with types uint8 and int16. Exponentiation power is not allowed to be a signed integer type. // Warning 3149: (126-132): The result type of the exponentiation operation is equal to the type of the first operand (uint8) ignoring the (larger) type of the second operand (int16) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other. +// TypeError 2271: (175-182): Operator ** not compatible with types int16 and int_const -3. Exponentiation power is not allowed to be a negative integer literal. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol index 1b9825623f60..cfd3fcda6838 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; library c { struct S { uint x; } function f() public returns (S memory) {} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/262_bound_function_in_var.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/262_bound_function_in_var.sol index 699a0b551ff6..2463bee5ec37 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/262_bound_function_in_var.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/262_bound_function_in_var.sol @@ -9,5 +9,5 @@ contract C { } } // ---- -// TypeError 9574: (218-271): Type function (struct D.s storage pointer,uint256) returns (uint256) is not implicitly convertible to expected type function (struct D.s storage pointer,uint256) returns (uint256). +// TypeError 9574: (218-271): Type function (struct D.s storage pointer,uint256) returns (uint256) is not implicitly convertible to expected type function (struct D.s storage pointer,uint256) returns (uint256). Bound functions can not be converted to non-bound functions. // TypeError 6160: (298-302): Wrong argument count for function call: 1 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol index b3d8aea81020..19920b50c1bb 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol @@ -36,15 +36,15 @@ contract C { // real is not there yet. // array - byte[2] memory a; - byte[2] memory b; - byte[2] memory k = true ? a : b; - k[0] = byte(0); //Avoid unused var warning + bytes1[2] memory a; + bytes1[2] memory b; + bytes1[2] memory k = true ? a : b; + k[0] = bytes1(0); //Avoid unused var warning bytes memory e; bytes memory f; bytes memory l = true ? e : f; - l[0] = byte(0); // Avoid unused var warning + l[0] = bytes1(0); // Avoid unused var warning // fixed bytes bytes2 c; @@ -84,6 +84,6 @@ contract C { } } // ---- -// Warning 2519: (1005-1019): This declaration shadows an existing declaration. +// Warning 2519: (1013-1027): This declaration shadows an existing declaration. // Warning 2018: (257-642): Function state mutability can be restricted to pure -// Warning 2018: (647-1227): Function state mutability can be restricted to pure +// Warning 2018: (647-1237): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol index 563d14d2ca8b..cea40fc4c89a 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol @@ -6,5 +6,4 @@ contract test { } } // ---- -// TypeError 1273: (87-90): The type of a variable cannot be a library. -// TypeError 9582: (100-103): Member "l" not found or not visible after argument-dependent lookup in library L. +// TypeError 1130: (87-88): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol index 8b401c1ae90a..276c6533d07b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol @@ -7,3 +7,4 @@ contract test { } // ---- // SyntaxError 9636: (70-75): Use of unary + is disallowed. +// TypeError 4907: (70-75): Unary operator + cannot be applied to type rational_const 13 / 4 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol index b5a716d65e84..80787c10c467 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol @@ -1,7 +1,7 @@ contract test { function f() public { - address(0x12).send(1); + payable(address(0x12)).send(1); } } // ---- -// Warning 5878: (50-71): Failure condition of 'send' ignored. Consider using 'transfer' instead. +// Warning 5878: (50-80): Failure condition of 'send' ignored. Consider using 'transfer' instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol index 063244eed4a6..d025dfb8b992 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; T[] sub; } struct T { uint[] x; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/404_read_returned_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/404_read_returned_struct.sol index b074cb488ab7..39958b66c010 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/404_read_returned_struct.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/404_read_returned_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract A { struct T { int x; diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol index 81cc7d0d413f..25acbfd42a3e 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol @@ -1,6 +1,6 @@ contract C { function f() public { - (0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E).transfer(2); + payable(0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E).transfer(2); } } // ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol index dd405ab728d1..2abd832f137e 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol index 2a1a402c71a2..2299c4e85eb5 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol index 49e115ee9e80..d0ee67e857e5 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-105): This looks like an address but is not exactly 40 hex digits. It is 39 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-105): This looks like an address but is not exactly 40 hex digits. It is 39 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol index 17eeb3b91088..bc5fc8b999c3 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// SyntaxError 9429: (64-107): This looks like an address but is not exactly 40 hex digits. It is 41 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals +// SyntaxError 9429: (64-107): This looks like an address but is not exactly 40 hex digits. It is 41 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://docs.soliditylang.org/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol index 47dbb3c7bbfa..642ae8360569 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol @@ -9,7 +9,7 @@ contract B { A a; fallback() external { - address(a).transfer(100); + payable(a).transfer(100); } } // ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/524_accept_library_creation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/524_accept_library_creation.sol deleted file mode 100644 index 6a5e97af848d..000000000000 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/524_accept_library_creation.sol +++ /dev/null @@ -1,6 +0,0 @@ -library L {} -contract C { - function f() public { - new L(); - } -} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol index 7afc3fed8936..a36aeed86364 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol @@ -4,7 +4,7 @@ contract C { return 1; } function transfer(uint amount) public { - address(this).transfer(amount); // to avoid pureness warning + payable(this).transfer(amount); // to avoid pureness warning } receive() payable external { } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol index e8deef5192d9..4da28a5e93e1 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol @@ -12,6 +12,6 @@ contract c { // DeclarationError 7576: (52-53): Undeclared identifier. // DeclarationError 7576: (56-60): Undeclared identifier. Did you mean "long"? // DeclarationError 7576: (70-71): Undeclared identifier. -// DeclarationError 7576: (74-78): Undeclared identifier. Did you mean "long", "log0", "log1", "log2", "log3" or "log4"? +// DeclarationError 7576: (74-78): Undeclared identifier. Did you mean "long"? // DeclarationError 7576: (88-89): Undeclared identifier. // DeclarationError 7576: (92-96): Undeclared identifier. Did you mean "long"? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol index a2ec34ecfc34..abbf76182f9b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol @@ -1,3 +1,4 @@ +pragma abicoder v1; contract C { struct s { uint a; uint b; } function f() pure public { @@ -5,4 +6,4 @@ contract C { } } // ---- -// TypeError 9611: (98-99): Decoding type struct C.s memory not supported. +// TypeError 9611: (118-119): Decoding type struct C.s memory not supported. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/array_length_fractional_computed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/array_length_fractional_computed.sol new file mode 100644 index 000000000000..dd4c8cb83637 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/array_length_fractional_computed.sol @@ -0,0 +1,10 @@ +contract test { + uint constant a = 7; + uint constant b = 3; + function f() public { + uint[a / b] memory x; x[0]; + uint[7 / 3] memory y; y[0]; + } +} +// ---- +// TypeError 3208: (141-146): Array with fractional length specified. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/free_and_constant.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/free_and_constant.sol new file mode 100644 index 000000000000..ceb172bad0b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/free_and_constant.sol @@ -0,0 +1,4 @@ +uint constant c = 7; +function c() returns (uint) {} +// ---- +// DeclarationError 2333: (21-51): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/invalidTypes/conditional_expression.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/invalidTypes/conditional_expression.sol index 8cb32f72d63b..e7abd890dfd1 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/invalidTypes/conditional_expression.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/invalidTypes/conditional_expression.sol @@ -1,5 +1,5 @@ contract C { - function o(byte) public pure {} + function o(bytes1) public pure {} function f() public { o(true ? 99**99 : 99); o(true ? 99 : 99**99); @@ -8,9 +8,9 @@ contract C { } } // ---- -// TypeError 9717: (92-98): Invalid mobile type in true expression. -// TypeError 9553: (85-103): Invalid type for argument in function call. Invalid implicit conversion from uint8 to bytes1 requested. -// TypeError 3703: (128-134): Invalid mobile type in false expression. -// TypeError 9553: (116-134): Invalid type for argument in function call. Invalid implicit conversion from uint8 to bytes1 requested. -// TypeError 9717: (155-161): Invalid mobile type in true expression. -// TypeError 3703: (164-170): Invalid mobile type in false expression. +// TypeError 9717: (94-100): Invalid mobile type in true expression. +// TypeError 9553: (87-105): Invalid type for argument in function call. Invalid implicit conversion from uint8 to bytes1 requested. +// TypeError 3703: (130-136): Invalid mobile type in false expression. +// TypeError 9553: (118-136): Invalid type for argument in function call. Invalid implicit conversion from uint8 to bytes1 requested. +// TypeError 9717: (157-163): Invalid mobile type in true expression. +// TypeError 3703: (166-172): Invalid mobile type in false expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/no_effect_statements.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/no_effect_statements.sol index 68b588ad8054..5f5b9a13051a 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/no_effect_statements.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/no_effect_statements.sol @@ -1,6 +1,6 @@ contract test { struct s { uint a; uint b;} - function f() pure public returns (byte) { + function f() pure public returns (bytes1) { s; s(1,2); s[7]; @@ -9,8 +9,9 @@ contract test { } } // ---- -// Warning 6133: (93-94): Statement has no effect. -// Warning 6133: (98-104): Statement has no effect. -// Warning 6133: (108-112): Statement has no effect. -// Warning 6133: (116-120): Statement has no effect. -// Warning 6133: (124-131): Statement has no effect. +// Warning 6321: (83-89): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6133: (95-96): Statement has no effect. +// Warning 6133: (100-106): Statement has no effect. +// Warning 6133: (110-114): Statement has no effect. +// Warning 6133: (118-122): Statement has no effect. +// Warning 6133: (126-133): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_assembly_functions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_assembly_functions.sol new file mode 100644 index 000000000000..cb1f793b2c46 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_assembly_functions.sol @@ -0,0 +1,16 @@ +contract C { + function f() public pure { + assembly { + function this() { + } + function super() { + } + function _() { + } + } + } +} +// ---- +// DeclarationError 4113: (75-106): The identifier name "this" is reserved. +// DeclarationError 4113: (119-151): The identifier name "super" is reserved. +// DeclarationError 4113: (164-192): The identifier name "_" is reserved. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_assembly_identifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_assembly_identifier.sol new file mode 100644 index 000000000000..4fc9b245bbad --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_assembly_identifier.sol @@ -0,0 +1,15 @@ +contract C { + function f() public { + assembly { + let super := 1 + let this := 1 + let _ := 1 + } + } +} +// ---- +// DeclarationError 4113: (74-79): The identifier name "super" is reserved. +// DeclarationError 3859: (74-79): This declaration shadows a declaration outside the inline assembly block. +// DeclarationError 4113: (101-105): The identifier name "this" is reserved. +// DeclarationError 3859: (101-105): This declaration shadows a declaration outside the inline assembly block. +// DeclarationError 4113: (127-128): The identifier name "_" is reserved. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_function_parameters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_function_parameters.sol new file mode 100644 index 000000000000..6b5c7c99bbe7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_function_parameters.sol @@ -0,0 +1,28 @@ +contract C { + function f(uint super) public { + } + function g(uint this) public { + } + function h(uint _) public { + } + function i() public returns (uint super) { + return 1; + } + function j() public returns (uint this) { + return 1; + } + function k() public returns (uint _) { + return 1; + } +} +// ---- +// DeclarationError 3726: (28-38): The name "super" is reserved. +// DeclarationError 3726: (70-79): The name "this" is reserved. +// DeclarationError 3726: (111-117): The name "_" is reserved. +// DeclarationError 3726: (167-177): The name "super" is reserved. +// DeclarationError 3726: (238-247): The name "this" is reserved. +// DeclarationError 3726: (308-314): The name "_" is reserved. +// Warning 2319: (28-38): This declaration shadows a builtin symbol. +// Warning 2319: (70-79): This declaration shadows a builtin symbol. +// Warning 2319: (167-177): This declaration shadows a builtin symbol. +// Warning 2319: (238-247): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_library_using_for.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_library_using_for.sol new file mode 100644 index 000000000000..ac928fa9f493 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_library_using_for.sol @@ -0,0 +1,26 @@ +library super { + function f() public { + } +} + +library this { + function f() public { + } +} +library _ { + function f() public { + } +} + +contract C { + // These are not errors + using super for uint; + using this for uint16; + using _ for int; +} +// ---- +// DeclarationError 3726: (0-49): The name "super" is reserved. +// DeclarationError 3726: (51-99): The name "this" is reserved. +// DeclarationError 3726: (100-145): The name "_" is reserved. +// Warning 2319: (0-49): This declaration shadows a builtin symbol. +// Warning 2319: (51-99): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_using_for.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_using_for.sol new file mode 100644 index 000000000000..bf995ba2f490 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/illegal_names_using_for.sol @@ -0,0 +1,32 @@ +library L { + function f() public { + } +} + +// error +struct super { + uint a; +} + +// error +struct this { + uint a; +} + +// error +struct _ { + uint a; +} + +contract C { + // These are not errors + using L for super; + using L for _; + using L for this; +} +// ---- +// DeclarationError 3726: (56-84): The name "super" is reserved. +// DeclarationError 3726: (95-122): The name "this" is reserved. +// DeclarationError 3726: (133-157): The name "_" is reserved. +// Warning 2319: (56-84): This declaration shadows a builtin symbol. +// Warning 2319: (95-122): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/this_super.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/this_super.sol index 646cd6e19da4..47970c046111 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/this_super.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/shadowsBuiltin/this_super.sol @@ -5,7 +5,7 @@ contract C { } } // ---- +// DeclarationError 3726: (52-62): The name "super" is reserved. +// DeclarationError 3726: (76-85): The name "this" is reserved. // Warning 2319: (52-62): This declaration shadows a builtin symbol. // Warning 2319: (76-85): This declaration shadows a builtin symbol. -// Warning 2072: (52-62): Unused local variable. -// Warning 2072: (76-85): Unused local variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol index 15de57c41305..b340c2d396a5 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/typeChecking/library_instances.sol @@ -9,6 +9,6 @@ contract Y { } } // ---- -// TypeError 1273: (29-34): The type of a variable cannot be a library. -// TypeError 1273: (50-57): The type of a variable cannot be a library. -// TypeError 1273: (77-82): The type of a variable cannot be a library. +// TypeError 1130: (29-30): Invalid use of a library name. +// TypeError 1130: (50-51): Invalid use of a library name. +// TypeError 1130: (77-78): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/natspec/docstring_variable.sol b/test/libsolidity/syntaxTests/natspec/docstring_variable.sol index a126ca940f5f..140ed468ebd4 100644 --- a/test/libsolidity/syntaxTests/natspec/docstring_variable.sol +++ b/test/libsolidity/syntaxTests/natspec/docstring_variable.sol @@ -11,4 +11,4 @@ contract C { } } // ---- -// ParserError 2837: (290-295): Only state variables can have a docstring. +// ParserError 2837: (290-295): Only state variables or file-level variables can have a docstring. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inherit_modifier_no_return.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inherit_modifier_no_return.sol new file mode 100644 index 000000000000..2517ff381752 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inherit_modifier_no_return.sol @@ -0,0 +1,10 @@ +contract C { + ///@return + modifier m22 virtual { _; } +} + +contract D is C { + modifier m22 override { _; } +} +// ---- +// DocstringParsingError 6546: (14-24): Documentation tag @return not valid for modifiers. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inherit_modifier_no_return2.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inherit_modifier_no_return2.sol new file mode 100644 index 000000000000..5b81a466001e --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inherit_modifier_no_return2.sol @@ -0,0 +1,12 @@ +contract A { + /// @return a + function g(int x) public virtual { return 2; } +} + +contract B is A { + function g(int x) public pure override returns (int b) { return 2; } +} +// ---- +// DocstringParsingError 2604: (14-27): Documentation tag "@return a" exceeds the number of return parameters. +// TypeError 4822: (98-166): Overriding function return types differ. +// TypeError 8863: (64-72): Different number of arguments in return statement than in returns declaration. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol new file mode 100644 index 000000000000..d2c41cd2c287 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_emptys.sol @@ -0,0 +1,19 @@ +contract C { + /// @inheritdoc + function f() internal { + } + /// @inheritdoc . + function f() internal { + } + /// @inheritdoc C..f + function f() internal { + } + /// @inheritdoc C. + function f() internal { + } +} +// ---- +// DocstringParsingError 1933: (17-32): Expected contract name following documentation tag @inheritdoc. +// DocstringParsingError 5967: (71-88): Documentation tag @inheritdoc reference "." is malformed. +// DocstringParsingError 5967: (127-147): Documentation tag @inheritdoc reference "C..f" is malformed. +// DocstringParsingError 5967: (186-204): Documentation tag @inheritdoc reference "C." is malformed. diff --git a/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_twice.sol b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_twice.sol new file mode 100644 index 000000000000..d59dea2999d3 --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/invalid/docstring_inheritdoc_twice.sol @@ -0,0 +1,9 @@ +contract B {} + +contract C { + /// @inheritdoc B + /// @inheritdoc B + function f() internal {} +} +// ---- +// DocstringParsingError 5142: (32-71): Documentation tag @inheritdoc can only be given once. diff --git a/test/libsolidity/syntaxTests/negation.sol b/test/libsolidity/syntaxTests/negation.sol new file mode 100644 index 000000000000..f79920954e61 --- /dev/null +++ b/test/libsolidity/syntaxTests/negation.sol @@ -0,0 +1,9 @@ +contract test { + function f() public pure { + int x; + uint y = uint(-x); + -y; + } +} +// ---- +// TypeError 4907: (97-99): Unary operator - cannot be applied to type uint256 diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol b/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol index 154bfb54b6a3..b83ad3f94e83 100644 --- a/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol +++ b/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol @@ -1,3 +1,3 @@ contract C { - address payable constant a = address(0); + address payable constant a = payable(0); } diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol b/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol index 234b528a870e..2a0a046593c1 100644 --- a/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol +++ b/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol @@ -4,3 +4,5 @@ contract C { function (address payable) payable external returns (address payable) h; h; } } +// ---- +// Warning 6321: (197-267): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/parsing/array_range_nested.sol b/test/libsolidity/syntaxTests/parsing/array_range_nested.sol index 16fde7999663..5f027cc8cfc6 100644 --- a/test/libsolidity/syntaxTests/parsing/array_range_nested.sol +++ b/test/libsolidity/syntaxTests/parsing/array_range_nested.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][] calldata x) external pure { x[0][1:2]; diff --git a/test/libsolidity/syntaxTests/parsing/array_range_nested_invalid.sol b/test/libsolidity/syntaxTests/parsing/array_range_nested_invalid.sol index c224818b4dd3..700f3e1ff845 100644 --- a/test/libsolidity/syntaxTests/parsing/array_range_nested_invalid.sol +++ b/test/libsolidity/syntaxTests/parsing/array_range_nested_invalid.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(uint256[][] calldata x) external pure { x[1:2]; diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol new file mode 100644 index 000000000000..8b84de0553fa --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal internal {} +} +// ---- +// ParserError 9439: (38-46): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol new file mode 100644 index 000000000000..15d60c392c5a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_internal_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() internal public {} +} +// ---- +// ParserError 9439: (38-44): Visibility already specified as "internal". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol new file mode 100644 index 000000000000..495e3759c849 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_payable_payable.sol @@ -0,0 +1,5 @@ +contract C { + constructor() payable payable {} +} +// ---- +// ParserError 9680: (37-44): State mutability already specified as "payable". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol new file mode 100644 index 000000000000..7d58520e5a31 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_internal.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public internal {} +} +// ---- +// ParserError 9439: (36-44): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol new file mode 100644 index 000000000000..c0674f2e5409 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_public_public.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public public {} +} +// ---- +// ParserError 9439: (36-42): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/declaring_fixed_and_ufixed_variables.sol b/test/libsolidity/syntaxTests/parsing/declaring_fixed_and_ufixed_variables.sol index 230176df7c0c..e34522540e8d 100644 --- a/test/libsolidity/syntaxTests/parsing/declaring_fixed_and_ufixed_variables.sol +++ b/test/libsolidity/syntaxTests/parsing/declaring_fixed_and_ufixed_variables.sol @@ -6,7 +6,7 @@ contract A { } } // ---- -// UnimplementedFeatureError: Not yet implemented - FixedPointType. +// UnimplementedFeatureError: Fixed point types not implemented. // Warning 5667: (52-60): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning 5667: (62-74): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning 2072: (93-104): Unused local variable. diff --git a/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol b/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol index 8993d87ec267..bc5db10baea9 100644 --- a/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol +++ b/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol @@ -5,7 +5,7 @@ contract C { int256 payable d; uint payable e; uint256 payable f; - byte payable g; + bytes1 payable g; bytes payable h; bytes32 payable i; fixed payable j; @@ -20,10 +20,10 @@ contract C { // ParserError 9106: (85-92): State mutability can only be specified for address types. // ParserError 9106: (105-112): State mutability can only be specified for address types. // ParserError 9106: (128-135): State mutability can only be specified for address types. -// ParserError 9106: (148-155): State mutability can only be specified for address types. -// ParserError 9106: (169-176): State mutability can only be specified for address types. -// ParserError 9106: (192-199): State mutability can only be specified for address types. -// ParserError 9106: (213-220): State mutability can only be specified for address types. -// ParserError 9106: (239-246): State mutability can only be specified for address types. -// ParserError 9106: (261-268): State mutability can only be specified for address types. -// ParserError 9106: (288-295): State mutability can only be specified for address types. +// ParserError 9106: (150-157): State mutability can only be specified for address types. +// ParserError 9106: (171-178): State mutability can only be specified for address types. +// ParserError 9106: (194-201): State mutability can only be specified for address types. +// ParserError 9106: (215-222): State mutability can only be specified for address types. +// ParserError 9106: (241-248): State mutability can only be specified for address types. +// ParserError 9106: (263-270): State mutability can only be specified for address types. +// ParserError 9106: (290-297): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol index cc39855d43cc..7d26f74c07c0 100644 --- a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol @@ -5,7 +5,7 @@ contract C { function d(int256 payable) public pure {} function e(uint payable) public pure {} function f(uint256 payable) public pure {} - function g(byte payable) public pure {} + function g(bytes1 payable) public pure {} function h(bytes payable) public pure {} function i(bytes32 payable) public pure {} function j(fixed payable) public pure {} @@ -20,10 +20,10 @@ contract C { // ParserError 9106: (168-175): State mutability can only be specified for address types. // ParserError 9106: (212-219): State mutability can only be specified for address types. // ParserError 9106: (259-266): State mutability can only be specified for address types. -// ParserError 9106: (303-310): State mutability can only be specified for address types. -// ParserError 9106: (348-355): State mutability can only be specified for address types. -// ParserError 9106: (395-402): State mutability can only be specified for address types. -// ParserError 9106: (440-447): State mutability can only be specified for address types. -// ParserError 9106: (490-497): State mutability can only be specified for address types. -// ParserError 9106: (536-543): State mutability can only be specified for address types. -// ParserError 9106: (587-594): State mutability can only be specified for address types. +// ParserError 9106: (305-312): State mutability can only be specified for address types. +// ParserError 9106: (350-357): State mutability can only be specified for address types. +// ParserError 9106: (397-404): State mutability can only be specified for address types. +// ParserError 9106: (442-449): State mutability can only be specified for address types. +// ParserError 9106: (492-499): State mutability can only be specified for address types. +// ParserError 9106: (538-545): State mutability can only be specified for address types. +// ParserError 9106: (589-596): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol index b36f9e6d50d5..2aad9cd04e11 100644 --- a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol @@ -6,7 +6,7 @@ contract C { int256 payable d; uint payable e; uint256 payable f; - byte payable g; + bytes1 payable g; bytes payable h; bytes32 payable i; fixed payable j; @@ -22,10 +22,10 @@ contract C { // ParserError 9106: (132-139): State mutability can only be specified for address types. // ParserError 9106: (156-163): State mutability can only be specified for address types. // ParserError 9106: (183-190): State mutability can only be specified for address types. -// ParserError 9106: (207-214): State mutability can only be specified for address types. -// ParserError 9106: (232-239): State mutability can only be specified for address types. -// ParserError 9106: (259-266): State mutability can only be specified for address types. -// ParserError 9106: (284-291): State mutability can only be specified for address types. -// ParserError 9106: (314-321): State mutability can only be specified for address types. -// ParserError 9106: (340-347): State mutability can only be specified for address types. -// ParserError 9106: (371-378): State mutability can only be specified for address types. +// ParserError 9106: (209-216): State mutability can only be specified for address types. +// ParserError 9106: (234-241): State mutability can only be specified for address types. +// ParserError 9106: (261-268): State mutability can only be specified for address types. +// ParserError 9106: (286-293): State mutability can only be specified for address types. +// ParserError 9106: (316-323): State mutability can only be specified for address types. +// ParserError 9106: (342-349): State mutability can only be specified for address types. +// ParserError 9106: (373-380): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol index 5006936ef4c6..026138cde62e 100644 --- a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol @@ -5,7 +5,7 @@ contract C { function d() public pure returns (int256 payable) {} function e() public pure returns (uint payable) {} function f() public pure returns (uint256 payable) {} - function g() public pure returns (byte payable) {} + function g() public pure returns (bytes1 payable) {} function h() public pure returns (bytes payable) {} function i() public pure returns (bytes32 payable) {} function j() public pure returns (fixed payable) {} @@ -20,10 +20,10 @@ contract C { // ParserError 9106: (224-231): State mutability can only be specified for address types. // ParserError 9106: (279-286): State mutability can only be specified for address types. // ParserError 9106: (337-344): State mutability can only be specified for address types. -// ParserError 9106: (392-399): State mutability can only be specified for address types. -// ParserError 9106: (448-455): State mutability can only be specified for address types. -// ParserError 9106: (506-513): State mutability can only be specified for address types. -// ParserError 9106: (562-569): State mutability can only be specified for address types. -// ParserError 9106: (623-630): State mutability can only be specified for address types. -// ParserError 9106: (680-687): State mutability can only be specified for address types. -// ParserError 9106: (742-749): State mutability can only be specified for address types. +// ParserError 9106: (394-401): State mutability can only be specified for address types. +// ParserError 9106: (450-457): State mutability can only be specified for address types. +// ParserError 9106: (508-515): State mutability can only be specified for address types. +// ParserError 9106: (564-571): State mutability can only be specified for address types. +// ParserError 9106: (625-632): State mutability can only be specified for address types. +// ParserError 9106: (682-689): State mutability can only be specified for address types. +// ParserError 9106: (744-751): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/empty_enum.sol b/test/libsolidity/syntaxTests/parsing/empty_enum.sol index 36c05fd4b768..1dc04d914cd3 100644 --- a/test/libsolidity/syntaxTests/parsing/empty_enum.sol +++ b/test/libsolidity/syntaxTests/parsing/empty_enum.sol @@ -2,4 +2,4 @@ contract c { enum foo { } } // ---- -// ParserError 3147: (25-26): enum with no members is not allowed. +// ParserError 3147: (25-26): Enum with no members is not allowed. diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol new file mode 100644 index 000000000000..8d67db095deb --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_mutability.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() pure pure g; + } +} +// ---- +// ParserError 9680: (62-66): State mutability already specified as "pure". diff --git a/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol new file mode 100644 index 000000000000..c9b035864018 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_multiple_visibility.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + function() public public g; + } +} +// ---- +// ParserError 9439: (64-70): Visibility already specified as "public". diff --git a/test/libsolidity/syntaxTests/parsing/if_statement.sol b/test/libsolidity/syntaxTests/parsing/if_statement.sol index b649203107ba..580a0f557d59 100644 --- a/test/libsolidity/syntaxTests/parsing/if_statement.sol +++ b/test/libsolidity/syntaxTests/parsing/if_statement.sol @@ -4,5 +4,6 @@ contract test { } } // ---- +// Warning 6321: (60-64): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. // Warning 2072: (109-115): Unused local variable. // Warning 2018: (20-128): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params.sol index a441c37b7b0f..7d3dd564055a 100644 --- a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params.sol +++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params.sol @@ -2,4 +2,4 @@ contract Foo { function f(uint[] storage constant x, uint[] memory y) internal { } } // ---- -// DeclarationError 1788: (30-55): The "constant" keyword can only be used for state variables. +// DeclarationError 1788: (30-55): The "constant" keyword can only be used for state variables or variables at file level. diff --git a/test/libsolidity/syntaxTests/parsing/max_depth_reached_1.sol b/test/libsolidity/syntaxTests/parsing/max_depth_reached_1.sol new file mode 100644 index 000000000000..b193b3c42877 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/max_depth_reached_1.sol @@ -0,0 +1,8 @@ +contract C { + bytes + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ // 100*[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [ +.... +// ---- +// ParserError 7319: (287-288): Maximum recursion depth reached during parsing. diff --git a/test/libsolidity/syntaxTests/parsing/max_depth_reached_2.sol b/test/libsolidity/syntaxTests/parsing/max_depth_reached_2.sol new file mode 100644 index 000000000000..d57f24055d95 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/max_depth_reached_2.sol @@ -0,0 +1,11 @@ +contract C { + function f() { + {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ // 100*{ + {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ + {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ + {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ + {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ + {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ { +.... +// ---- +// ParserError 7319: (693-694): Maximum recursion depth reached during parsing. diff --git a/test/libsolidity/syntaxTests/parsing/max_depth_reached_3.sol b/test/libsolidity/syntaxTests/parsing/max_depth_reached_3.sol new file mode 100644 index 000000000000..d5dfa4af5150 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/max_depth_reached_3.sol @@ -0,0 +1,9 @@ +contract C { + function f() { + uint x = f + (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( // 100*( + (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( + (((((((((((((((((((((((((((((((((((((( ( +.... +// ---- +// ParserError 7319: (325-326): Maximum recursion depth reached during parsing. diff --git a/test/libsolidity/syntaxTests/parsing/max_depth_reached_4.sol b/test/libsolidity/syntaxTests/parsing/max_depth_reached_4.sol new file mode 100644 index 000000000000..dc9b3103422c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/max_depth_reached_4.sol @@ -0,0 +1,28 @@ +contract C { + function f() public pure { + uint ok = 0; + uint nok = 0; + + (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( + ( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( ( + (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( + ( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( ( + (-( (-( (-( (-( (-( (-( (-( (-( ( + ok++ + ))))))))))))))))) + )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); + + ( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( ( + (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( + ( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( ( + (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( (-( + ( (-( (-( (-( (-( (-( (-( (-( (-( ( + nok++ + )))))))))))))))))) + )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) + )))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))); + } +} +// ---- +// ParserError 7319: (1305-1308): Maximum recursion depth reached during parsing. diff --git a/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol b/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol index 6fa007c7d87e..dfd7cfdf3163 100644 --- a/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol +++ b/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol @@ -1,5 +1,5 @@ contract c { - modifier mod1(uint a) { if (msg.sender == address(a)) _; } + modifier mod1(uint a) { if (msg.sender == address(uint160(a))) _; } modifier mod2 { if (msg.sender == address(2)) _; } function f() public mod1(7) mod2 { } } diff --git a/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol b/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol new file mode 100644 index 000000000000..8dbe4ac67eea --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_modifier_overrides.sol @@ -0,0 +1,5 @@ +contract C { + modifier f() override override {} +} +// ---- +// ParserError 9102: (39-47): Override already specified. diff --git a/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol b/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol new file mode 100644 index 000000000000..c04558171dbc --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_visibility_specifiers.sol @@ -0,0 +1,7 @@ +contract C { + uint private internal a; + function f() private external {} +} +// ---- +// ParserError 4110: (30-38): Visibility already specified as "private". +// ParserError 9439: (67-75): Visibility already specified as "private". diff --git a/test/libsolidity/syntaxTests/parsing/placeholder_in_function_context.sol b/test/libsolidity/syntaxTests/parsing/placeholder_in_function_context.sol index 67db5ee35ccb..5b16ad02ce74 100644 --- a/test/libsolidity/syntaxTests/parsing/placeholder_in_function_context.sol +++ b/test/libsolidity/syntaxTests/parsing/placeholder_in_function_context.sol @@ -5,4 +5,4 @@ contract c { } } // ---- -// Warning 2018: (17-105): Function state mutability can be restricted to pure +// DeclarationError 3726: (66-72): The name "_" is reserved. diff --git a/test/libsolidity/syntaxTests/parsing/wrong_compiler_1.sol b/test/libsolidity/syntaxTests/parsing/wrong_compiler_1.sol new file mode 100644 index 000000000000..ea05513de189 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/wrong_compiler_1.sol @@ -0,0 +1,3 @@ +pragma solidity ^99.99.0; +// ---- +// ParserError 5333: (0-25): Source file requires different compiler version (current compiler is .... diff --git a/test/libsolidity/syntaxTests/parsing/wrong_compiler_2.sol b/test/libsolidity/syntaxTests/parsing/wrong_compiler_2.sol new file mode 100644 index 000000000000..6e97cd06c2ad --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/wrong_compiler_2.sol @@ -0,0 +1,4 @@ +pragma solidity ^99.99.0; +this is surely invalid +// ---- +// ParserError 5333: (0-25): Source file requires different compiler version (current compiler is .... diff --git a/test/libsolidity/syntaxTests/parsing/wrong_compiler_3.sol b/test/libsolidity/syntaxTests/parsing/wrong_compiler_3.sol new file mode 100644 index 000000000000..06d7c4dda806 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/wrong_compiler_3.sol @@ -0,0 +1,6 @@ +pragma solidity ^99.99.0; +contract C { + uint ; +} +// ---- +// ParserError 5333: (0-25): Source file requires different compiler version (current compiler is .... diff --git a/test/libsolidity/syntaxTests/parsing/wrong_compiler_4.sol b/test/libsolidity/syntaxTests/parsing/wrong_compiler_4.sol new file mode 100644 index 000000000000..0d052123e876 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/wrong_compiler_4.sol @@ -0,0 +1,6 @@ +pragma solidity ^99.99.0; +contract C { + function f() {} +} +// ---- +// ParserError 5333: (0-25): Source file requires different compiler version (current compiler is .... diff --git a/test/libsolidity/syntaxTests/receiveEther/msg_data_in_receive.sol b/test/libsolidity/syntaxTests/receiveEther/msg_data_in_receive.sol new file mode 100644 index 000000000000..20e0dbdf2682 --- /dev/null +++ b/test/libsolidity/syntaxTests/receiveEther/msg_data_in_receive.sol @@ -0,0 +1,5 @@ +contract C { + receive() external payable { msg.data; } +} +// ---- +// TypeError 7139: (46-54): "msg.data" cannot be used inside of "receive" function. diff --git a/test/libsolidity/syntaxTests/scoping/access_in_assignment_dynamic_array.sol b/test/libsolidity/syntaxTests/scoping/access_in_assignment_dynamic_array.sol new file mode 100644 index 000000000000..12454b740127 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/access_in_assignment_dynamic_array.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + uint[] memory x = x[0]; + } +} +// ---- +// DeclarationError 7576: (70-71): Undeclared identifier. "x" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/scoping/access_in_assignment_struct.sol b/test/libsolidity/syntaxTests/scoping/access_in_assignment_struct.sol new file mode 100644 index 000000000000..152ee038e96f --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/access_in_assignment_struct.sol @@ -0,0 +1,8 @@ +contract C { + struct S { uint y; } + function f() public pure { + S memory x = x.y; + } +} +// ---- +// DeclarationError 7576: (90-91): Undeclared identifier. "x" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol index 2e997016375b..e6beef51aec7 100644 --- a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol @@ -5,5 +5,6 @@ contract test { } } // ---- +// Warning 2519: (57-63): This declaration shadows an existing declaration. // Warning 2072: (57-63): Unused local variable. // Warning 2072: (75-81): Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_and_disjoint_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_and_disjoint_scope.sol index 47c3d1302564..f457b183fec6 100644 --- a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_and_disjoint_scope.sol +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_and_disjoint_scope.sol @@ -6,5 +6,5 @@ contract test { } } // ---- -// Warning 2519: (73-79): This declaration shadows an existing declaration. // DeclarationError 2333: (91-97): Identifier already declared. +// Warning 2519: (73-79): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/scoping/name_pseudo_shadowing.sol b/test/libsolidity/syntaxTests/scoping/name_pseudo_shadowing.sol new file mode 100644 index 000000000000..d2a191eeb0d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/name_pseudo_shadowing.sol @@ -0,0 +1,6 @@ +contract test { + function e() external { } + function f() public pure { uint e; e = 0; } +} +// ---- +// Warning 8760: (77-83): This declaration has the same name as another declaration. diff --git a/test/libsolidity/syntaxTests/scoping/name_pseudo_shadowing2.sol b/test/libsolidity/syntaxTests/scoping/name_pseudo_shadowing2.sol new file mode 100644 index 000000000000..88a59cc16825 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/name_pseudo_shadowing2.sol @@ -0,0 +1,7 @@ +contract test { + function e() external { } + function f() public pure { uint e; e = 0; } + function e(int) external { } +} +// ---- +// Warning 8760: (77-83): This declaration has the same name as another declaration. diff --git a/test/libsolidity/syntaxTests/scoping/name_shadowing2.sol b/test/libsolidity/syntaxTests/scoping/name_shadowing2.sol new file mode 100644 index 000000000000..b7818410987b --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/name_shadowing2.sol @@ -0,0 +1,6 @@ +function e() {} +contract test { + function f() pure public { uint e; e = 0; } +} +// ---- +// Warning 2519: (63-69): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/scoping/name_shadowing3.sol b/test/libsolidity/syntaxTests/scoping/name_shadowing3.sol new file mode 100644 index 000000000000..43f7c86b3bd5 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/name_shadowing3.sol @@ -0,0 +1,10 @@ +function e() {} +contract test { + function f() pure public { uint e; uint g; uint h; e = g = h = 0; } + function g() pure public {} +} +function h() {} +// ---- +// Warning 2519: (63-69): This declaration shadows an existing declaration. +// Warning 2519: (71-77): This declaration shadows an existing declaration. +// Warning 2519: (79-85): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array.sol new file mode 100644 index 000000000000..c10dc10f25e0 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array.sol @@ -0,0 +1,8 @@ +pragma abicoder v1; +contract C { + function f() public pure { + abi.encodePacked([new uint[](5), new uint[](7)]); + } +} +// ---- +// TypeError 9578: (89-119): Type not supported in packed mode. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array_v2.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array_v2.sol new file mode 100644 index 000000000000..150ac68e387e --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array_v2.sol @@ -0,0 +1,9 @@ +pragma abicoder v2; + +contract C { + function f() public pure { + abi.encodePacked([new uint[](5), new uint[](7)]); + } +} +// ---- +// TypeError 9578: (104-134): Type not supported in packed mode. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_structs_v2.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_structs_v2.sol index 044ce6031f1c..f03c27a1bd17 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_structs_v2.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_structs_v2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint x; } diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array.sol new file mode 100644 index 000000000000..8c679258c5fe --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array.sol @@ -0,0 +1,8 @@ +pragma abicoder v1; +contract C { + function test() public pure { + abi.encode([new uint[](5), new uint[](7)]); + } +} +// ---- +// TypeError 2056: (86-116): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array_v2.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array_v2.sol new file mode 100644 index 000000000000..e12c37e7bd40 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array_v2.sol @@ -0,0 +1,9 @@ +pragma abicoder v2; + +contract C { + function f() public pure { + abi.encode([new uint[](5), new uint[](7)]); + } +} +// ---- +// Warning 6133: (87-129): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol index e502f1e13e6d..a7d0e3dbe099 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol @@ -1,3 +1,4 @@ +pragma abicoder v1; contract C { struct S { uint x; } S s; @@ -11,7 +12,7 @@ contract C { } } // ---- -// TypeError 2056: (131-132): This type cannot be encoded. -// TypeError 2056: (134-135): This type cannot be encoded. -// TypeError 9578: (200-201): Type not supported in packed mode. -// TypeError 9578: (203-204): Type not supported in packed mode. +// TypeError 2056: (151-152): This type cannot be encoded. +// TypeError 2056: (154-155): This type cannot be encoded. +// TypeError 9578: (220-221): Type not supported in packed mode. +// TypeError 9578: (223-224): Type not supported in packed mode. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol index 7f1bc61807ef..e7117f2b1ab4 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint x; } diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array.sol new file mode 100644 index 000000000000..c705ab2ec1b9 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array.sol @@ -0,0 +1,8 @@ +pragma abicoder v1; +contract C { + function f() public pure { + abi.decode("1234", (uint[][3])); + } +} +// ---- +// TypeError 9611: (92-101): Decoding type uint256[] memory[3] memory not supported. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array_v2.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array_v2.sol new file mode 100644 index 000000000000..54f1d5a6e555 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array_v2.sol @@ -0,0 +1,9 @@ +pragma abicoder v2; + +contract C { + function f() public pure { + abi.decode("1234", (uint[][3])); + } +} +// ---- +// Warning 6133: (87-118): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct.sol new file mode 100644 index 000000000000..dd981b54c9b5 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct.sol @@ -0,0 +1,12 @@ +pragma abicoder v1; +struct S { + uint x; +} + +contract C { + function f() public pure { + abi.decode("1234", (S)); + } +} +// ---- +// TypeError 9611: (118-119): Decoding type struct S memory not supported. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct_v2.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct_v2.sol new file mode 100644 index 000000000000..824f723ed2ca --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct_v2.sol @@ -0,0 +1,13 @@ +pragma abicoder v2; + +struct S { + uint x; +} + +contract C { + function f() public pure { + abi.decode("1234", (S)); + } +} +// ---- +// Warning 6133: (113-136): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/contract_array.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/contract_array.sol new file mode 100644 index 000000000000..601def4b5acc --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/contract_array.sol @@ -0,0 +1,5 @@ +contract C { + function f(bytes calldata x) public pure returns (C[] memory c) { + c = abi.decode(x, (C[])); + } +} \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/specialFunctions/encode_array_of_struct.sol b/test/libsolidity/syntaxTests/specialFunctions/encode_array_of_struct.sol index 8918decb77a8..0e7b99a54b49 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/encode_array_of_struct.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/encode_array_of_struct.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint x; } function f() public pure { diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol index 214ed8a54eb7..e83498b6c98d 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol @@ -1,13 +1,13 @@ contract C { function f() public pure { (bool a,) = address(this).call(abi.encode(address(this).delegatecall, super)); - (a,) = address(this).delegatecall(abi.encode(log0, tx, mulmod)); + (a,) = address(this).delegatecall(abi.encode(block, tx, mulmod)); a; } } // ---- // TypeError 2056: (94-120): This type cannot be encoded. // TypeError 2056: (122-127): This type cannot be encoded. -// TypeError 2056: (184-188): This type cannot be encoded. -// TypeError 2056: (190-192): This type cannot be encoded. -// TypeError 2056: (194-200): This type cannot be encoded. +// TypeError 2056: (184-189): This type cannot be encoded. +// TypeError 2056: (191-193): This type cannot be encoded. +// TypeError 2056: (195-201): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/string/invalid_legacy_escape.sol b/test/libsolidity/syntaxTests/string/invalid_legacy_escape.sol new file mode 100644 index 000000000000..1972990ef59c --- /dev/null +++ b/test/libsolidity/syntaxTests/string/invalid_legacy_escape.sol @@ -0,0 +1,8 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "\t\b\f"; + return escapeCharacters; + } +} +// ---- +// ParserError 8936: (100-105): Invalid escape sequence. diff --git a/test/libsolidity/syntaxTests/string/invalid_utf8_explicit_string.sol b/test/libsolidity/syntaxTests/string/invalid_utf8_explicit_string.sol index b9a8c5279f96..3ad1c4b56617 100644 --- a/test/libsolidity/syntaxTests/string/invalid_utf8_explicit_string.sol +++ b/test/libsolidity/syntaxTests/string/invalid_utf8_explicit_string.sol @@ -2,4 +2,4 @@ contract C { string s = string("\xa0\x00"); } // ---- -// TypeError 9640: (28-46): Explicit type conversion not allowed from "literal_string (contains invalid UTF-8 sequence at position 0)" to "string memory". +// TypeError 9640: (28-46): Explicit type conversion not allowed from "literal_string hex"a000"" to "string memory". Contains invalid UTF-8 sequence at position 0. diff --git a/test/libsolidity/syntaxTests/string/invalid_utf8_hex_string.sol b/test/libsolidity/syntaxTests/string/invalid_utf8_hex_string.sol index d72ee5af0dda..a089137dc130 100644 --- a/test/libsolidity/syntaxTests/string/invalid_utf8_hex_string.sol +++ b/test/libsolidity/syntaxTests/string/invalid_utf8_hex_string.sol @@ -2,4 +2,4 @@ contract C { string s = hex"a000"; } // ---- -// TypeError 7407: (28-37): Type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type string storage ref. +// TypeError 7407: (28-37): Type literal_string hex"a000" is not implicitly convertible to expected type string storage ref. Contains invalid UTF-8 sequence at position 0. diff --git a/test/libsolidity/syntaxTests/string/invalid_utf8_implicit_string.sol b/test/libsolidity/syntaxTests/string/invalid_utf8_implicit_string.sol index 3cf3361f3bdb..98d83e1d40d3 100644 --- a/test/libsolidity/syntaxTests/string/invalid_utf8_implicit_string.sol +++ b/test/libsolidity/syntaxTests/string/invalid_utf8_implicit_string.sol @@ -2,4 +2,4 @@ contract C { string s = "\xa0\x00"; } // ---- -// TypeError 7407: (28-38): Type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type string storage ref. +// TypeError 7407: (28-38): Type literal_string hex"a000" is not implicitly convertible to expected type string storage ref. Contains invalid UTF-8 sequence at position 0. diff --git a/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol b/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol new file mode 100644 index 000000000000..f8df14ca5760 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/invalid_utf8_sequence.sol @@ -0,0 +1,6 @@ +contract C { + string s = unicode"À"; +} +// ---- +// SyntaxError 8452: (28-38): Contains invalid UTF-8 sequence at position 0. +// TypeError 7407: (28-38): Type literal_string hex"c0" is not implicitly convertible to expected type string storage ref. Contains invalid UTF-8 sequence at position 0. diff --git a/test/libsolidity/syntaxTests/string/string_escapes.sol b/test/libsolidity/syntaxTests/string/string_escapes.sol index 51b90d735692..95739062b849 100644 --- a/test/libsolidity/syntaxTests/string/string_escapes.sol +++ b/test/libsolidity/syntaxTests/string/string_escapes.sol @@ -1,6 +1,6 @@ contract test { function f() public pure returns (bytes32) { - bytes32 escapeCharacters = "\t\b\n\r\f\'\"\\\b"; + bytes32 escapeCharacters = "\n\r\'\"\\"; return escapeCharacters; } } diff --git a/test/libsolidity/syntaxTests/string/unicode_escape_literals_invalid_codepoint.sol b/test/libsolidity/syntaxTests/string/unicode_escape_literals_invalid_codepoint.sol index 46a0de5f4fe5..975c5ac39741 100644 --- a/test/libsolidity/syntaxTests/string/unicode_escape_literals_invalid_codepoint.sol +++ b/test/libsolidity/syntaxTests/string/unicode_escape_literals_invalid_codepoint.sol @@ -4,4 +4,4 @@ contract test { } } // ---- -// TypeError 6359: (86-92): Return argument type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type (type of first return variable) string memory. +// TypeError 6359: (86-92): Return argument type literal_string hex"c1" is not implicitly convertible to expected type (type of first return variable) string memory. Contains invalid UTF-8 sequence at position 0. diff --git a/test/libsolidity/syntaxTests/structs/array_calldata.sol b/test/libsolidity/syntaxTests/structs/array_calldata.sol index e0a750c10818..30ca2ebed250 100644 --- a/test/libsolidity/syntaxTests/structs/array_calldata.sol +++ b/test/libsolidity/syntaxTests/structs/array_calldata.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct S { int a; } function f(S[] calldata) external { } diff --git a/test/libsolidity/syntaxTests/structs/calldata.sol b/test/libsolidity/syntaxTests/structs/calldata.sol index 16a66aaba8dc..420cc6b7fd98 100644 --- a/test/libsolidity/syntaxTests/structs/calldata.sol +++ b/test/libsolidity/syntaxTests/structs/calldata.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct S { int a; } function f(S calldata) external { } diff --git a/test/libsolidity/syntaxTests/structs/calldata_array_assign.sol b/test/libsolidity/syntaxTests/structs/calldata_array_assign.sol index adfa389aeba3..33b10f5f5af3 100644 --- a/test/libsolidity/syntaxTests/structs/calldata_array_assign.sol +++ b/test/libsolidity/syntaxTests/structs/calldata_array_assign.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct S { int[3] a; } function f(S calldata s, int[3] calldata a) external { diff --git a/test/libsolidity/syntaxTests/structs/calldata_assign.sol b/test/libsolidity/syntaxTests/structs/calldata_assign.sol index d8d9210087af..798e6dd608ec 100644 --- a/test/libsolidity/syntaxTests/structs/calldata_assign.sol +++ b/test/libsolidity/syntaxTests/structs/calldata_assign.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct S { int a; } function f(S calldata s) external { s.a = 4; } diff --git a/test/libsolidity/syntaxTests/structs/calldata_dynamic.sol b/test/libsolidity/syntaxTests/structs/calldata_dynamic.sol index 72e126f1d886..8e3f1954de8d 100644 --- a/test/libsolidity/syntaxTests/structs/calldata_dynamic.sol +++ b/test/libsolidity/syntaxTests/structs/calldata_dynamic.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct S { int[] a; } function f(S calldata) external { } diff --git a/test/libsolidity/syntaxTests/structs/calldata_struct_function_type.sol b/test/libsolidity/syntaxTests/structs/calldata_struct_function_type.sol index 420ee6cd6990..b2e5f6e2c111 100644 --- a/test/libsolidity/syntaxTests/structs/calldata_struct_function_type.sol +++ b/test/libsolidity/syntaxTests/structs/calldata_struct_function_type.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { function (uint) external returns (uint) fn; } function f(S calldata s) external returns (uint256 a) { diff --git a/test/libsolidity/syntaxTests/structs/calldata_struct_mapping_function.sol b/test/libsolidity/syntaxTests/structs/calldata_struct_mapping_function.sol index 398c52701189..dcdbad68bc5b 100644 --- a/test/libsolidity/syntaxTests/structs/calldata_struct_mapping_function.sol +++ b/test/libsolidity/syntaxTests/structs/calldata_struct_mapping_function.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract test { struct S { diff --git a/test/libsolidity/syntaxTests/structs/illegal_names.sol b/test/libsolidity/syntaxTests/structs/illegal_names.sol new file mode 100644 index 000000000000..48e7d93a908a --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/illegal_names.sol @@ -0,0 +1,15 @@ +struct this { uint a; } +struct super { uint b; } +struct _ { uint c; } + +contract C { + this a; + super b; + _ c; +} +// ---- +// DeclarationError 3726: (0-23): The name "this" is reserved. +// DeclarationError 3726: (24-48): The name "super" is reserved. +// DeclarationError 3726: (49-69): The name "_" is reserved. +// Warning 2319: (0-23): This declaration shadows a builtin symbol. +// Warning 2319: (24-48): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/structs/memory_to_calldata.sol b/test/libsolidity/syntaxTests/structs/memory_to_calldata.sol index 19747e9a538a..56d26a27adc4 100644 --- a/test/libsolidity/syntaxTests/structs/memory_to_calldata.sol +++ b/test/libsolidity/syntaxTests/structs/memory_to_calldata.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct S { int a; } function f(S calldata s) external { s = S(2); } diff --git a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol index aab55d643088..40f1eca8d80e 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct T { U u; V v; } diff --git a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol index 34da3e0339db..3bd34360a082 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract TestContract { diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_contract_function_parameter.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_contract_function_parameter.sol index f0a3de4012d7..2f29d3d44c29 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_contract_function_parameter.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_contract_function_parameter.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract Test { struct MyStructName { diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_memory_library_function_parameter.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_memory_library_function_parameter.sol index e32cceef30c1..6739a89858bc 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_memory_library_function_parameter.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_as_memory_library_function_parameter.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; library Test { struct MyStructName { diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol index e799573c9840..3e429d5b33c2 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { function f(Data.S memory a) public {} diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_function_pointer.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_function_pointer.sol index dc40ae3b2682..5856e9efa942 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_function_pointer.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_function_pointer.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol new file mode 100644 index 000000000000..6009ee94d2db --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol @@ -0,0 +1,10 @@ +library a { + struct b { + mapping (uint => b) c ; + } + // Segfaults in https://github.com/ethereum/solidity/issues/9443 + function d(b memory) public {} +} +// ---- +// TypeError 4103: (149-157): Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions. +// TypeError 4061: (149-157): Type struct a.b is only valid in storage because it contains a (nested) mapping. diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol new file mode 100644 index 000000000000..d40a4407cd51 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_storage.sol @@ -0,0 +1,6 @@ +library a { + struct b { + mapping (uint => b) c ; + } + function d(b storage) public {} +} diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol index 15fca7d711e6..043dcd658997 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; S[] sub; } diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol index a5b1507d34ea..05a53a71b11f 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; S[2][] sub; } diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol index fa9eebb9b225..118dc1973288 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { uint a; S[][][] sub; } diff --git a/test/libsolidity/syntaxTests/super/super_in_function.sol b/test/libsolidity/syntaxTests/super/super_in_function.sol new file mode 100644 index 000000000000..5156ddd35aea --- /dev/null +++ b/test/libsolidity/syntaxTests/super/super_in_function.sol @@ -0,0 +1,7 @@ +contract C { +} +function f() pure { + super; +} +// ---- +// DeclarationError 7576: (39-44): Undeclared identifier. "super" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/super/super_in_library.sol b/test/libsolidity/syntaxTests/super/super_in_library.sol new file mode 100644 index 000000000000..299df290c9b5 --- /dev/null +++ b/test/libsolidity/syntaxTests/super/super_in_library.sol @@ -0,0 +1,7 @@ +library L { + function f() public { + (super); + } +} +// ---- +// DeclarationError 7576: (41-46): Undeclared identifier. "super" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/tryCatch/almost_call_options.sol b/test/libsolidity/syntaxTests/tryCatch/almost_call_options.sol index 91da9e312377..49ee9ad5e89f 100644 --- a/test/libsolidity/syntaxTests/tryCatch/almost_call_options.sol +++ b/test/libsolidity/syntaxTests/tryCatch/almost_call_options.sol @@ -11,4 +11,6 @@ contract C { // ==== // EVMVersion: >=byzantium // ---- +// Warning 6321: (73-77): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (79-83): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. // Warning 2072: (122-134): Unused local variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/catch_error.sol b/test/libsolidity/syntaxTests/tryCatch/catch_error.sol index 34fc2431eb55..ccdfac28b98f 100644 --- a/test/libsolidity/syntaxTests/tryCatch/catch_error.sol +++ b/test/libsolidity/syntaxTests/tryCatch/catch_error.sol @@ -8,4 +8,7 @@ contract C { } } // ==== -// EVMVersion: >=byzantium \ No newline at end of file +// EVMVersion: >=byzantium +// ---- +// Warning 6321: (46-50): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (52-56): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/catch_error_named.sol b/test/libsolidity/syntaxTests/tryCatch/catch_error_named.sol index 1a4a1273a7ce..590c6cbba077 100644 --- a/test/libsolidity/syntaxTests/tryCatch/catch_error_named.sol +++ b/test/libsolidity/syntaxTests/tryCatch/catch_error_named.sol @@ -8,4 +8,7 @@ contract C { } } // ==== -// EVMVersion: >=byzantium \ No newline at end of file +// EVMVersion: >=byzantium +// ---- +// Warning 6321: (46-50): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (52-56): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/catch_low_level.sol b/test/libsolidity/syntaxTests/tryCatch/catch_low_level.sol index 58948e211267..68e7cc32ff29 100644 --- a/test/libsolidity/syntaxTests/tryCatch/catch_low_level.sol +++ b/test/libsolidity/syntaxTests/tryCatch/catch_low_level.sol @@ -8,4 +8,7 @@ contract C { } } // ==== -// EVMVersion: >=byzantium \ No newline at end of file +// EVMVersion: >=byzantium +// ---- +// Warning 6321: (46-50): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (52-56): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/creation.sol b/test/libsolidity/syntaxTests/tryCatch/creation.sol new file mode 100644 index 000000000000..d92c179dc918 --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/creation.sol @@ -0,0 +1,13 @@ +contract D { +} +contract C { + function f() public { + try new D() { + } catch (bytes memory x) { + x; + } + } +} +// ==== +// EVMVersion: >=byzantium +// ---- diff --git a/test/libsolidity/syntaxTests/tryCatch/no_returns.sol b/test/libsolidity/syntaxTests/tryCatch/no_returns.sol index bfda49ab9573..a328530533c8 100644 --- a/test/libsolidity/syntaxTests/tryCatch/no_returns.sol +++ b/test/libsolidity/syntaxTests/tryCatch/no_returns.sol @@ -6,4 +6,7 @@ contract C { } } -} \ No newline at end of file +} +// ---- +// Warning 6321: (46-50): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (52-56): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/no_special.sol b/test/libsolidity/syntaxTests/tryCatch/no_special.sol new file mode 100644 index 000000000000..6cfed6eda844 --- /dev/null +++ b/test/libsolidity/syntaxTests/tryCatch/no_special.sol @@ -0,0 +1,17 @@ +contract C { + function f() public returns (uint, uint) { + try this { + } catch { + } + try gasleft() { + } catch { + } + try type(address) { + } catch { + } + } +} +// ---- +// TypeError 5347: (72-76): Try can only be used with external function calls and contract creation calls. +// TypeError 2536: (119-128): Try can only be used with external function calls and contract creation calls. +// TypeError 4259: (176-183): Invalid type for argument in the function call. A contract type or an integer type is required, but type(address) provided. diff --git a/test/libsolidity/syntaxTests/tryCatch/returns.sol b/test/libsolidity/syntaxTests/tryCatch/returns.sol index 507a83905eba..335270b0b5b7 100644 --- a/test/libsolidity/syntaxTests/tryCatch/returns.sol +++ b/test/libsolidity/syntaxTests/tryCatch/returns.sol @@ -8,3 +8,6 @@ contract C { } } } +// ---- +// Warning 6321: (46-50): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (52-56): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/returns_memory.sol b/test/libsolidity/syntaxTests/tryCatch/returns_memory.sol index f71a4af25c98..d575e455e20d 100644 --- a/test/libsolidity/syntaxTests/tryCatch/returns_memory.sol +++ b/test/libsolidity/syntaxTests/tryCatch/returns_memory.sol @@ -9,3 +9,6 @@ contract C { } // ==== // EVMVersion: >=byzantium +// ---- +// Warning 6321: (46-59): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (61-65): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/returns_memory_anonymous.sol b/test/libsolidity/syntaxTests/tryCatch/returns_memory_anonymous.sol index 9eb8f86c4787..4647e762e5fb 100644 --- a/test/libsolidity/syntaxTests/tryCatch/returns_memory_anonymous.sol +++ b/test/libsolidity/syntaxTests/tryCatch/returns_memory_anonymous.sol @@ -8,4 +8,7 @@ contract C { } } // ==== -// EVMVersion: >=byzantium \ No newline at end of file +// EVMVersion: >=byzantium +// ---- +// Warning 6321: (46-59): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (61-65): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tryCatch/two_catch_clauses.sol b/test/libsolidity/syntaxTests/tryCatch/two_catch_clauses.sol index fe2beb426a69..7ca4376fe1ce 100644 --- a/test/libsolidity/syntaxTests/tryCatch/two_catch_clauses.sol +++ b/test/libsolidity/syntaxTests/tryCatch/two_catch_clauses.sol @@ -11,4 +11,7 @@ contract C { } } // ==== -// EVMVersion: >=byzantium \ No newline at end of file +// EVMVersion: >=byzantium +// ---- +// Warning 6321: (46-50): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (52-56): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol index f14641cb9fcf..e941a0eb18ed 100644 --- a/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol +++ b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol @@ -21,3 +21,6 @@ contract C { } } // ---- +// Warning 6321: (194-198): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (200-204): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. +// Warning 6321: (206-213): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/types/address/address_binary_operators.sol b/test/libsolidity/syntaxTests/types/address/address_binary_operators.sol index dfeab70228dd..f26eed18af70 100644 --- a/test/libsolidity/syntaxTests/types/address/address_binary_operators.sol +++ b/test/libsolidity/syntaxTests/types/address/address_binary_operators.sol @@ -9,7 +9,7 @@ contract C { } } // ---- -// TypeError 2271: (85-108): Operator + not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them. -// TypeError 2271: (122-145): Operator - not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them. -// TypeError 2271: (159-182): Operator * not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them. -// TypeError 2271: (196-219): Operator / not compatible with types address payable and address payable. Arithmetic operations on addresses are not supported. Convert to integer first before using them. +// TypeError 2271: (85-108): Operator + not compatible with types address and address. Arithmetic operations on addresses are not supported. Convert to integer first before using them. +// TypeError 2271: (122-145): Operator - not compatible with types address and address. Arithmetic operations on addresses are not supported. Convert to integer first before using them. +// TypeError 2271: (159-182): Operator * not compatible with types address and address. Arithmetic operations on addresses are not supported. Convert to integer first before using them. +// TypeError 2271: (196-219): Operator / not compatible with types address and address. Arithmetic operations on addresses are not supported. Convert to integer first before using them. diff --git a/test/libsolidity/syntaxTests/types/address/address_constant.sol b/test/libsolidity/syntaxTests/types/address/address_constant.sol index 0b1af991709f..c8bbfc51145a 100644 --- a/test/libsolidity/syntaxTests/types/address/address_constant.sol +++ b/test/libsolidity/syntaxTests/types/address/address_constant.sol @@ -1,6 +1,6 @@ contract C { address constant a = address(0); - address payable constant b = address(0); + address payable constant b = payable(0); function f() public pure returns (address, address) { return (a,b); } diff --git a/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol b/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol index d843c5990a81..76df63cddb4d 100644 --- a/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol +++ b/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol @@ -1,9 +1,9 @@ contract C { address constant a = address(0); - address payable constant b = address(0); + address payable constant b = payable(0); function f() public { a = address(0); - b = address(0); + b = payable(0); } } // ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol b/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol index 11d5e8677106..6759bc11def3 100644 --- a/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol +++ b/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol @@ -13,8 +13,8 @@ contract B { } S s; function f() public { - s.a = address(this); + s.a = payable(this); } receive() external payable { } -} \ No newline at end of file +} diff --git a/test/libsolidity/syntaxTests/types/address/address_literal_to_payable.sol b/test/libsolidity/syntaxTests/types/address/address_literal_to_payable.sol new file mode 100644 index 000000000000..509eb0398eac --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_literal_to_payable.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + address payable a = payable(address(0x00000000219ab540356cBB839Cbe05303d7705Fa)); + address payable b = payable(0x00000000219ab540356cBB839Cbe05303d7705Fa); + a = b; + b = a; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_literal_to_payable_err.sol b/test/libsolidity/syntaxTests/types/address/address_literal_to_payable_err.sol new file mode 100644 index 000000000000..7a9edf32dfdb --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_literal_to_payable_err.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + address payable a = address(0x00000000219ab540356cBB839Cbe05303d7705Fa); + address payable b = 0x00000000219ab540356cBB839Cbe05303d7705Fa; + } +} +// ---- +// TypeError 9574: (52-123): Type address is not implicitly convertible to expected type address payable. +// TypeError 9574: (133-195): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/address_members.sol b/test/libsolidity/syntaxTests/types/address/address_members.sol new file mode 100644 index 000000000000..144361b9409a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_members.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view returns (address) { return address(this); } + function g() public view returns (uint) { return f().balance; } + function h() public view returns (bytes memory) { return f().code; } + function i() public view returns (uint) { return f().code.length; } + function j() public view returns (uint) { return h().length; } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_members_in_contract.sol b/test/libsolidity/syntaxTests/types/address/address_members_in_contract.sol index 338d9ef97e50..d3488257586a 100644 --- a/test/libsolidity/syntaxTests/types/address/address_members_in_contract.sol +++ b/test/libsolidity/syntaxTests/types/address/address_members_in_contract.sol @@ -1,6 +1,6 @@ contract C { function f() public returns (C) { return this; } - function g() public returns (uint) { return f().balance(); } + function g() public returns (uint) { return f().balance; } } // ---- // TypeError 3125: (114-125): Member "balance" not found or not visible after argument-dependent lookup in contract C. Use "address(...).balance" to access this address member. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol index e5eab5f5041b..c07dd81f6373 100644 --- a/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol +++ b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol @@ -2,7 +2,7 @@ contract C { function f(address payable) internal pure {} function f(address) internal pure {} function g() internal pure { - address payable a = address(0); + address payable a = payable(0); f(a); } } diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol index 90fb3e85fbb6..2aa032e0ecd9 100644 --- a/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol @@ -4,4 +4,4 @@ contract C { } } // ---- -// TypeError 9574: (46-62): Type address payable is not implicitly convertible to expected type contract C. +// TypeError 9574: (46-62): Type address is not implicitly convertible to expected type contract C. diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol index 69cb233427a5..6c6a7d70680c 100644 --- a/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol @@ -1,9 +1,9 @@ contract C { function f() public pure returns (C c) { - c = C(address(2)); + c = C(payable(address(2))); } fallback() external payable { } } // ---- -// Warning 3628: (0-120): This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function. +// Warning 3628: (0-129): This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function. diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract_receive.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract_receive.sol index 19b702c6e2cf..75bd2537601a 100644 --- a/test/libsolidity/syntaxTests/types/address/address_to_contract_receive.sol +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract_receive.sol @@ -1,6 +1,6 @@ contract C { function f() public pure returns (C c) { - c = C(address(2)); + c = C(payable(address(2))); } receive() external payable { } diff --git a/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol b/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol index e9d1f6375cbb..727be340444f 100644 --- a/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol +++ b/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol @@ -1,6 +1,6 @@ contract C { function f() public view returns (address payable a, address b) { - (address c, address payable d) = (address(this), address(0)); + (address c, address payable d) = (address(this), payable(0)); (a,b) = (c,d); } } diff --git a/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol b/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol index 846de1f4876c..beee4d9c7a4d 100644 --- a/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol +++ b/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol @@ -1,6 +1,6 @@ contract C { function f() public view returns (address payable a, address b) { - (address c, address payable d) = (address(this), address(0)); + (address c, address payable d) = (address(this), payable(0)); (a,b) = (d,c); } -} \ No newline at end of file +} diff --git a/test/libsolidity/syntaxTests/types/address/address_uint_bytes20_this.sol b/test/libsolidity/syntaxTests/types/address/address_uint_bytes20_this.sol new file mode 100644 index 000000000000..c1204f266d99 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_uint_bytes20_this.sol @@ -0,0 +1,19 @@ +contract C { + function f() public view { + address a1 = address(uint160(0)); + address a2 = address(bytes20(0)); + address a3 = address(this); + + // Trivial conversions + address payable a4 = payable(address(uint160(0))); + address payable a5 = payable(address(bytes20(0))); + address payable a6 = payable(address(this)); + + a1; a2; a3; a4; a5; a6; + } + + // to make payable(this) work + receive() payable external { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_uint_bytes20_this_to_payable_err.sol b/test/libsolidity/syntaxTests/types/address/address_uint_bytes20_this_to_payable_err.sol new file mode 100644 index 000000000000..37f087e5bfa2 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_uint_bytes20_this_to_payable_err.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + address payable a = address(uint160(0)); + address payable b = address(bytes20(0)); + address payable c = address(this); + } +} +// ---- +// TypeError 9574: (52-91): Type address is not implicitly convertible to expected type address payable. +// TypeError 9574: (101-140): Type address is not implicitly convertible to expected type address payable. +// TypeError 9574: (150-183): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/address_zero_to_payable_err.sol b/test/libsolidity/syntaxTests/types/address/address_zero_to_payable_err.sol new file mode 100644 index 000000000000..d72f7483f13d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_zero_to_payable_err.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + address payable a = address(0); + } +} +// ---- +// TypeError 9574: (52-82): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/bytes_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/bytes_to_payable_address.sol index 5b6a6714d827..92d87120b07a 100644 --- a/test/libsolidity/syntaxTests/types/address/bytes_to_payable_address.sol +++ b/test/libsolidity/syntaxTests/types/address/bytes_to_payable_address.sol @@ -1,5 +1,5 @@ contract C { function f(bytes20 x) public pure returns (address payable) { - return address(x); + return payable(address(x)); } } diff --git a/test/libsolidity/syntaxTests/types/address/codehash.sol b/test/libsolidity/syntaxTests/types/address/codehash.sol new file mode 100644 index 000000000000..a7c418ca7d40 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/codehash.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view returns (bytes32) { + return address(this).codehash; + } +} +// ==== +// EVMVersion: >=constantinople +// ---- \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/codehash_before_constantinople.sol b/test/libsolidity/syntaxTests/types/address/codehash_before_constantinople.sol new file mode 100644 index 000000000000..2e65e0ba38b7 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/codehash_before_constantinople.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view returns (bytes32) { + return address(this).codehash; + } +} +// ==== +// EVMVersion: =istanbul diff --git a/test/libsolidity/syntaxTests/types/mapping/contract_storage_parameter_with_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/contract_storage_parameter_with_mapping.sol new file mode 100644 index 000000000000..5e33008ba623 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/contract_storage_parameter_with_mapping.sol @@ -0,0 +1,6 @@ +struct S { mapping(uint => uint)[2] a; } +contract C { + function f(S storage s) public {} +} +// ---- +// TypeError 6651: (69-80): Data location must be "memory" or "calldata" for parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/library_mapping.sol index 3c26c7795538..1456565db8d8 100644 --- a/test/libsolidity/syntaxTests/types/mapping/library_mapping.sol +++ b/test/libsolidity/syntaxTests/types/mapping/library_mapping.sol @@ -1,4 +1,9 @@ library L {} -contract C { mapping(L => bool) i; } +contract C +{ + mapping(bool => L) j; + mapping(L => bool) i; +} // ---- -// TypeError 1665: (34-35): Library types cannot be used as mapping keys. +// TypeError 1130: (44-45): Invalid use of a library name. +// TypeError 1130: (60-61): Invalid use of a library name. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_nested_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/library_nested_mapping.sol index 31bf3cb1fcb4..5eb22d83356b 100644 --- a/test/libsolidity/syntaxTests/types/mapping/library_nested_mapping.sol +++ b/test/libsolidity/syntaxTests/types/mapping/library_nested_mapping.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; library L { struct S { mapping(uint => uint) m; } function f(S memory a) external pure returns (S memory) {} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_storage_parameter_with_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/library_storage_parameter_with_mapping.sol new file mode 100644 index 000000000000..a8ee8a3e3187 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_storage_parameter_with_mapping.sol @@ -0,0 +1,4 @@ +struct S { mapping(uint => uint)[2] a; } +library L { + function f(S storage s) public {} +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_storage_parameter_with_nested_mapping.sol b/test/libsolidity/syntaxTests/types/mapping/library_storage_parameter_with_nested_mapping.sol new file mode 100644 index 000000000000..9c4579874017 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_storage_parameter_with_nested_mapping.sol @@ -0,0 +1,5 @@ +library Test { + struct Nested { mapping(uint => uint)[2][] a; } + struct X { Nested n; } + function f(X storage x) public {} +} diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_function_calldata.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_function_calldata.sol index f2360b2d1b31..1301a03c3c51 100644 --- a/test/libsolidity/syntaxTests/types/mapping/mapping_function_calldata.sol +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_function_calldata.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract test { struct S { diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_struct_data_location_memory.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_struct_data_location_memory.sol index 648e2f74edd1..8208c2e2e86f 100644 --- a/test/libsolidity/syntaxTests/types/mapping/mapping_struct_data_location_memory.sol +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_struct_data_location_memory.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { mapping(uint => uint) a; } function f(S memory) public {} diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_struct_recusrive_data_location_memory.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_struct_recusrive_data_location_memory.sol index 350129dff2e4..fa09bdeaee5b 100644 --- a/test/libsolidity/syntaxTests/types/mapping/mapping_struct_recusrive_data_location_memory.sol +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_struct_recusrive_data_location_memory.sol @@ -1,4 +1,4 @@ -pragma experimental ABIEncoderV2; +pragma abicoder v2; contract C { struct S { mapping(uint => uint) a; } struct T { S s; } diff --git a/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol index 9b4f05c42ef6..f41eb8b7a032 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol @@ -2,4 +2,4 @@ contract c { uint[2**253] data; } // ---- -// Warning 3408: (17-34): Variable "data" covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. +// Warning 7325: (17-29): Type uint256[14474011154664524427946373126085988481658748083205070504932198000989141204992] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol index a397993b9963..9c59b015c767 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol @@ -7,7 +7,7 @@ contract c { } } // ---- -// TypeError 7407: (71-80): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. +// TypeError 7407: (71-80): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. // TypeError 2271: (133-142): Operator << not compatible with types int_const 1 and int_const 4096 // TypeError 2271: (169-182): Operator << not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2 -// TypeError 7407: (169-182): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError 7407: (169-182): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. diff --git a/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol index a989c4583a23..2d7fbdfad9e2 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol @@ -1,8 +1,8 @@ contract c { function f() public pure { int a; - a = 4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4; - a = -4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4 ** 4; + a = (((((4 ** 4) ** 2) ** 4) ** 4) ** 4) ** 4; + a = -(((4 ** 4 ** 2 ** 4 ** 4) ** 4) ** 4) ** 4; a = 4 ** (-(2 ** 4 ** 4 ** 4 ** 4 ** 4)); a = 2 ** 1E1233; a = -2 ** 1E1233; @@ -19,29 +19,29 @@ contract c { } } // ---- -// TypeError 2271: (71-102): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4. Precision of rational constants is limited to 4096 bits. -// TypeError 7407: (71-102): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. -// TypeError 2271: (116-148): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4. Precision of rational constants is limited to 4096 bits. -// TypeError 2271: (116-153): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4. Precision of rational constants is limited to 4096 bits. -// TypeError 7407: (116-153): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. -// TypeError 2271: (167-203): Operator ** not compatible with types int_const 4 and int_const -179...(302 digits omitted)...7216 -// TypeError 2271: (217-228): Operator ** not compatible with types int_const 2 and int_const 1000...(1226 digits omitted)...0000 -// TypeError 2271: (242-254): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000 -// TypeError 2271: (268-280): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000 -// TypeError 2271: (294-307): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000 -// TypeError 2271: (321-332): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2. Precision of rational constants is limited to 4096 bits. -// TypeError 7407: (321-332): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError 2271: (346-358): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2. Precision of rational constants is limited to 4096 bits. -// TypeError 7407: (346-358): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError 2271: (372-384): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2. Precision of rational constants is limited to 4096 bits. -// TypeError 7407: (372-384): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError 2271: (398-411): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2. Precision of rational constants is limited to 4096 bits. -// TypeError 7407: (398-411): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError 2271: (425-441): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 -// TypeError 7407: (425-441): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError 2271: (455-472): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 -// TypeError 7407: (455-472): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError 2271: (486-503): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 -// TypeError 7407: (486-503): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError 2271: (517-535): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 -// TypeError 7407: (517-535): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError 2271: (71-112): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 +// TypeError 7407: (71-112): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (135-151): Operator ** not compatible with types int_const 4 and int_const 1157...(70 digits omitted)...9936 +// TypeError 7407: (126-169): Type int_const 1340...(147 digits omitted)...4096 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (201-217): Operator ** not compatible with types int_const 4 and int_const 1340...(147 digits omitted)...4096 +// TypeError 2271: (183-219): Operator ** not compatible with types int_const 4 and int_const -115...(71 digits omitted)...9936 +// TypeError 2271: (233-244): Operator ** not compatible with types int_const 2 and int_const 1000...(1226 digits omitted)...0000 +// TypeError 2271: (258-270): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000 +// TypeError 2271: (284-296): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000 +// TypeError 2271: (310-323): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000 +// TypeError 2271: (337-348): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2 +// TypeError 7407: (337-348): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (362-374): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2 +// TypeError 7407: (362-374): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (388-400): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2 +// TypeError 7407: (388-400): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (414-427): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2 +// TypeError 7407: (414-427): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (441-457): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 +// TypeError 7407: (441-457): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (471-488): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 +// TypeError 7407: (471-488): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (502-519): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 +// TypeError 7407: (502-519): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. +// TypeError 2271: (533-551): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 +// TypeError 7407: (533-551): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. diff --git a/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol index 1e3239ddf5cf..cec814ef3677 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// TypeError 7407: (142-209): Type int_const 1852...(71 digits omitted)...7281 is not implicitly convertible to expected type uint256. +// TypeError 7407: (142-209): Type int_const 1852...(71 digits omitted)...7281 is not implicitly convertible to expected type uint256. Literal is too large to fit in uint256. diff --git a/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol index c2d94da36518..a0257845387e 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol @@ -6,4 +6,4 @@ contract c { } // ---- // TypeError 2271: (71-90): Operator * not compatible with types int_const 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168. Precision of rational constants is limited to 4096 bits. -// TypeError 7407: (71-90): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. +// TypeError 7407: (71-90): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. diff --git a/test/libsolidity/syntaxTests/types/rational_number_signed_to_unsigned.sol b/test/libsolidity/syntaxTests/types/rational_number_signed_to_unsigned.sol index 21989d5d84d5..94429034751a 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_signed_to_unsigned.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_signed_to_unsigned.sol @@ -1,7 +1,9 @@ contract c { function f() public pure { uint a = -1; + uint b = uint(-1); } } // ---- // TypeError 9574: (52-63): Type int_const -1 is not implicitly convertible to expected type uint256. Cannot implicitly convert signed literal to unsigned type. +// TypeError 9640: (82-90): Explicit type conversion not allowed from "int_const -1" to "uint256". diff --git a/test/libsolidity/syntaxTests/types/rational_number_too_large.sol b/test/libsolidity/syntaxTests/types/rational_number_too_large.sol index f331ac0b5e1a..b3c065f1e68b 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_too_large.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_too_large.sol @@ -1,7 +1,11 @@ -contract c { +contract C { function f() public pure { uint8 a = 256; + uint8 b = uint8(256); + int8 c = int8(-129); } } // ---- // TypeError 9574: (52-65): Type int_const 256 is not implicitly convertible to expected type uint8. Literal is too large to fit in uint8. +// TypeError 9640: (85-95): Explicit type conversion not allowed from "int_const 256" to "uint8". +// TypeError 9640: (114-124): Explicit type conversion not allowed from "int_const -129" to "int8". diff --git a/test/libsolidity/syntaxTests/types/strict_explicit.sol b/test/libsolidity/syntaxTests/types/strict_explicit.sol new file mode 100644 index 000000000000..d2b67c891005 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/strict_explicit.sol @@ -0,0 +1,42 @@ +contract B{} +contract C +{ + function f() public pure { + + uint16 a = uint16(uint8(int8(-1))); + a; + + int8 b = -1; + b; + uint16 c = uint16(uint8(b)); + c; + + int8 d = int8(int16(uint16(type(uint16).max))); + d; + + uint16 e = type(uint16).max; + e; + int8 g = int8(uint8(e)); + g; + + address h = address(uint160(uint(type(uint).max))); + h; + + uint i = uint(uint160(address(0))); + i; + + uint j = type(uint).max; + j; + address k = address(uint160(j)); + k; + + int80 l = int80(uint80(bytes10("h"))); + l; + bytes10 m = bytes10(uint80(int80(-1))); + m; + + B n = B(address(uint160(uint(int(100))))); + n; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/strict_explicit_err.sol b/test/libsolidity/syntaxTests/types/strict_explicit_err.sol new file mode 100644 index 000000000000..2f0c9a080e02 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/strict_explicit_err.sol @@ -0,0 +1,52 @@ +contract B{} + +enum E { Zero } + +contract C +{ + function f() public pure { + + uint16 a = uint16(int8(-1)); + + int8 b = -1; + uint16 c = uint16(b); + + int8 d = int8(uint16(type(uint16).max)); + + uint16 e = type(uint16).max; + int8 g = int8(e); + + address h = address(uint(type(uint).max)); + + uint i = uint(address(0)); + + uint j = type(uint).max; + address k = address(j); + + int80 l = int80(bytes10("h")); + bytes10 m = bytes10(int80(-1)); + + B n = B(int(100)); + int o = int(new B()); + + B p = B(0x00); + + int q = int(E(0)); + int r = int(E.Zero); + } +} +// ---- +// TypeError 9640: (95-111): Explicit type conversion not allowed from "int8" to "uint16". +// TypeError 9640: (154-163): Explicit type conversion not allowed from "int8" to "uint16". +// TypeError 9640: (183-213): Explicit type conversion not allowed from "uint16" to "int8". +// TypeError 9640: (270-277): Explicit type conversion not allowed from "uint16" to "int8". +// TypeError 9640: (300-329): Explicit type conversion not allowed from "uint256" to "address". +// TypeError 9640: (349-365): Explicit type conversion not allowed from "address" to "uint256". +// TypeError 9640: (421-431): Explicit type conversion not allowed from "uint256" to "address". +// TypeError 9640: (452-471): Explicit type conversion not allowed from "bytes10" to "int80". +// TypeError 9640: (493-511): Explicit type conversion not allowed from "int80" to "bytes10". +// TypeError 9640: (528-539): Explicit type conversion not allowed from "int256" to "contract B". +// TypeError 9640: (557-569): Explicit type conversion not allowed from "contract B" to "int256". +// TypeError 9640: (586-593): Explicit type conversion not allowed from "int_const 0" to "contract B". +// TypeError 9640: (612-621): Explicit type conversion not allowed from "enum E" to "int256". +// TypeError 9640: (639-650): Explicit type conversion not allowed from "enum E" to "int256". diff --git a/test/libsolidity/syntaxTests/types/weird_sized_types.sol b/test/libsolidity/syntaxTests/types/weird_sized_types.sol new file mode 100644 index 000000000000..563778d7ea0e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/weird_sized_types.sol @@ -0,0 +1,42 @@ +contract C { + uint bytes01 = 0; + uint bytes000000001 = 0; + uint bytes000099000 = 0; + uint bytes0a = 0; + + uint int0 = 0; + uint int01 = 0; + uint int000000001 = 0; + uint int000099000 = 0; + uint int0a = 0; + + uint uint0 = 0; + uint uint01 = 0; + uint uint000000001 = 0; + uint uint000099000 = 0; + uint uint0a = 0; + + uint fixed0x0 = 0; + uint fixed01x1 = 0; + uint fixed1x01 = 0; + uint fixed000000001x1 = 0; + uint fixed1x000000001 = 0; + uint fixed000099000x1 = 0; + uint fixed1x000099000 = 0; + uint fixed0ax1 = 0; + uint fixed1x0a = 0; + + uint ufixed0x0 = 0; + uint ufixed01x1 = 0; + uint ufixed1x01 = 0; + uint ufixed000000001x1 = 0; + uint ufixed1x000000001 = 0; + uint ufixed000099000x1 = 0; + uint ufixed1x000099000 = 0; + uint ufixed0ax1 = 0; + uint ufixed1x0a = 0; + + // overflow check + uint uint300 = 0; +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_explicit.sol b/test/libsolidity/syntaxTests/types/zero_literal_to_bytesNN_explicit.sol similarity index 100% rename from test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_explicit.sol rename to test/libsolidity/syntaxTests/types/zero_literal_to_bytesNN_explicit.sol diff --git a/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_implicit.sol b/test/libsolidity/syntaxTests/types/zero_literal_to_bytesNN_implicit.sol similarity index 100% rename from test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_implicit.sol rename to test/libsolidity/syntaxTests/types/zero_literal_to_bytesNN_implicit.sol diff --git a/test/libsolidity/syntaxTests/unchecked/unchecked_for_header.sol b/test/libsolidity/syntaxTests/unchecked/unchecked_for_header.sol new file mode 100644 index 000000000000..a5b4ecbdcc1c --- /dev/null +++ b/test/libsolidity/syntaxTests/unchecked/unchecked_for_header.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + for (unchecked { uint x = 2 }; x < 2; x ++) { + + } + } +} +// ---- +// ParserError 6933: (57-66): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/unchecked/unchecked_function_body.sol b/test/libsolidity/syntaxTests/unchecked/unchecked_function_body.sol new file mode 100644 index 000000000000..ef45a8e2b831 --- /dev/null +++ b/test/libsolidity/syntaxTests/unchecked/unchecked_function_body.sol @@ -0,0 +1,3 @@ +function f() pure returns (uint) unchecked {} +// ---- +// ParserError 5296: (33-42): "unchecked" blocks can only be used inside regular blocks. diff --git a/test/libsolidity/syntaxTests/unchecked/unchecked_modifier.sol b/test/libsolidity/syntaxTests/unchecked/unchecked_modifier.sol new file mode 100644 index 000000000000..2d424236929a --- /dev/null +++ b/test/libsolidity/syntaxTests/unchecked/unchecked_modifier.sol @@ -0,0 +1,5 @@ +contract C { + modifier m() { unchecked { _; } } +} +// ---- +// SyntaxError 2573: (44-45): The placeholder statement "_" cannot be used inside an "unchecked" block. diff --git a/test/libsolidity/syntaxTests/unchecked/unchecked_nested.sol b/test/libsolidity/syntaxTests/unchecked/unchecked_nested.sol new file mode 100644 index 000000000000..6c64ce52205e --- /dev/null +++ b/test/libsolidity/syntaxTests/unchecked/unchecked_nested.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + unchecked { + unchecked { + uint x = 2 + 3; + } + } + } +} +// ---- +// SyntaxError 1941: (76-133): "unchecked" blocks cannot be nested. diff --git a/test/libsolidity/syntaxTests/unchecked/unchecked_post_for.sol b/test/libsolidity/syntaxTests/unchecked/unchecked_post_for.sol new file mode 100644 index 000000000000..2b77920dce9b --- /dev/null +++ b/test/libsolidity/syntaxTests/unchecked/unchecked_post_for.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + for (uint x = 2; x < 2; unchecked { x ++; }) { + + } + } +} +// ---- +// ParserError 6933: (76-85): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/unchecked/unchecked_vardecl.sol b/test/libsolidity/syntaxTests/unchecked/unchecked_vardecl.sol new file mode 100644 index 000000000000..b49112f5eac1 --- /dev/null +++ b/test/libsolidity/syntaxTests/unchecked/unchecked_vardecl.sol @@ -0,0 +1,8 @@ +contract C { + uint x = unchecked { f() + 2 } + function f() public pure returns (uint) { + return 4; + } +} +// ---- +// ParserError 6933: (26-35): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/unchecked/unchecked_while_body.sol b/test/libsolidity/syntaxTests/unchecked/unchecked_while_body.sol new file mode 100644 index 000000000000..4b7c6f2abc9c --- /dev/null +++ b/test/libsolidity/syntaxTests/unchecked/unchecked_while_body.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + while (true) unchecked { + + } + } +} +// ---- +// ParserError 5296: (65-74): "unchecked" blocks can only be used inside regular blocks. diff --git a/test/libsolidity/syntaxTests/underscore/as_function.sol b/test/libsolidity/syntaxTests/underscore/as_function.sol new file mode 100644 index 000000000000..e8bfdae4dc5d --- /dev/null +++ b/test/libsolidity/syntaxTests/underscore/as_function.sol @@ -0,0 +1,13 @@ +contract C { + function _() public pure { + } + + function g() public pure { + _(); + } + + function h() public pure { + _; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/underscore/in_function.sol b/test/libsolidity/syntaxTests/underscore/in_function.sol new file mode 100644 index 000000000000..5b41e889ace9 --- /dev/null +++ b/test/libsolidity/syntaxTests/underscore/in_function.sol @@ -0,0 +1,19 @@ +contract C { + function f() public pure returns (uint) { + uint _; + return _; + } + + function g() public pure returns (uint) { + uint _ = 1; + return _; + } + + function h() public pure { + _; + } +} +// ---- +// DeclarationError 3726: (67-73): The name "_" is reserved. +// DeclarationError 3726: (154-160): The name "_" is reserved. +// DeclarationError 7576: (230-231): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/underscore/in_modifier.sol b/test/libsolidity/syntaxTests/underscore/in_modifier.sol new file mode 100644 index 000000000000..5e330d010f32 --- /dev/null +++ b/test/libsolidity/syntaxTests/underscore/in_modifier.sol @@ -0,0 +1,19 @@ +contract C { + modifier m() { + _; + } + + modifier n() { + string memory _ = ""; + _; + revert(_); + } + + function f() m() public { + } + + function g() n() public { + } +} +// ---- +// DeclarationError 3726: (77-92): The name "_" is reserved. diff --git a/test/libsolidity/syntaxTests/unexpected.sol b/test/libsolidity/syntaxTests/unexpected.sol index 79ee1b6cbecb..1ffa40c21c97 100644 --- a/test/libsolidity/syntaxTests/unexpected.sol +++ b/test/libsolidity/syntaxTests/unexpected.sol @@ -1,3 +1,3 @@ unexpected // ---- -// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum definition. +// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition. diff --git a/test/libsolidity/syntaxTests/unimplemented_super_function.sol b/test/libsolidity/syntaxTests/unimplemented_super_function.sol index 5da68a0eaa35..22b98df0214f 100644 --- a/test/libsolidity/syntaxTests/unimplemented_super_function.sol +++ b/test/libsolidity/syntaxTests/unimplemented_super_function.sol @@ -5,4 +5,4 @@ contract b is a { function f() public override { super.f(); } } // ---- -// TypeError 9582: (110-117): Member "f" not found or not visible after argument-dependent lookup in contract super b. +// TypeError 9582: (110-117): Member "f" not found or not visible after argument-dependent lookup in type(contract super b). diff --git a/test/libsolidity/syntaxTests/unimplemented_super_function_derived.sol b/test/libsolidity/syntaxTests/unimplemented_super_function_derived.sol index f3213e72fb83..6a683b4612d8 100644 --- a/test/libsolidity/syntaxTests/unimplemented_super_function_derived.sol +++ b/test/libsolidity/syntaxTests/unimplemented_super_function_derived.sol @@ -9,4 +9,4 @@ contract c is a,b { function f() public override(a, b) { super.f(); } } // ---- -// TypeError 9582: (118-125): Member "f" not found or not visible after argument-dependent lookup in contract super b. +// TypeError 9582: (118-125): Member "f" not found or not visible after argument-dependent lookup in type(contract super b). diff --git a/test/libsolidity/syntaxTests/variableDeclaration/illegal_names.sol b/test/libsolidity/syntaxTests/variableDeclaration/illegal_names.sol new file mode 100644 index 000000000000..24d1d98d6e67 --- /dev/null +++ b/test/libsolidity/syntaxTests/variableDeclaration/illegal_names.sol @@ -0,0 +1,30 @@ +uint constant this = 1; +uint constant super = 2; +uint constant _ = 3; +contract C { + address this; + int super; + mapping (address => address) _; +} + +contract D { + address[] this; + struct _ { uint super; } +} +// ---- +// DeclarationError 3726: (0-22): The name "this" is reserved. +// DeclarationError 3726: (24-47): The name "super" is reserved. +// DeclarationError 3726: (49-68): The name "_" is reserved. +// DeclarationError 3726: (84-96): The name "this" is reserved. +// DeclarationError 3726: (99-108): The name "super" is reserved. +// DeclarationError 3726: (111-141): The name "_" is reserved. +// DeclarationError 3726: (160-174): The name "this" is reserved. +// DeclarationError 3726: (177-201): The name "_" is reserved. +// DeclarationError 3726: (188-198): The name "super" is reserved. +// Warning 2519: (84-96): This declaration shadows an existing declaration. +// Warning 2519: (99-108): This declaration shadows an existing declaration. +// Warning 2519: (111-141): This declaration shadows an existing declaration. +// Warning 2519: (160-174): This declaration shadows an existing declaration. +// Warning 2519: (177-201): This declaration shadows an existing declaration. +// Warning 2319: (0-22): This declaration shadows a builtin symbol. +// Warning 2319: (24-47): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/address.sol b/test/libsolidity/syntaxTests/viewPureChecker/address.sol new file mode 100644 index 000000000000..5c17dac6415d --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/address.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view returns (uint) { + return address(this).balance; + } + function g() public view returns (uint) { + return address(0).balance; + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/address_constantinople.sol b/test/libsolidity/syntaxTests/viewPureChecker/address_constantinople.sol new file mode 100644 index 000000000000..4387ca60a75b --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/address_constantinople.sol @@ -0,0 +1,11 @@ +contract C { + function f() public view returns (bytes32) { + return address(this).codehash; + } + function g() public view returns (bytes32) { + return address(0).codehash; + } +} +// ==== +// EVMVersion: >=constantinople +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/assembly.sol b/test/libsolidity/syntaxTests/viewPureChecker/assembly.sol index c3ab88b5bd98..8e1081385719 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/assembly.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/assembly.sol @@ -14,10 +14,16 @@ contract C { function h() view public { assembly { function g() { pop(blockhash(20)) } } } - function j() public { + function i() public { assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) } } - function k() public { + function j() public { assembly { pop(call(gas(), 1, 2, 3, 4, 5, 6)) } } + function k() public view { + assembly { pop(balance(0)) } + } + function l() public view { + assembly { pop(extcodesize(0)) } + } } diff --git a/test/libsolidity/syntaxTests/viewPureChecker/assembly_chainid_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/assembly_chainid_not_pure.sol new file mode 100644 index 000000000000..2f681ed57009 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/assembly_chainid_not_pure.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { pop(chainid()) } + } + function g() public pure returns (uint) { + return block.chainid; + } +} +// ==== +// EVMVersion: >=istanbul +// ---- +// TypeError 2527: (67-76): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (147-160): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/assembly_constantinople.sol b/test/libsolidity/syntaxTests/viewPureChecker/assembly_constantinople.sol new file mode 100644 index 000000000000..ed0b20d53793 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/assembly_constantinople.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view { + assembly { pop(extcodehash(0)) } + } +} +// ==== +// EVMVersion: >=constantinople +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/assembly_istanbul.sol b/test/libsolidity/syntaxTests/viewPureChecker/assembly_istanbul.sol new file mode 100644 index 000000000000..1f4fb430983f --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/assembly_istanbul.sol @@ -0,0 +1,11 @@ +contract C { + function f() public view { + assembly { pop(chainid()) } + } + function g() public view returns (uint) { + return block.chainid; + } +} +// ==== +// EVMVersion: >=istanbul +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol index f828a0cf13ad..225a954cc836 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol @@ -1,8 +1,8 @@ contract C { function f() public { - address(this).transfer(1); - require(address(this).send(2)); - selfdestruct(address(this)); + payable(this).transfer(1); + require(payable(this).send(2)); + selfdestruct(payable(this)); (bool success,) = address(this).delegatecall(""); require(success); (success,) = address(this).call(""); diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol index ffd412375da7..000f5b009b78 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol @@ -1,12 +1,12 @@ contract C { function f() view public { - address(this).transfer(1); + payable(this).transfer(1); } function g() view public { - require(address(this).send(2)); + require(payable(this).send(2)); } function h() view public { - selfdestruct(address(this)); + selfdestruct(payable(this)); } function i() view public { (bool success,) = address(this).delegatecall(""); diff --git a/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_success.sol b/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_success.sol index 5aa21ce1a982..740f83cc8503 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_success.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_success.sol @@ -4,3 +4,5 @@ contract C { function h() public { h(); g(); f(); } function i() payable public { i(); h(); g(); f(); } } +// ---- +// Warning 6321: (89-93): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol index 2ef5d10235b6..a1081959ea8c 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol @@ -5,4 +5,3 @@ contract B { } } // ---- -// TypeError 2527: (100-101): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/selector_complex2.sol b/test/libsolidity/syntaxTests/viewPureChecker/selector_complex2.sol index d1543fed5f4f..2680e5e0b90a 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/selector_complex2.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/selector_complex2.sol @@ -3,7 +3,7 @@ contract C { return this; } function g() pure public returns (bytes4) { - C x = C(0x123); + C x = C(address(0x123)); return x.f.selector; } } diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol b/test/libsolidity/syntaxTests/viewPureChecker/view_pure_abi_encode.sol similarity index 100% rename from test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol rename to test/libsolidity/syntaxTests/viewPureChecker/view_pure_abi_encode.sol diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol b/test/libsolidity/syntaxTests/viewPureChecker/view_pure_abi_encode_arguments.sol similarity index 100% rename from test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol rename to test/libsolidity/syntaxTests/viewPureChecker/view_pure_abi_encode_arguments.sol diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index d095a0a7647c..23fa8684cbd8 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -36,11 +36,9 @@ using namespace solidity; using namespace solidity::util; -using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -using namespace soltest; bytes BytesUtils::alignLeft(bytes _bytes) { @@ -218,7 +216,7 @@ string BytesUtils::formatRawBytes( auto it = _bytes.begin(); if (_bytes.size() != ContractABIUtils::encodingSize(_parameters)) - parameters = ContractABIUtils::defaultParameters(ceil(_bytes.size() / 32)); + parameters = ContractABIUtils::defaultParameters((_bytes.size() + 31) / 32); else parameters = _parameters; @@ -266,13 +264,13 @@ string BytesUtils::formatBytes( { auto entropy = [](std::string const& str) -> double { double result = 0; - map frequencies; + map frequencies; for (char c: str) frequencies[c]++; for (auto p: frequencies) { - double freq = static_cast(p.second) / str.length(); - result -= freq * (log(freq) / log(2)); + double freq = p.second / double(str.length()); + result -= freq * (log(freq) / log(2.0)); } return result; }; @@ -320,7 +318,7 @@ string BytesUtils::formatBytesRange( auto it = _bytes.begin(); if (_bytes.size() != ContractABIUtils::encodingSize(_parameters)) - parameters = ContractABIUtils::defaultParameters(ceil(_bytes.size() / 32)); + parameters = ContractABIUtils::defaultParameters((_bytes.size() + 31) / 32); else parameters = _parameters; diff --git a/test/libsolidity/util/ContractABIUtils.cpp b/test/libsolidity/util/ContractABIUtils.cpp index a3a19e6e5129..70e20c185d7c 100644 --- a/test/libsolidity/util/ContractABIUtils.cpp +++ b/test/libsolidity/util/ContractABIUtils.cpp @@ -20,6 +20,9 @@ #include +#include +#include + #include #include @@ -38,7 +41,6 @@ using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::frontend::test; using namespace std; -using namespace soltest; namespace { @@ -314,20 +316,34 @@ solidity::frontend::test::ParameterList ContractABIUtils::defaultParameters(size return parameters; } -solidity::frontend::test::ParameterList ContractABIUtils::failureParameters(bytes const _bytes) +solidity::frontend::test::ParameterList ContractABIUtils::failureParameters(bytes const& _bytes) { - ParameterList parameters; - - parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::HexString, ABIType::AlignNone, 4}, FormatInfo{}}); - parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::Hex}, FormatInfo{}}); - parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::UnsignedDec}, FormatInfo{}}); - - /// If _bytes contains at least a 1 byte message (function selector + tail pointer + message length + message) - /// append an additional string parameter to represent that message. - if (_bytes.size() > 68) - parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::String}, FormatInfo{}}); + if (_bytes.empty()) + return {}; + else if (_bytes.size() < 4) + return {Parameter{bytes(), "", ABIType{ABIType::HexString, ABIType::AlignNone, _bytes.size()}, FormatInfo{}}}; + else + { + ParameterList parameters; + parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::HexString, ABIType::AlignNone, 4}, FormatInfo{}}); - return parameters; + uint64_t selector = fromBigEndian(bytes{_bytes.begin(), _bytes.begin() + 4}); + if (selector == selectorFromSignature32("Panic(uint256)")) + parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::Hex}, FormatInfo{}}); + else if (selector == selectorFromSignature32("Error(string)")) + { + parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::Hex}, FormatInfo{}}); + parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::UnsignedDec}, FormatInfo{}}); + /// If _bytes contains at least a 1 byte message (function selector + tail pointer + message length + message) + /// append an additional string parameter to represent that message. + if (_bytes.size() > 68) + parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::String}, FormatInfo{}}); + } + else + for (size_t i = 4; i < _bytes.size(); i += 32) + parameters.push_back(Parameter{bytes(), "", ABIType{ABIType::HexString, ABIType::AlignNone, 32}, FormatInfo{}}); + return parameters; + } } size_t ContractABIUtils::encodingSize( diff --git a/test/libsolidity/util/ContractABIUtils.h b/test/libsolidity/util/ContractABIUtils.h index 0e56c98e57b2..209eca4232ba 100644 --- a/test/libsolidity/util/ContractABIUtils.h +++ b/test/libsolidity/util/ContractABIUtils.h @@ -65,7 +65,7 @@ class ContractABIUtils /// returned values in case of a failure. Creates an additional parameter /// for the error message if _bytes is larger than 68 bytes /// (function_selector + tail_ptr + message_length). - static ParameterList failureParameters(bytes const _bytes); + static ParameterList failureParameters(bytes const& _bytes); /// Returns _count parameters with their type set to ABIType::UnsignedDec /// and their size set to 32 bytes. diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index 26b92d5356cb..e0f6caa1f26a 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -16,7 +16,6 @@ #include #include -#include namespace solidity::frontend::test { @@ -58,6 +57,7 @@ namespace solidity::frontend::test K(Library, "library", 0) \ K(Right, "right", 0) \ K(Failure, "FAILURE", 0) \ + K(Storage, "storage", 0) \ namespace soltest { @@ -275,16 +275,23 @@ struct FunctionCall MultiLine }; DisplayMode displayMode = DisplayMode::SingleLine; - /// Marks this function call as the constructor. - bool isConstructor = false; - /// If this function call's signature has no name and no arguments, - /// a low-level call with unstructured calldata will be issued. - bool useCallWithoutSignature = false; + + enum class Kind { + Regular, + /// Marks this function call as the constructor. + Constructor, + /// If this function call's signature has no name and no arguments, + /// a low-level call with unstructured calldata will be issued. + LowLevel, + /// Marks a library deployment call. + Library, + /// Check that the storage of the current contract is empty or non-empty. + Storage + }; + Kind kind = Kind::Regular; /// Marks this function call as "short-handed", meaning /// no `->` declared. bool omitsArrow = true; - /// Marks a library deployment call. - bool isLibrary = false; }; } diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 046193d39112..7654e384d1a1 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -34,11 +34,11 @@ #include using namespace solidity; -using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace std; -using namespace soltest; + +using Token = soltest::Token; char TestFileParser::Scanner::peek() const noexcept { @@ -82,12 +82,31 @@ vector TestFileParser::parseFunctionCall expect(Token::Colon); call.signature = m_scanner.currentLiteral(); expect(Token::Identifier); - call.isLibrary = true; + call.kind = FunctionCall::Kind::Library; + call.expectations.failure = false; + } + else if (accept(Token::Storage, true)) + { + expect(Token::Colon); call.expectations.failure = false; + call.expectations.result.push_back(Parameter()); + // empty / non-empty is encoded as false / true + if (m_scanner.currentLiteral() == "empty") + call.expectations.result.back().rawBytes = bytes(1, uint8_t(false)); + else if (m_scanner.currentLiteral() == "nonempty") + call.expectations.result.back().rawBytes = bytes(1, uint8_t(true)); + else + throw TestParserError("Expected \"empty\" or \"nonempty\"."); + call.kind = FunctionCall::Kind::Storage; + m_scanner.scanNextToken(); } else { - tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature(); + bool lowLevelCall = false; + tie(call.signature, lowLevelCall) = parseFunctionSignature(); + if (lowLevelCall) + call.kind = FunctionCall::Kind::LowLevel; + if (accept(Token::Comma, true)) call.value = parseFunctionCallValue(); @@ -124,8 +143,7 @@ vector TestFileParser::parseFunctionCall call.expectations.comment = parseComment(); if (call.signature == "constructor()") - call.isConstructor = true; - + call.kind = FunctionCall::Kind::Constructor; } calls.emplace_back(std::move(call)); @@ -140,7 +158,7 @@ vector TestFileParser::parseFunctionCall return calls; } -bool TestFileParser::accept(soltest::Token _token, bool const _expect) +bool TestFileParser::accept(Token _token, bool const _expect) { if (m_scanner.currentToken() != _token) return false; @@ -149,7 +167,7 @@ bool TestFileParser::accept(soltest::Token _token, bool const _expect) return true; } -bool TestFileParser::expect(soltest::Token _token, bool const _advance) +bool TestFileParser::expect(Token _token, bool const _advance) { if (m_scanner.currentToken() != _token || m_scanner.currentToken() == Token::Invalid) throw TestParserError( @@ -466,30 +484,31 @@ void TestFileParser::Scanner::readStream(istream& _stream) void TestFileParser::Scanner::scanNextToken() { - using namespace langutil; - // Make code coverage happy. assert(formatToken(Token::NUM_TOKENS) == ""); - auto detectKeyword = [](std::string const& _literal = "") -> TokenDesc { - if (_literal == "true") return TokenDesc{Token::Boolean, _literal}; - if (_literal == "false") return TokenDesc{Token::Boolean, _literal}; - if (_literal == "ether") return TokenDesc{Token::Ether, _literal}; - if (_literal == "wei") return TokenDesc{Token::Wei, _literal}; - if (_literal == "left") return TokenDesc{Token::Left, _literal}; - if (_literal == "library") return TokenDesc{Token::Library, _literal}; - if (_literal == "right") return TokenDesc{Token::Right, _literal}; - if (_literal == "hex") return TokenDesc{Token::Hex, _literal}; - if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal}; - return TokenDesc{Token::Identifier, _literal}; + auto detectKeyword = [](std::string const& _literal = "") -> std::pair { + if (_literal == "true") return {Token::Boolean, "true"}; + if (_literal == "false") return {Token::Boolean, "false"}; + if (_literal == "ether") return {Token::Ether, ""}; + if (_literal == "wei") return {Token::Wei, ""}; + if (_literal == "left") return {Token::Left, ""}; + if (_literal == "library") return {Token::Library, ""}; + if (_literal == "right") return {Token::Right, ""}; + if (_literal == "hex") return {Token::Hex, ""}; + if (_literal == "FAILURE") return {Token::Failure, ""}; + if (_literal == "storage") return {Token::Storage, ""}; + return {Token::Identifier, _literal}; }; - auto selectToken = [this](Token _token, std::string const& _literal = "") -> TokenDesc { + auto selectToken = [this](Token _token, std::string const& _literal = "") { advance(); - return make_pair(_token, !_literal.empty() ? _literal : formatToken(_token)); + m_currentToken = _token; + m_currentLiteral = _literal; }; - TokenDesc token = make_pair(Token::Unknown, ""); + m_currentToken = Token::Unknown; + m_currentLiteral = ""; do { switch(current()) @@ -497,71 +516,73 @@ void TestFileParser::Scanner::scanNextToken() case '/': advance(); if (current() == '/') - token = selectToken(Token::Newline); + selectToken(Token::Newline); else - token = selectToken(Token::Invalid); + selectToken(Token::Invalid); break; case '-': if (peek() == '>') { advance(); - token = selectToken(Token::Arrow); + selectToken(Token::Arrow); } else - token = selectToken(Token::Sub); + selectToken(Token::Sub); break; case ':': - token = selectToken(Token::Colon); + selectToken(Token::Colon); break; case '#': - token = selectToken(Token::Comment, scanComment()); + selectToken(Token::Comment, scanComment()); break; case ',': - token = selectToken(Token::Comma); + selectToken(Token::Comma); break; case '(': - token = selectToken(Token::LParen); + selectToken(Token::LParen); break; case ')': - token = selectToken(Token::RParen); + selectToken(Token::RParen); break; case '[': - token = selectToken(Token::LBrack); + selectToken(Token::LBrack); break; case ']': - token = selectToken(Token::RBrack); + selectToken(Token::RBrack); break; case '\"': - token = selectToken(Token::String, scanString()); + selectToken(Token::String, scanString()); break; default: - if (isIdentifierStart(current())) + if (langutil::isIdentifierStart(current())) { - TokenDesc detectedToken = detectKeyword(scanIdentifierOrKeyword()); - token = selectToken(detectedToken.first, detectedToken.second); + std::tie(m_currentToken, m_currentLiteral) = detectKeyword(scanIdentifierOrKeyword()); + advance(); } - else if (isDecimalDigit(current())) + else if (langutil::isDecimalDigit(current())) { if (current() == '0' && peek() == 'x') { advance(); advance(); - token = selectToken(Token::HexNumber, "0x" + scanHexNumber()); + selectToken(Token::HexNumber, "0x" + scanHexNumber()); } else - token = selectToken(Token::Number, scanDecimalNumber()); + selectToken(Token::Number, scanDecimalNumber()); } - else if (isWhiteSpace(current())) - token = selectToken(Token::Whitespace); + else if (langutil::isWhiteSpace(current())) + selectToken(Token::Whitespace); else if (isEndOfLine()) - token = make_pair(Token::EOS, "EOS"); + { + m_currentToken = Token::EOS; + m_currentLiteral = ""; + } else throw TestParserError("Unexpected character: '" + string{current()} + "'"); break; } } - while (token.first == Token::Whitespace); - m_currentToken = token; + while (m_currentToken == Token::Whitespace); } string TestFileParser::Scanner::scanComment() @@ -661,11 +682,12 @@ string TestFileParser::Scanner::scanString() return str; } +// TODO: use fromHex() from CommonData char TestFileParser::Scanner::scanHexPart() { advance(); // skip 'x' - char value{}; + int value{}; if (isdigit(current())) value = current() - '0'; else if (tolower(current()) >= 'a' && tolower(current()) <= 'f') @@ -675,7 +697,7 @@ char TestFileParser::Scanner::scanHexPart() advance(); if (current() == '"') - return value; + return static_cast(value); value <<= 4; if (isdigit(current())) @@ -685,5 +707,5 @@ char TestFileParser::Scanner::scanHexPart() advance(); - return value; + return static_cast(value); } diff --git a/test/libsolidity/util/TestFileParser.h b/test/libsolidity/util/TestFileParser.h index 502279d2dbb1..63a95dd4a685 100644 --- a/test/libsolidity/util/TestFileParser.h +++ b/test/libsolidity/util/TestFileParser.h @@ -15,7 +15,6 @@ #pragma once #include -#include #include #include @@ -44,7 +43,8 @@ namespace solidity::frontend::test * // h(uint256), 1 ether: 42 * // -> FAILURE # If REVERT or other EVM failure was detected # * // () # Call fallback function # - * // (), 1 ether # Call ether function # + * // (), 1 ether # Call receive ether function # + * // EMPTY_STORAGE # Check that storage is empty * ... */ class TestFileParser @@ -63,7 +63,6 @@ class TestFileParser std::vector parseFunctionCalls(std::size_t _lineOffset); private: - using Token = soltest::Token; /** * Token scanner that is used internally to abstract away character traversal. */ @@ -80,8 +79,8 @@ class TestFileParser /// Reads character stream and creates token. void scanNextToken(); - soltest::Token currentToken() { return m_currentToken.first; } - std::string currentLiteral() { return m_currentToken.second; } + soltest::Token currentToken() { return m_currentToken; } + std::string currentLiteral() { return m_currentLiteral; } std::string scanComment(); std::string scanIdentifierOrKeyword(); @@ -91,8 +90,6 @@ class TestFileParser char scanHexPart(); private: - using TokenDesc = std::pair; - /// Advances current position in the input stream. void advance(unsigned n = 1) { @@ -120,8 +117,7 @@ class TestFileParser std::string::const_iterator m_char; std::string m_currentLiteral; - - TokenDesc m_currentToken; + soltest::Token m_currentToken = soltest::Token::Unknown; }; bool accept(soltest::Token _token, bool const _expect = false); diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 6b3ba2d7a67f..242b721b9d02 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -39,6 +39,9 @@ namespace solidity::frontend::test using fmt = ExecutionFramework; using Mode = FunctionCall::DisplayMode; +namespace +{ + vector parse(string const& _source) { istringstream stream{_source, ios_base::out}; @@ -82,8 +85,10 @@ void testFunctionCall( } } - BOOST_REQUIRE_EQUAL(_call.isConstructor, _isConstructor); - BOOST_REQUIRE_EQUAL(_call.isLibrary, _isLibrary); + BOOST_REQUIRE_EQUAL(_call.kind == FunctionCall::Kind::Constructor, _isConstructor); + BOOST_REQUIRE_EQUAL(_call.kind == FunctionCall::Kind::Library, _isLibrary); +} + } BOOST_AUTO_TEST_SUITE(TestFileParserTest) @@ -953,6 +958,28 @@ BOOST_AUTO_TEST_CASE(library) ); } +BOOST_AUTO_TEST_CASE(empty_storage) +{ + char const* source = R"( + // storage: empty + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage); + BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 0)); +} + +BOOST_AUTO_TEST_CASE(nonempty_storage) +{ + char const* source = R"( + // storage: nonempty + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage); + BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 1)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index f961c615c5e5..e13fccf0916d 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -30,6 +30,8 @@ using namespace solidity::util; using namespace solidity::frontend::test; using namespace std; +using Token = soltest::Token; + string TestFunctionCall::format( ErrorReporter& _errorReporter, string const& _linePrefix, @@ -37,9 +39,6 @@ string TestFunctionCall::format( bool const _highlight ) const { - using namespace soltest; - using Token = soltest::Token; - stringstream stream; bool highlight = !matchesExpectation() && _highlight; @@ -56,11 +55,25 @@ string TestFunctionCall::format( string newline = formatToken(Token::Newline); string failure = formatToken(Token::Failure); - if (m_call.isLibrary) + if (m_call.kind == FunctionCall::Kind::Library) { stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature; return; } + else if (m_call.kind == FunctionCall::Kind::Storage) + { + stream << _linePrefix << newline << ws << "storage" << colon << ws; + soltestAssert(m_rawBytes.size() == 1, ""); + soltestAssert(m_call.expectations.rawBytes().size() == 1, ""); + bool isEmpty = _renderResult ? m_rawBytes.front() == 0 : m_call.expectations.rawBytes().front() == 0; + string output = isEmpty ? "empty" : "nonempty"; + if (_renderResult && !matchesExpectation()) + AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << output; + else + stream << output; + + return; + } /// Formats the function signature. This is the same independent from the display-mode. stream << _linePrefix << newline << ws << m_call.signature; @@ -159,7 +172,7 @@ string TestFunctionCall::format( BytesUtils::formatRawBytes(output, abiParams.value(), _linePrefix) : BytesUtils::formatRawBytes( output, - ContractABIUtils::defaultParameters(ceil(output.size() / 32)), + ContractABIUtils::defaultParameters((output.size() + 31) / 32), _linePrefix ); @@ -248,7 +261,7 @@ string TestFunctionCall::formatBytesParameters( } else { - ParameterList defaultParameters = ContractABIUtils::defaultParameters(ceil(_bytes.size() / 32)); + ParameterList defaultParameters = ContractABIUtils::defaultParameters((_bytes.size() + 31) / 32); ContractABIUtils::overwriteParameters(_errorReporter, defaultParameters, _parameters); os << BytesUtils::formatBytesRange(_bytes, defaultParameters, _highlight); @@ -265,8 +278,6 @@ string TestFunctionCall::formatFailure( bool _highlight ) const { - using Token = soltest::Token; - stringstream os; os << formatToken(Token::Failure); @@ -300,7 +311,8 @@ string TestFunctionCall::formatRawParameters( { if (param.format.newline) os << endl << _linePrefix << "// "; - os << param.rawString; + for (auto const c: param.rawString) + os << (c >= ' ' ? string(1, c) : "\\x" + toHex(static_cast(c))); if (¶m != &_params.back()) os << ", "; } diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index 6f21fc9eb4cb..7daac093acd1 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/test/libsolutil/FixedHash.cpp b/test/libsolutil/FixedHash.cpp new file mode 100644 index 000000000000..4efe0a9e1023 --- /dev/null +++ b/test/libsolutil/FixedHash.cpp @@ -0,0 +1,272 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Unit tests for FixedHash. + */ + +#include + +#include + +#include +#include + +using namespace std; + +namespace solidity::util::test +{ + +static_assert(std::is_same>()); +static_assert(std::is_same>()); + +BOOST_AUTO_TEST_SUITE(FixedHashTest) + +BOOST_AUTO_TEST_CASE(default_constructor) +{ + BOOST_CHECK_EQUAL( + FixedHash<1>{}.hex(), + "00" + ); + BOOST_CHECK_EQUAL( + FixedHash<1>{}.size, + 1 + ); + BOOST_CHECK_EQUAL( + FixedHash<8>{}.hex(), + "0000000000000000" + ); + BOOST_CHECK_EQUAL( + FixedHash<8>{}.size, + 8 + ); + BOOST_CHECK_EQUAL( + FixedHash<20>{}.hex(), + "0000000000000000000000000000000000000000" + ); + BOOST_CHECK_EQUAL( + FixedHash<20>{}.size, + 20 + ); + BOOST_CHECK_EQUAL( + FixedHash<32>{}.hex(), + "0000000000000000000000000000000000000000000000000000000000000000" + ); + BOOST_CHECK_EQUAL( + FixedHash<32>{}.size, + 32 + ); +} + +BOOST_AUTO_TEST_CASE(bytes_constructor) +{ + FixedHash<8> a(bytes{}); + BOOST_CHECK_EQUAL(a.size, 8); + BOOST_CHECK_EQUAL(a.hex(), "0000000000000000"); + + FixedHash<8> b(bytes{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}); + BOOST_CHECK_EQUAL(b.size, 8); + BOOST_CHECK_EQUAL(b.hex(), "1122334455667788"); + + // TODO: short input, this should fail + FixedHash<8> c(bytes{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}); + BOOST_CHECK_EQUAL(c.size, 8); + BOOST_CHECK_EQUAL(c.hex(), "0000000000000000"); + + // TODO: oversized input, this should fail + FixedHash<8> d(bytes{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99}); + BOOST_CHECK_EQUAL(d.size, 8); + BOOST_CHECK_EQUAL(d.hex(), "0000000000000000"); +} + +// TODO: add FixedHash(bytesConstRef) constructor + +BOOST_AUTO_TEST_CASE(string_constructor_fromhex) +{ + // TODO: this tests the default settings ConstructFromStringType::fromHex, ConstructFromHashType::FailIfDifferent + // should test other options too + + FixedHash<8> a(""); + BOOST_CHECK_EQUAL(a.size, 8); + BOOST_CHECK_EQUAL(a.hex(), "0000000000000000"); + + FixedHash<8> b("1122334455667788"); + BOOST_CHECK_EQUAL(b.size, 8); + BOOST_CHECK_EQUAL(b.hex(), "1122334455667788"); + + FixedHash<8> c("0x1122334455667788"); + BOOST_CHECK_EQUAL(c.size, 8); + BOOST_CHECK_EQUAL(c.hex(), "1122334455667788"); + + // TODO: short input, this should fail + FixedHash<8> d("112233445566"); + BOOST_CHECK_EQUAL(d.size, 8); + BOOST_CHECK_EQUAL(d.hex(), "0000000000000000"); + + // TODO: oversized input, this should fail + FixedHash<8> e("112233445566778899"); + BOOST_CHECK_EQUAL(e.size, 8); + BOOST_CHECK_EQUAL(e.hex(), "0000000000000000"); +} + +BOOST_AUTO_TEST_CASE(string_constructor_frombytes) +{ + + FixedHash<8> b("", FixedHash<8>::FromBinary); + BOOST_CHECK_EQUAL(b.size, 8); + BOOST_CHECK_EQUAL(b.hex(), "0000000000000000"); + + FixedHash<8> c("abcdefgh", FixedHash<8>::FromBinary); + BOOST_CHECK_EQUAL(c.size, 8); + BOOST_CHECK_EQUAL(c.hex(), "6162636465666768"); + + // TODO: short input, this should fail + FixedHash<8> d("abcdefg", FixedHash<8>::FromBinary); + BOOST_CHECK_EQUAL(d.size, 8); + BOOST_CHECK_EQUAL(d.hex(), "0000000000000000"); + + // TODO: oversized input, this should fail + FixedHash<8> e("abcdefghi", FixedHash<8>::FromBinary); + BOOST_CHECK_EQUAL(e.size, 8); + BOOST_CHECK_EQUAL(e.hex(), "0000000000000000"); +} + +BOOST_AUTO_TEST_CASE(converting_constructor) +{ + // Truncation + FixedHash<8> a = FixedHash<8>(FixedHash<12>("112233445566778899001122")); + BOOST_CHECK_EQUAL(a.size, 8); + BOOST_CHECK_EQUAL(a.hex(), "1122334455667788"); + + // Left-aligned extension + FixedHash<12> b = FixedHash<12>(FixedHash<8>("1122334455667788"), FixedHash<12>::AlignLeft); + BOOST_CHECK_EQUAL(b.size, 12); + BOOST_CHECK_EQUAL(b.hex(), "112233445566778800000000"); + + // Right-aligned extension + FixedHash<12> c = FixedHash<12>(FixedHash<8>("1122334455667788"), FixedHash<12>::AlignRight); + BOOST_CHECK_EQUAL(c.size, 12); + BOOST_CHECK_EQUAL(c.hex(), "000000001122334455667788"); + + // Default setting + FixedHash<12> d = FixedHash<12>(FixedHash<8>("1122334455667788")); + BOOST_CHECK_EQUAL(d, b); + + // FailIfDifferent setting + // TODO: Shouldn't this throw? + FixedHash<12> e = FixedHash<12>(FixedHash<8>("1122334455667788"), FixedHash<12>::FailIfDifferent); + BOOST_CHECK_EQUAL(e, b); +} + +BOOST_AUTO_TEST_CASE(arith_constructor) +{ + FixedHash<20> a(u160(0x1234)); + BOOST_CHECK_EQUAL( + a.hex(), + "0000000000000000000000000000000000001234" + ); + + FixedHash<32> b(u256(0x12340000)); + BOOST_CHECK_EQUAL( + b.hex(), + "0000000000000000000000000000000000000000000000000000000012340000" + ); + + // NOTE: size-mismatched constructor is not available +} + +BOOST_AUTO_TEST_CASE(to_arith) +{ + FixedHash<20> a{}; + BOOST_CHECK_EQUAL(u160(a), 0); + + FixedHash<32> b("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + BOOST_CHECK_EQUAL(u256(b), u256("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); +} + +BOOST_AUTO_TEST_CASE(comparison) +{ + FixedHash<32> a("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + FixedHash<32> b("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + FixedHash<32> c("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a471"); + FixedHash<32> d("233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470c5d2460186f7"); + + BOOST_CHECK(a == a); + BOOST_CHECK(b == b); + BOOST_CHECK(a == b); + BOOST_CHECK(b == a); + BOOST_CHECK(a != c); + BOOST_CHECK(c != a); + BOOST_CHECK(a != d); + BOOST_CHECK(d != a); + + // Only equal size comparison is supported. + BOOST_CHECK(FixedHash<8>{} == FixedHash<8>{}); + BOOST_CHECK(FixedHash<32>{} != b); + + // Only equal size less than comparison is supported. + BOOST_CHECK(!(a < b)); + BOOST_CHECK(d < c); + BOOST_CHECK(FixedHash<32>{} < a); +} + +BOOST_AUTO_TEST_CASE(indexing) +{ + // NOTE: uses std::array, so "Accessing a nonexistent element through this operator is undefined behavior." + + FixedHash<32> a("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + BOOST_CHECK_EQUAL(a[0], 0xc5); + BOOST_CHECK_EQUAL(a[1], 0xd2); + BOOST_CHECK_EQUAL(a[31], 0x70); + a[0] = 0xff; + a[31] = 0x54; + BOOST_CHECK_EQUAL(a[0], 0xff); + BOOST_CHECK_EQUAL(a[1], 0xd2); + BOOST_CHECK_EQUAL(a[31], 0x54); +} + +BOOST_AUTO_TEST_CASE(misc) +{ + FixedHash<32> a("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"); + + uint8_t* mut_a = a.data(); + uint8_t const* const_a = a.data(); + BOOST_CHECK_EQUAL(mut_a, const_a); + BOOST_CHECK_EQUAL(memcmp(mut_a, const_a, a.size), 0); + + bytes bytes_a = a.asBytes(); + bytesRef bytesref_a = a.ref(); + bytesConstRef bytesconstref_a = a.ref(); + + // There's no printing for bytesRef/bytesConstRef + BOOST_CHECK(bytes(a.data(), a.data() + a.size) == bytes_a); + BOOST_CHECK(bytesRef(a.data(), a.size) == bytesref_a); + BOOST_CHECK(bytesConstRef(a.data(), a.size) == bytesconstref_a); +} + +BOOST_AUTO_TEST_CASE(tostream) +{ + std::ostringstream out; + out << FixedHash<4>("44114411"); + out << FixedHash<32>{}; + out << FixedHash<2>("f77f"); + out << FixedHash<1>("1"); + BOOST_CHECK_EQUAL(out.str(), "441144110000000000000000000000000000000000000000000000000000000000000000f77f01"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/libsolutil/LEB128.cpp b/test/libsolutil/LEB128.cpp new file mode 100644 index 000000000000..96ce11c7a527 --- /dev/null +++ b/test/libsolutil/LEB128.cpp @@ -0,0 +1,101 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Unit tests for LEB128. + */ + +#include + +#include + +using namespace std; + +namespace solidity::util::test +{ + +BOOST_AUTO_TEST_SUITE(LEB128Test) + +BOOST_AUTO_TEST_CASE(encode_unsigned) +{ + bytes zero = solidity::util::lebEncode(0); + BOOST_REQUIRE(zero.size() == 1); + BOOST_REQUIRE(zero[0] == 0x00); + + bytes one = solidity::util::lebEncode(1); + BOOST_REQUIRE(one.size() == 1); + BOOST_REQUIRE(one[0] == 0x01); + + bytes large = solidity::util::lebEncode(624485); + BOOST_REQUIRE(large.size() == 3); + BOOST_REQUIRE(large[0] == 0xE5); + BOOST_REQUIRE(large[1] == 0x8E); + BOOST_REQUIRE(large[2] == 0x26); + + bytes larger = solidity::util::lebEncodeSigned(123456123456); + BOOST_REQUIRE(larger.size() == 6); + BOOST_REQUIRE(larger[0] == 0xC0); + BOOST_REQUIRE(larger[1] == 0xE4); + BOOST_REQUIRE(larger[2] == 0xBB); + BOOST_REQUIRE(larger[3] == 0xF4); + BOOST_REQUIRE(larger[4] == 0xCB); + BOOST_REQUIRE(larger[5] == 0x03); +} + +BOOST_AUTO_TEST_CASE(encode_signed) +{ + bytes zero = solidity::util::lebEncodeSigned(0); + BOOST_REQUIRE(zero.size() == 1); + BOOST_REQUIRE(zero[0] == 0x00); + + bytes one = solidity::util::lebEncodeSigned(1); + BOOST_REQUIRE(one.size() == 1); + BOOST_REQUIRE(one[0] == 0x01); + + bytes negative_one = solidity::util::lebEncodeSigned(-1); + BOOST_REQUIRE(negative_one.size() == 1); + BOOST_REQUIRE(negative_one[0] == 0x7f); + + bytes negative_two = solidity::util::lebEncodeSigned(-2); + BOOST_REQUIRE(negative_two.size() == 1); + BOOST_REQUIRE(negative_two[0] == 0x7e); + + bytes large = solidity::util::lebEncodeSigned(624485); + BOOST_REQUIRE(large.size() == 3); + BOOST_REQUIRE(large[0] == 0xE5); + BOOST_REQUIRE(large[1] == 0x8E); + BOOST_REQUIRE(large[2] == 0x26); + + bytes negative_large = solidity::util::lebEncodeSigned(-123456); + BOOST_REQUIRE(negative_large.size() == 3); + BOOST_REQUIRE(negative_large[0] == 0xC0); + BOOST_REQUIRE(negative_large[1] == 0xBB); + BOOST_REQUIRE(negative_large[2] == 0x78); + + bytes negative_larger = solidity::util::lebEncodeSigned(-123456123456); + BOOST_REQUIRE(negative_larger.size() == 6); + BOOST_REQUIRE(negative_larger[0] == 0xC0); + BOOST_REQUIRE(negative_larger[1] == 0x9B); + BOOST_REQUIRE(negative_larger[2] == 0xC4); + BOOST_REQUIRE(negative_larger[3] == 0x8B); + BOOST_REQUIRE(negative_larger[4] == 0xB4); + BOOST_REQUIRE(negative_larger[5] == 0x7C); +} + +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/test/libsolutil/SwarmHash.cpp b/test/libsolutil/SwarmHash.cpp index 3b13cd38213b..5d0c22a80185 100644 --- a/test/libsolutil/SwarmHash.cpp +++ b/test/libsolutil/SwarmHash.cpp @@ -34,6 +34,9 @@ namespace solidity::util::test BOOST_AUTO_TEST_SUITE(SwarmHash, *boost::unit_test::label("nooptions")) +namespace +{ + string bzzr0HashHex(string const& _input) { return toHex(bzzr0Hash(_input).asBytes()); @@ -52,6 +55,8 @@ bytes sequence(size_t _length) return data; } +} + BOOST_AUTO_TEST_CASE(test_zeros) { BOOST_CHECK_EQUAL(bzzr0HashHex(string()), string("011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce")); diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index b0b07a8fb8ef..547f665b9512 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -55,7 +56,7 @@ Dialect const& defaultDialect(bool _yul) void yul::test::printErrors(ErrorList const& _errors) { - SourceReferenceFormatter formatter(cout); + SourceReferenceFormatter formatter(cout, true, false); for (auto const& error: _errors) formatter.printErrorInformation(*error); @@ -76,7 +77,7 @@ pair, shared_ptr> yul::test::parse(strin return make_pair(stack.parserResult()->code, stack.parserResult()->analysisInfo); } -pair, shared_ptr> yul::test::parse( +pair, shared_ptr> yul::test::parse( string const& _source, Dialect const& _dialect, ErrorList& _errors @@ -94,7 +95,7 @@ pair, shared_ptr> yul::test::parse( // TODO this should be done recursively. if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors()) return {}; - return {std::move(parserResult->code), std::move(analysisInfo)}; + return {std::move(parserResult), std::move(analysisInfo)}; } yul::Block yul::test::disambiguate(string const& _source, bool _yul) diff --git a/test/libyul/Common.h b/test/libyul/Common.h index b5f175ac283d..57f659400a76 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -21,8 +21,6 @@ #pragma once -#include - #include #include @@ -38,6 +36,8 @@ using ErrorList = std::vector>; namespace solidity::yul { struct AsmAnalysisInfo; +struct Block; +struct Object; struct Dialect; } @@ -49,7 +49,7 @@ void printErrors(langutil::ErrorList const& _errors); std::pair, std::shared_ptr> parse(std::string const& _source, bool _yul = true); -std::pair, std::shared_ptr> +std::pair, std::shared_ptr> parse(std::string const& _source, Dialect const& _dialect, langutil::ErrorList& _errors); Block disambiguate(std::string const& _source, bool _yul = true); diff --git a/test/libyul/CompilabilityChecker.cpp b/test/libyul/CompilabilityChecker.cpp index ad1a6fd5eade..e8890a8bbddd 100644 --- a/test/libyul/CompilabilityChecker.cpp +++ b/test/libyul/CompilabilityChecker.cpp @@ -39,7 +39,7 @@ string check(string const& _input) Object obj; std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false); BOOST_REQUIRE(obj.code); - map functions = CompilabilityChecker::run(EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()), obj, true); + auto functions = CompilabilityChecker(EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()), obj, true).stackDeficit; string out; for (auto const& function: functions) out += function.first.str() + ": " + to_string(function.second) + " "; diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index be9ed77d957b..15ddfb86e256 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -100,6 +102,7 @@ string EwasmTranslationTest::interpret() InterpreterState state; state.maxTraceSize = 10000; state.maxSteps = 1000000; + state.maxExprNesting = 64; try { Interpreter::run(state, WasmDialect{}, *m_object->code); @@ -115,7 +118,7 @@ string EwasmTranslationTest::interpret() void EwasmTranslationTest::printErrors(ostream& _stream, ErrorList const& _errors) { - SourceReferenceFormatter formatter(_stream); + SourceReferenceFormatter formatter(_stream, true, false); for (auto const& error: _errors) formatter.printErrorInformation(*error); diff --git a/test/libyul/EwasmTranslationTest.h b/test/libyul/EwasmTranslationTest.h index f6307bdbc5c4..5175430e38ee 100644 --- a/test/libyul/EwasmTranslationTest.h +++ b/test/libyul/EwasmTranslationTest.h @@ -19,7 +19,6 @@ #pragma once #include -#include namespace solidity::langutil { @@ -28,6 +27,11 @@ class Error; using ErrorList = std::vector>; } +namespace solidity::yul +{ +struct Object; +} + namespace solidity::yul::test { diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index 56051ffddc66..e72d98be2202 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -49,14 +49,27 @@ string toString(SideEffects const& _sideEffects) vector ret; if (_sideEffects.movable) ret.emplace_back("movable"); - if (_sideEffects.sideEffectFree) - ret.emplace_back("sideEffectFree"); - if (_sideEffects.sideEffectFreeIfNoMSize) - ret.emplace_back("sideEffectFreeIfNoMSize"); - if (_sideEffects.invalidatesStorage) - ret.emplace_back("invalidatesStorage"); - if (_sideEffects.invalidatesMemory) - ret.emplace_back("invalidatesMemory"); + if (_sideEffects.movableApartFromEffects) + ret.emplace_back("movable apart from effects"); + if (_sideEffects.canBeRemoved) + ret.emplace_back("can be removed"); + if (_sideEffects.canBeRemovedIfNoMSize) + ret.emplace_back("can be removed if no msize"); + if (!_sideEffects.cannotLoop) + ret.emplace_back("can loop"); + if (_sideEffects.otherState == SideEffects::Write) + ret.emplace_back("writes other state"); + else if (_sideEffects.otherState == SideEffects::Read) + ret.emplace_back("reads other state"); + if (_sideEffects.storage == SideEffects::Write) + ret.emplace_back("writes storage"); + else if (_sideEffects.storage == SideEffects::Read) + ret.emplace_back("reads storage"); + if (_sideEffects.memory == SideEffects::Write) + ret.emplace_back("writes memory"); + else if (_sideEffects.memory == SideEffects::Read) + ret.emplace_back("reads memory"); + return joinHumanReadable(ret); } } diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp index f44d86618f82..6fc50ddc6ab2 100644 --- a/test/libyul/Inliner.cpp +++ b/test/libyul/Inliner.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include diff --git a/test/libyul/Metrics.cpp b/test/libyul/Metrics.cpp index 7ae9e44f5b00..e9a591b62a32 100644 --- a/test/libyul/Metrics.cpp +++ b/test/libyul/Metrics.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index 950c0210f04e..6d8cd313e939 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -44,6 +44,7 @@ ObjectCompilerTest::ObjectCompilerTest(string const& _filename): { m_source = m_reader.source(); m_optimize = m_reader.boolSetting("optimize", false); + m_wasm = m_reader.boolSetting("wasm", false); m_expectation = m_reader.simpleExpectations(); } @@ -51,7 +52,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li { AssemblyStack stack( EVMVersion(), - AssemblyStack::Language::StrictAssembly, + m_wasm ? AssemblyStack::Language::Ewasm : AssemblyStack::Language::StrictAssembly, m_optimize ? OptimiserSettings::full() : OptimiserSettings::minimal() ); if (!stack.parseAndAnalyze("source", m_source)) @@ -62,29 +63,40 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li } stack.optimize(); - MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM); - solAssert(obj.bytecode, ""); - solAssert(obj.sourceMappings, ""); + if (m_wasm) + { + MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::Ewasm); + solAssert(obj.bytecode, ""); - m_obtainedResult = "Assembly:\n" + obj.assembly; - if (obj.bytecode->bytecode.empty()) - m_obtainedResult += "-- empty bytecode --\n"; + m_obtainedResult = "Text:\n" + obj.assembly + "\n"; + m_obtainedResult += "Binary:\n" + toHex(obj.bytecode->bytecode) + "\n"; + } else - m_obtainedResult += - "Bytecode: " + - toHex(obj.bytecode->bytecode) + - "\nOpcodes: " + - boost::trim_copy(evmasm::disassemble(obj.bytecode->bytecode)) + - "\nSourceMappings:" + - (obj.sourceMappings->empty() ? "" : " " + *obj.sourceMappings) + - "\n"; + { + MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM); + solAssert(obj.bytecode, ""); + solAssert(obj.sourceMappings, ""); + + m_obtainedResult = "Assembly:\n" + obj.assembly; + if (obj.bytecode->bytecode.empty()) + m_obtainedResult += "-- empty bytecode --\n"; + else + m_obtainedResult += + "Bytecode: " + + toHex(obj.bytecode->bytecode) + + "\nOpcodes: " + + boost::trim_copy(evmasm::disassemble(obj.bytecode->bytecode)) + + "\nSourceMappings:" + + (obj.sourceMappings->empty() ? "" : " " + *obj.sourceMappings) + + "\n"; + } return checkResult(_stream, _linePrefix, _formatted); } void ObjectCompilerTest::printErrors(ostream& _stream, ErrorList const& _errors) { - SourceReferenceFormatter formatter(_stream); + SourceReferenceFormatter formatter(_stream, true, false); for (auto const& error: _errors) formatter.printErrorInformation(*error); diff --git a/test/libyul/ObjectCompilerTest.h b/test/libyul/ObjectCompilerTest.h index 85a46a1cf3df..771942e5990b 100644 --- a/test/libyul/ObjectCompilerTest.h +++ b/test/libyul/ObjectCompilerTest.h @@ -55,6 +55,7 @@ class ObjectCompilerTest: public solidity::frontend::test::TestCase static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); bool m_optimize = false; + bool m_wasm = false; }; } diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 2d932006e65d..de5b00f3e3f8 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -118,21 +118,6 @@ BOOST_AUTO_TEST_CASE(empty_code) BOOST_CHECK(successParse("{ }")); } -BOOST_AUTO_TEST_CASE(object_with_empty_code) -{ - BOOST_CHECK(successParse("object \"a\" { code { } }")); -} - -BOOST_AUTO_TEST_CASE(non_object) -{ - CHECK_ERROR("code {}", ParserError, "Expected keyword \"object\""); -} - -BOOST_AUTO_TEST_CASE(empty_name) -{ - CHECK_ERROR("object \"\" { code {} }", ParserError, "Object name cannot be empty"); -} - BOOST_AUTO_TEST_CASE(recursion_depth) { string input; @@ -144,77 +129,6 @@ BOOST_AUTO_TEST_CASE(recursion_depth) CHECK_ERROR(input, ParserError, "recursion"); } -BOOST_AUTO_TEST_CASE(object_with_code) -{ - BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } }")); -} - -BOOST_AUTO_TEST_CASE(object_with_code_and_data) -{ - BOOST_CHECK(successParse("object \"a\" { code { let x := mload(0) sstore(0, x) } data \"b\" hex\"01010202\" }")); -} - -BOOST_AUTO_TEST_CASE(object_with_non_code_at_start) -{ - CHECK_ERROR("object \"a\" { data \"d\" hex\"0102\" code { } }", ParserError, "Expected keyword \"code\""); -} - -BOOST_AUTO_TEST_CASE(nested_object) -{ - string code = R"( - object "outer" { - code { let x := mload(0) } - data "x" "stringdata" - object "inner" { - code { mstore(0, 1) } - object "inner inner" { code {} } - data "innerx" "abc" - data "innery" "def" - } - } - )"; - BOOST_CHECK(successParse(code)); -} - -BOOST_AUTO_TEST_CASE(incomplete) -{ - CHECK_ERROR("object \"abc\" { code {} } object", ParserError, "Expected end of source"); -} - -BOOST_AUTO_TEST_CASE(reuse_object_name) -{ - string code = R"( - object "outer" { - code { } - data "outer" "stringdata" - } - )"; - CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); -} - -BOOST_AUTO_TEST_CASE(reuse_object_in_subobject) -{ - string code = R"( - object "outer" { - code { } - object "outer" { code {} } - } - )"; - CHECK_ERROR(code, ParserError, "Object name cannot be the same as the name of the containing object"); -} - -BOOST_AUTO_TEST_CASE(reuse_object_of_sibling) -{ - string code = R"( - object "O" { - code { } - object "i" { code {} } - data "i" "abc" - } - )"; - CHECK_ERROR(code, ParserError, "already exists inside the"); -} - BOOST_AUTO_TEST_CASE(to_string) { string code = R"( @@ -248,49 +162,6 @@ BOOST_AUTO_TEST_CASE(to_string) BOOST_CHECK_EQUAL(asmStack.print(), expectation); } -BOOST_AUTO_TEST_CASE(arg_to_dataoffset_must_be_literal) -{ - string code = R"( - object "outer" { - code { let x := "outer" let y := dataoffset(x) } - } - )"; - CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); -} - -BOOST_AUTO_TEST_CASE(arg_to_datasize_must_be_literal) -{ - string code = R"( - object "outer" { - code { let x := "outer" let y := datasize(x) } - } - )"; - CHECK_ERROR(code, TypeError, "Function expects direct literals as arguments."); -} - -BOOST_AUTO_TEST_CASE(args_to_datacopy_are_arbitrary) -{ - string code = R"( - object "outer" { - code { let x := 0 let y := 2 let s := 3 datacopy(x, y, s) } - } - )"; - BOOST_CHECK(successParse(code)); -} - - -BOOST_AUTO_TEST_CASE(non_existing_objects) -{ - BOOST_CHECK(successParse( - "object \"main\" { code { pop(datasize(\"main\")) } }" - )); - CHECK_ERROR( - "object \"main\" { code { pop(datasize(\"abc\")) } }", - TypeError, - "Unknown data object" - ); -} - BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 7b8e56119d09..6d40acdd48ff 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -126,411 +127,6 @@ do \ BOOST_AUTO_TEST_SUITE(YulParser) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - BOOST_CHECK(successParse("{ }")); -} - -BOOST_AUTO_TEST_CASE(vardecl) -{ - BOOST_CHECK(successParse("{ let x:u256 := 7:u256 }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_bool) -{ - BOOST_CHECK(successParse("{ let x:bool := true:bool }")); - BOOST_CHECK(successParse("{ let x:bool := false:bool }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_empty) -{ - BOOST_CHECK(successParse("{ let x:u256 }")); -} - -BOOST_AUTO_TEST_CASE(assignment) -{ - BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); -} - -BOOST_AUTO_TEST_CASE(period_in_identifier) -{ - BOOST_CHECK(successParse("{ let x.y:u256 := 2:u256 }")); -} - -BOOST_AUTO_TEST_CASE(period_not_as_identifier_start) -{ - CHECK_ERROR("{ let .y:u256 }", ParserError, "Expected identifier but got '.'"); -} - -BOOST_AUTO_TEST_CASE(period_in_identifier_spaced) -{ - CHECK_ERROR("{ let x. y:u256 }", ParserError, "Call or assignment expected"); - CHECK_ERROR("{ let x .y:u256 }", ParserError, "Literal or identifier expected"); - CHECK_ERROR("{ let x . y:u256 }", ParserError, "Literal or identifier expected"); -} - -BOOST_AUTO_TEST_CASE(period_in_identifier_start) -{ - BOOST_CHECK(successParse("{ x.y(2:u256) function x.y(a:u256) {} }")); -} - -BOOST_AUTO_TEST_CASE(period_in_identifier_start_with_comment) -{ - BOOST_CHECK(successParse("/// comment\n{ x.y(2:u256) function x.y(a:u256) {} }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_complex) -{ - BOOST_CHECK(successParse("{ function add(a:u256, b:u256) -> c:u256 {} let y:u256 := 2:u256 let x:u256 := add(7:u256, add(6:u256, y)) }")); -} - -BOOST_AUTO_TEST_CASE(blocks) -{ - BOOST_CHECK(successParse("{ let x:u256 := 7:u256 { let y:u256 := 3:u256 } { let z:u256 := 2:u256 } }")); -} - -BOOST_AUTO_TEST_CASE(function_definitions) -{ - BOOST_CHECK(successParse("{ function f() { } function g(a:u256) -> x:u256 { } }")); -} - -BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) -{ - BOOST_CHECK(successParse("{ function f(a:u256, d:u256) { } function g(a:u256, d:u256) -> x:u256, y:u256 { } }")); -} - -BOOST_AUTO_TEST_CASE(function_calls) -{ - BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(3:u256)) x() } }")); -} - -BOOST_AUTO_TEST_CASE(tuple_assignment) -{ - BOOST_CHECK(successParse("{ function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() }")); -} - -BOOST_AUTO_TEST_CASE(instructions) -{ - CHECK_ERROR("{ pop }", ParserError, "Call or assignment expected."); -} - -BOOST_AUTO_TEST_CASE(push) -{ - CHECK_ERROR("{ 0x42:u256 }", ParserError, "Call or assignment expected."); -} - -BOOST_AUTO_TEST_CASE(assign_from_stack) -{ - CHECK_ERROR("{ =: x:u256 }", ParserError, "Literal or identifier expected."); -} - -BOOST_AUTO_TEST_CASE(empty_call) -{ - CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); -} - -BOOST_AUTO_TEST_CASE(tokens_as_identifers) -{ - BOOST_CHECK(successParse("{ let return:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let byte:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let address:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let bool:u256 := 1:u256 }")); -} - -BOOST_AUTO_TEST_CASE(optional_types) -{ - BOOST_CHECK(successParse("{ let x := 1:u256 }")); - BOOST_CHECK(successParse("{ let x:u256 := 1 }")); - BOOST_CHECK(successParse("{ function f(a) {} }")); - BOOST_CHECK(successParse("{ function f(a:u256) -> b {} }")); -} - -BOOST_AUTO_TEST_CASE(number_literals) -{ - BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); - CHECK_ERROR("{ let x:u256 := .1:u256 }", ParserError, "Invalid number literal."); - CHECK_ERROR("{ let x:u256 := 1e5:u256 }", ParserError, "Invalid number literal."); - CHECK_ERROR("{ let x:u256 := 67.235:u256 }", ParserError, "Invalid number literal."); - CHECK_ERROR("{ let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 }", TypeError, "Number literal too large (> 256 bits)"); -} - -BOOST_AUTO_TEST_CASE(builtin_types) -{ - BOOST_CHECK(successParse("{ let x:bool := true:bool }")); - BOOST_CHECK(successParse("{ let x:u8 := 1:u8 }")); - BOOST_CHECK(successParse("{ let x:s8 := 1:s8 }")); - BOOST_CHECK(successParse("{ let x:u32 := 1:u32 }")); - BOOST_CHECK(successParse("{ let x:s32 := 1:s32 }")); - BOOST_CHECK(successParse("{ let x:u64 := 1:u64 }")); - BOOST_CHECK(successParse("{ let x:s64 := 1:s64 }")); - BOOST_CHECK(successParse("{ let x:u128 := 1:u128 }")); - BOOST_CHECK(successParse("{ let x:s128 := 1:s128 }")); - BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let x:s256 := 1:s256 }")); -} - -BOOST_AUTO_TEST_CASE(recursion_depth) -{ - string input; - for (size_t i = 0; i < 20000; i++) - input += "{"; - input += "let x:u256 := 0:u256"; - for (size_t i = 0; i < 20000; i++) - input += "}"; - - CHECK_ERROR(input, ParserError, "recursion"); -} - -BOOST_AUTO_TEST_CASE(multiple_assignment) -{ - CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} 123:u256, x := f() }", ParserError, "Variable name must precede \",\" in multiple assignment."); - CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} x, 123:u256 := f() }", ParserError, "Variable name must precede \":=\" in assignment."); - - /// NOTE: Travis hiccups if not having a variable - char const* text = R"( - { - function f(a:u256) -> r1:u256, r2:u256 { - r1 := a - r2 := 7:u256 - } - let x:u256 := 9:u256 - let y:u256 := 2:u256 - x, y := f(x) - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(if_statement) -{ - BOOST_CHECK(successParse("{ if true:bool {} }")); - BOOST_CHECK(successParse("{ if false:bool { let x:u256 := 3:u256 } }")); - BOOST_CHECK(successParse("{ function f() -> x:bool {} if f() { let b:bool := f() } }")); -} - -BOOST_AUTO_TEST_CASE(break_outside_of_for_loop) -{ - CHECK_ERROR_DIALECT( - "{ let x if x { break } }", - SyntaxError, - "Keyword \"break\" needs to be inside a for-loop body.", - EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()) - ); -} - -BOOST_AUTO_TEST_CASE(continue_outside_of_for_loop) -{ - CHECK_ERROR_DIALECT( - "{ let x if x { continue } }", - SyntaxError, - "Keyword \"continue\" needs to be inside a for-loop body.", - EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()) - ); -} - -BOOST_AUTO_TEST_CASE(for_statement) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {} }", dialect)); -} - -BOOST_AUTO_TEST_CASE(for_statement_break) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {break} }", dialect)); -} - -BOOST_AUTO_TEST_CASE(for_statement_break_init) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - CHECK_ERROR_DIALECT( - "{ for {let i := 0 break} iszero(eq(i, 10)) {i := add(i, 1)} {} }", - SyntaxError, - "Keyword \"break\" in for-loop init block is not allowed.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(for_statement_break_post) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - CHECK_ERROR_DIALECT( - "{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) break} {} }", - SyntaxError, - "Keyword \"break\" in for-loop post block is not allowed.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(for_statement_nested_break) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - CHECK_ERROR_DIALECT( - "{ for {let i := 0} iszero(eq(i, 10)) {} { function f() { break } } }", - SyntaxError, - "Keyword \"break\" needs to be inside a for-loop body.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(for_statement_continue) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - BOOST_CHECK(successParse("{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1)} {continue} }", dialect)); -} - -BOOST_AUTO_TEST_CASE(for_statement_continue_fail_init) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - CHECK_ERROR_DIALECT( - "{ for {let i := 0 continue} iszero(eq(i, 10)) {i := add(i, 1)} {} }", - SyntaxError, - "Keyword \"continue\" in for-loop init block is not allowed.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(for_statement_continue_fail_post) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - CHECK_ERROR_DIALECT( - "{ for {let i := 0} iszero(eq(i, 10)) {i := add(i, 1) continue} {} }", - SyntaxError, - "Keyword \"continue\" in for-loop post block is not allowed.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(for_statement_nested_continue) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - CHECK_ERROR_DIALECT( - "{ for {let i := 0} iszero(eq(i, 10)) {} { function f() { continue } } }", - SyntaxError, - "Keyword \"continue\" needs to be inside a for-loop body.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(for_statement_continue_nested_init_in_body) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion::constantinople()); - CHECK_ERROR_DIALECT( - "{ for {} 1 {} {let x for { continue } x {} {}} }", - SyntaxError, - "Keyword \"continue\" in for-loop init block is not allowed.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(for_statement_continue_nested_body_in_init) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - BOOST_CHECK(successParse("{ for {let x for {} x {} { continue }} 1 {} {} }", dialect)); -} - -BOOST_AUTO_TEST_CASE(for_statement_break_nested_body_in_init) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - BOOST_CHECK(successParse("{ for {let x for {} x {} { break }} 1 {} {} }", dialect)); -} - -BOOST_AUTO_TEST_CASE(for_statement_continue_nested_body_in_post) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - BOOST_CHECK(successParse("{ for {} 1 {let x for {} x {} { continue }} {} }", dialect)); -} - -BOOST_AUTO_TEST_CASE(for_statement_break_nested_body_in_post) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - BOOST_CHECK(successParse("{ for {} 1 {let x for {} x {} { break }} {} }", dialect)); -} - -BOOST_AUTO_TEST_CASE(function_defined_in_init_block) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - BOOST_CHECK(successParse("{ for { } 1 { function f() {} } {} }", dialect)); - BOOST_CHECK(successParse("{ for { } 1 {} { function f() {} } }", dialect)); - CHECK_ERROR_DIALECT( - "{ for { function f() {} } 1 {} {} }", - SyntaxError, - "Functions cannot be defined inside a for-loop init block.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(function_defined_in_init_nested) -{ - auto const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - BOOST_CHECK(successParse( - "{ for {" - "for { } 1 { function f() {} } {}" - "} 1 {} {} }", dialect)); - CHECK_ERROR_DIALECT( - "{ for { for {function foo() {}} 1 {} {} } 1 {} {} }", - SyntaxError, - "Functions cannot be defined inside a for-loop init block.", - dialect - ); - CHECK_ERROR_DIALECT( - "{ for {} 1 {for {function foo() {}} 1 {} {} } {} }", - SyntaxError, - "Functions cannot be defined inside a for-loop init block.", - dialect - ); -} - -BOOST_AUTO_TEST_CASE(if_statement_invalid) -{ - CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected."); - CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'"); - CHECK_ERROR("{ if 42:u256 { } }", TypeError, "Expected a value of boolean type"); -} - -BOOST_AUTO_TEST_CASE(switch_duplicate_case) -{ - CHECK_ERROR("{ switch 0:u256 case 0:u256 {} case 0x0:u256 {} }", DeclarationError, "Duplicate case defined."); - BOOST_CHECK(successParse("{ switch 0:u256 case 42:u256 {} case 0x42:u256 {} }")); -} - -BOOST_AUTO_TEST_CASE(switch_duplicate_case_different_literal) -{ - CHECK_ERROR("{ switch 0:u256 case 0:u256 {} case \"\":u256 {} }", DeclarationError, "Duplicate case defined."); - BOOST_CHECK(successParse("{ switch 1:u256 case \"1\":u256 {} case \"2\":u256 {} }")); -} - -BOOST_AUTO_TEST_CASE(switch_case_string_literal_too_long) -{ - BOOST_CHECK(successParse("{let x:u256 switch x case \"01234567890123456789012345678901\":u256 {}}")); - CHECK_ERROR("{let x:u256 switch x case \"012345678901234567890123456789012\":u256 {}}", TypeError, "String literal too long (33 > 32)"); -} - -BOOST_AUTO_TEST_CASE(function_shadowing_outside_vars) -{ - CHECK_ERROR("{ let x:u256 function f() -> x:u256 {} }", DeclarationError, "already taken in this scope"); - BOOST_CHECK(successParse("{ { let x:u256 } function f() -> x:u256 {} }")); -} - -BOOST_AUTO_TEST_CASE(builtins_parser) -{ - struct SimpleDialect: public Dialect - { - BuiltinFunction const* builtin(YulString _name) const override - { - return _name == "builtin"_yulstring ? &f : nullptr; - } - BuiltinFunction f; - }; - - SimpleDialect dialect; - CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); - CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); - CHECK_ERROR_DIALECT("{ function f(x) { f(builtin) } }", ParserError, "Expected '(' but got ')'", dialect); - CHECK_ERROR_DIALECT("{ function f(builtin) {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); - CHECK_ERROR_DIALECT("{ function f() -> builtin {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); -} - BOOST_AUTO_TEST_CASE(builtins_analysis) { struct SimpleDialect: public Dialect diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index bb0751b828df..f01728571a3c 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include using namespace std; @@ -43,17 +45,9 @@ void SyntaxTest::parseAndAnalyze() string const& source = m_sources.begin()->second; ErrorList errorList{}; - ErrorReporter errorReporter{errorList}; - - auto scanner = make_shared(CharStream(source, name)); - auto parserResult = yul::Parser(errorReporter, *m_dialect).parse(scanner, false); - - if (parserResult) - { - yul::AsmAnalysisInfo analysisInfo; - yul::AsmAnalyzer(analysisInfo, errorReporter, *m_dialect).analyze(*parserResult); - } - + soltestAssert(m_dialect, ""); + // Silently ignoring the results. + yul::test::parse(source, *m_dialect, errorList); for (auto const& error: errorList) { int locationStart = -1; diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index 78e81578569b..11e85d66b3ac 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -87,8 +87,9 @@ bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool string YulInterpreterTest::interpret() { InterpreterState state; - state.maxTraceSize = 10000; - state.maxSteps = 10000; + state.maxTraceSize = 32; + state.maxSteps = 512; + state.maxExprNesting = 64; try { Interpreter::run(state, EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion{}), *m_ast); @@ -104,7 +105,7 @@ string YulInterpreterTest::interpret() void YulInterpreterTest::printErrors(ostream& _stream, ErrorList const& _errors) { - SourceReferenceFormatter formatter(_stream); + SourceReferenceFormatter formatter(_stream, true, false); for (auto const& error: _errors) formatter.printErrorInformation(*error); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 436c46a656ac..9a8a3283688e 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -46,18 +46,22 @@ #include #include #include +#include #include #include #include +#include #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -68,6 +72,7 @@ #include #include #include +#include #include #include @@ -101,6 +106,12 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename): BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); m_optimizerStep = std::prev(std::prev(path.end()))->string(); + if (m_optimizerStep == "reasoningBasedSimplifier" && ( + solidity::test::CommonOptions::get().disableSMT || + ReasoningBasedSimplifier::invalidInCurrentEnvironment() + )) + m_shouldRun = false; + m_source = m_reader.source(); auto dialectName = m_reader.stringSetting("dialect", "evm"); @@ -111,7 +122,8 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename): TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - if (!parse(_stream, _linePrefix, _formatted)) + std::tie(m_object, m_analysisInfo) = parse(_stream, _linePrefix, _formatted, m_source); + if (!m_object) return TestResult::FatalError; soltestAssert(m_dialect, "Dialect not set."); @@ -126,263 +138,355 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line NameDisplacer{ *m_nameDispenser, {"illegal1"_yulstring, "illegal2"_yulstring, "illegal3"_yulstring, "illegal4"_yulstring, "illegal5"_yulstring} - }(*m_ast); + }(*m_object->code); } else if (m_optimizerStep == "blockFlattener") { disambiguate(); - BlockFlattener::run(*m_context, *m_ast); + BlockFlattener::run(*m_context, *m_object->code); } else if (m_optimizerStep == "constantOptimiser") { GasMeter meter(dynamic_cast(*m_dialect), false, 200); - ConstantOptimiser{dynamic_cast(*m_dialect), meter}(*m_ast); + ConstantOptimiser{dynamic_cast(*m_dialect), meter}(*m_object->code); } else if (m_optimizerStep == "varDeclInitializer") - VarDeclInitializer::run(*m_context, *m_ast); + VarDeclInitializer::run(*m_context, *m_object->code); else if (m_optimizerStep == "varNameCleaner") - VarNameCleaner::run(*m_context, *m_ast); + { + disambiguate(); + FunctionGrouper::run(*m_context, *m_object->code); + VarNameCleaner::run(*m_context, *m_object->code); + } else if (m_optimizerStep == "forLoopConditionIntoBody") { disambiguate(); - ForLoopConditionIntoBody::run(*m_context, *m_ast); + ForLoopConditionIntoBody::run(*m_context, *m_object->code); } else if (m_optimizerStep == "forLoopInitRewriter") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); } else if (m_optimizerStep == "commonSubexpressionEliminator") { disambiguate(); - CommonSubexpressionEliminator::run(*m_context, *m_ast); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "conditionalUnsimplifier") { disambiguate(); - ConditionalUnsimplifier::run(*m_context, *m_ast); + ConditionalUnsimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "conditionalSimplifier") { disambiguate(); - ConditionalSimplifier::run(*m_context, *m_ast); + ConditionalSimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "expressionSplitter") - ExpressionSplitter::run(*m_context, *m_ast); + ExpressionSplitter::run(*m_context, *m_object->code); else if (m_optimizerStep == "expressionJoiner") { disambiguate(); - ExpressionJoiner::run(*m_context, *m_ast); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "splitJoin") { disambiguate(); - ExpressionSplitter::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + ExpressionSplitter::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "functionGrouper") { disambiguate(); - FunctionGrouper::run(*m_context, *m_ast); + FunctionGrouper::run(*m_context, *m_object->code); } else if (m_optimizerStep == "functionHoister") { disambiguate(); - FunctionHoister::run(*m_context, *m_ast); + FunctionHoister::run(*m_context, *m_object->code); } else if (m_optimizerStep == "expressionInliner") { disambiguate(); - ExpressionInliner::run(*m_context, *m_ast); + ExpressionInliner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "fullInliner") { disambiguate(); - FunctionHoister::run(*m_context, *m_ast); - FunctionGrouper::run(*m_context, *m_ast); - ExpressionSplitter::run(*m_context, *m_ast); - FullInliner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + FunctionHoister::run(*m_context, *m_object->code); + FunctionGrouper::run(*m_context, *m_object->code); + ExpressionSplitter::run(*m_context, *m_object->code); + FullInliner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "mainFunction") { disambiguate(); - FunctionGrouper::run(*m_context, *m_ast); - MainFunction::run(*m_context, *m_ast); + FunctionGrouper::run(*m_context, *m_object->code); + MainFunction::run(*m_context, *m_object->code); } else if (m_optimizerStep == "rematerialiser") { disambiguate(); - Rematerialiser::run(*m_context, *m_ast); + Rematerialiser::run(*m_context, *m_object->code); } else if (m_optimizerStep == "expressionSimplifier") { disambiguate(); - ExpressionSimplifier::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); + ExpressionSplitter::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); + UnusedPruner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "fullSimplify") { disambiguate(); - ExpressionSplitter::run(*m_context, *m_ast); - ForLoopInitRewriter::run(*m_context, *m_ast); - CommonSubexpressionEliminator::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); - UnusedPruner::run(*m_context, *m_ast); - CircularReferencesPruner::run(*m_context, *m_ast); - DeadCodeEliminator::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + ExpressionSplitter::run(*m_context, *m_object->code); + ForLoopInitRewriter::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); + UnusedPruner::run(*m_context, *m_object->code); + CircularReferencesPruner::run(*m_context, *m_object->code); + DeadCodeEliminator::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + } + else if (m_optimizerStep == "unusedFunctionParameterPruner") + { + disambiguate(); + FunctionHoister::run(*m_context, *m_object->code); + LiteralRematerialiser::run(*m_context, *m_object->code); + UnusedFunctionParameterPruner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "unusedPruner") { disambiguate(); - UnusedPruner::run(*m_context, *m_ast); + UnusedPruner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "circularReferencesPruner") { disambiguate(); - FunctionHoister::run(*m_context, *m_ast); - CircularReferencesPruner::run(*m_context, *m_ast); + FunctionHoister::run(*m_context, *m_object->code); + CircularReferencesPruner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "deadCodeEliminator") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - DeadCodeEliminator::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + DeadCodeEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaTransform") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - SSATransform::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + SSATransform::run(*m_context, *m_object->code); } else if (m_optimizerStep == "redundantAssignEliminator") { disambiguate(); - RedundantAssignEliminator::run(*m_context, *m_ast); + RedundantAssignEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaPlusCleanup") { disambiguate(); - SSATransform::run(*m_context, *m_ast); - RedundantAssignEliminator::run(*m_context, *m_ast); + SSATransform::run(*m_context, *m_object->code); + RedundantAssignEliminator::run(*m_context, *m_object->code); } else if (m_optimizerStep == "loadResolver") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - ExpressionSplitter::run(*m_context, *m_ast); - CommonSubexpressionEliminator::run(*m_context, *m_ast); - ExpressionSimplifier::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + ExpressionSplitter::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); + ExpressionSimplifier::run(*m_context, *m_object->code); - LoadResolver::run(*m_context, *m_ast); + LoadResolver::run(*m_context, *m_object->code); - UnusedPruner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); - ExpressionJoiner::run(*m_context, *m_ast); + UnusedPruner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); + ExpressionJoiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "loopInvariantCodeMotion") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - LoopInvariantCodeMotion::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + LoopInvariantCodeMotion::run(*m_context, *m_object->code); } else if (m_optimizerStep == "controlFlowSimplifier") { disambiguate(); - ControlFlowSimplifier::run(*m_context, *m_ast); + ControlFlowSimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "structuralSimplifier") { disambiguate(); - ForLoopInitRewriter::run(*m_context, *m_ast); - LiteralRematerialiser::run(*m_context, *m_ast); - StructuralSimplifier::run(*m_context, *m_ast); + ForLoopInitRewriter::run(*m_context, *m_object->code); + LiteralRematerialiser::run(*m_context, *m_object->code); + StructuralSimplifier::run(*m_context, *m_object->code); + } + else if (m_optimizerStep == "reasoningBasedSimplifier") + { + disambiguate(); + ReasoningBasedSimplifier::run(*m_context, *m_object->code); } else if (m_optimizerStep == "equivalentFunctionCombiner") { disambiguate(); - EquivalentFunctionCombiner::run(*m_context, *m_ast); + EquivalentFunctionCombiner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaReverser") { disambiguate(); - SSAReverser::run(*m_context, *m_ast); + SSAReverser::run(*m_context, *m_object->code); } else if (m_optimizerStep == "ssaAndBack") { disambiguate(); // apply SSA - SSATransform::run(*m_context, *m_ast); - RedundantAssignEliminator::run(*m_context, *m_ast); + SSATransform::run(*m_context, *m_object->code); + RedundantAssignEliminator::run(*m_context, *m_object->code); // reverse SSA - SSAReverser::run(*m_context, *m_ast); - CommonSubexpressionEliminator::run(*m_context, *m_ast); - UnusedPruner::run(*m_context, *m_ast); + SSAReverser::run(*m_context, *m_object->code); + CommonSubexpressionEliminator::run(*m_context, *m_object->code); + UnusedPruner::run(*m_context, *m_object->code); } else if (m_optimizerStep == "stackCompressor") { disambiguate(); - FunctionGrouper::run(*m_context, *m_ast); + FunctionGrouper::run(*m_context, *m_object->code); size_t maxIterations = 16; Object obj; - obj.code = m_ast; + obj.code = m_object->code; StackCompressor::run(*m_dialect, obj, true, maxIterations); - m_ast = obj.code; - BlockFlattener::run(*m_context, *m_ast); + m_object->code = obj.code; + BlockFlattener::run(*m_context, *m_object->code); } else if (m_optimizerStep == "wordSizeTransform") { disambiguate(); - ExpressionSplitter::run(*m_context, *m_ast); - WordSizeTransform::run(*m_dialect, *m_dialect, *m_ast, *m_nameDispenser); + ExpressionSplitter::run(*m_context, *m_object->code); + WordSizeTransform::run(*m_dialect, *m_dialect, *m_object->code, *m_nameDispenser); } else if (m_optimizerStep == "fullSuite") { GasMeter meter(dynamic_cast(*m_dialect), false, 200); yul::Object obj; - obj.code = m_ast; + obj.code = m_object->code; obj.analysisInfo = m_analysisInfo; OptimiserSuite::run(*m_dialect, &meter, obj, true, solidity::frontend::OptimiserSettings::DefaultYulOptimiserSteps); } + else if (m_optimizerStep == "stackLimitEvader") + { + yul::Object obj; + obj.code = m_object->code; + obj.analysisInfo = m_analysisInfo; + disambiguate(); + StackLimitEvader::run(*m_context, obj, CompilabilityChecker{ + *m_dialect, + obj, + true + }.unreachableVariables); + } + else if (m_optimizerStep == "fakeStackLimitEvader") + { + yul::Object obj; + obj.code = m_object->code; + obj.analysisInfo = m_analysisInfo; + disambiguate(); + // Mark all variables with a name starting with "$" for escalation to memory. + struct FakeUnreachableGenerator: ASTWalker + { + map> fakeUnreachables; + using ASTWalker::operator(); + void operator()(FunctionDefinition const& _function) override + { + YulString originalFunctionName = m_currentFunction; + m_currentFunction = _function.name; + ASTWalker::operator()(_function); + m_currentFunction = originalFunctionName; + } + void visitVariableName(YulString _var) + { + if (!_var.empty() && _var.str().front() == '$') + fakeUnreachables[m_currentFunction].insert(_var); + } + void operator()(VariableDeclaration const& _varDecl) override + { + for (auto const& var: _varDecl.variables) + visitVariableName(var.name); + ASTWalker::operator()(_varDecl); + } + void operator()(Identifier const& _identifier) override + { + visitVariableName(_identifier.name); + ASTWalker::operator()(_identifier); + } + YulString m_currentFunction = YulString{}; + }; + FakeUnreachableGenerator fakeUnreachableGenerator; + fakeUnreachableGenerator(*obj.code); + StackLimitEvader::run(*m_context, obj, fakeUnreachableGenerator.fakeUnreachables); + } else { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; return TestResult::FatalError; } - m_obtainedResult = "step: " + m_optimizerStep + "\n\n" + AsmPrinter{ *m_dialect }(*m_ast) + "\n"; + auto const printed = (m_object->subObjects.empty() ? AsmPrinter{ *m_dialect }(*m_object->code) : m_object->toString(m_dialect)); + + // Re-parse new code for compilability + // TODO: support for wordSizeTransform which needs different input and output dialects + if (m_optimizerStep != "wordSizeTransform" && !std::get<0>(parse(_stream, _linePrefix, _formatted, printed))) + { + util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::CYAN}) + << _linePrefix << "Result after the optimiser:" << endl; + printIndented(_stream, printed, _linePrefix + " "); + return TestResult::FatalError; + } + + m_obtainedResult = "step: " + m_optimizerStep + "\n\n" + printed + "\n"; return checkResult(_stream, _linePrefix, _formatted); } -bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) +std::pair, std::shared_ptr> YulOptimizerTest::parse( + ostream& _stream, + string const& _linePrefix, + bool const _formatted, + string const& _source +) { ErrorList errors; soltestAssert(m_dialect, ""); - std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors); - if (!m_ast || !m_analysisInfo || !Error::containsOnlyWarnings(errors)) + shared_ptr object; + shared_ptr analysisInfo; + std::tie(object, analysisInfo) = yul::test::parse(_source, *m_dialect, errors); + if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; printErrors(_stream, errors); - return false; + return {}; } - return true; + return {std::move(object), std::move(analysisInfo)}; } void YulOptimizerTest::disambiguate() { - *m_ast = std::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_ast)); + *m_object->code = std::get(Disambiguator(*m_dialect, *m_analysisInfo)(*m_object->code)); m_analysisInfo.reset(); updateContext(); } void YulOptimizerTest::updateContext() { - m_nameDispenser = make_unique(*m_dialect, *m_ast, m_reservedIdentifiers); + m_nameDispenser = make_unique(*m_dialect, *m_object->code, m_reservedIdentifiers); m_context = make_unique(OptimiserStepContext{ *m_dialect, *m_nameDispenser, @@ -392,7 +496,7 @@ void YulOptimizerTest::updateContext() void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors) { - SourceReferenceFormatter formatter(_stream); + SourceReferenceFormatter formatter(_stream, true, false); for (auto const& error: _errors) formatter.printErrorInformation(*error); diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h index 5bba65682cef..6baeca7280d0 100644 --- a/test/libyul/YulOptimizerTest.h +++ b/test/libyul/YulOptimizerTest.h @@ -38,7 +38,7 @@ using ErrorList = std::vector>; namespace solidity::yul { struct AsmAnalysisInfo; -struct Block; +struct Object; struct Dialect; } @@ -58,7 +58,9 @@ class YulOptimizerTest: public solidity::frontend::test::EVMVersionRestrictedTes TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; private: - bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); + std::pair, std::shared_ptr> parse( + std::ostream& _stream, std::string const& _linePrefix, bool const _formatted, std::string const& _source + ); void disambiguate(); void updateContext(); @@ -71,7 +73,7 @@ class YulOptimizerTest: public solidity::frontend::test::EVMVersionRestrictedTes std::unique_ptr m_nameDispenser; std::unique_ptr m_context; - std::shared_ptr m_ast; + std::shared_ptr m_object; std::shared_ptr m_analysisInfo; }; diff --git a/test/libyul/ewasmTranslationTests/balance.yul b/test/libyul/ewasmTranslationTests/balance.yul index f8bb5017aed1..e9adc3470f57 100644 --- a/test/libyul/ewasmTranslationTests/balance.yul +++ b/test/libyul/ewasmTranslationTests/balance.yul @@ -6,7 +6,7 @@ // Trace: // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 -// 20: 0000000000000000000000000000000000000000000000000000000022222222 +// 20: 0000000000000000000000000000000022222222000000000000000000000000 // Storage dump: -// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000022222222 -// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000022222222 +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000022222222000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000022222222000000000000000000000000 diff --git a/test/libyul/ewasmTranslationTests/callvalue.yul b/test/libyul/ewasmTranslationTests/callvalue.yul index cf9104f7aa90..99bfa0f312dd 100644 --- a/test/libyul/ewasmTranslationTests/callvalue.yul +++ b/test/libyul/ewasmTranslationTests/callvalue.yul @@ -4,6 +4,6 @@ // ---- // Trace: // Memory dump: -// 20: 0000000000000000000000005555555500000000000000000000000000000000 +// 20: 5555555500000000000000000000000000000000000000000000000000000000 // Storage dump: -// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000005555555500000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000000: 5555555500000000000000000000000000000000000000000000000000000000 diff --git a/test/libyul/ewasmTranslationTests/difficulty.yul b/test/libyul/ewasmTranslationTests/difficulty.yul index c467d51a7bb7..fe706155d929 100644 --- a/test/libyul/ewasmTranslationTests/difficulty.yul +++ b/test/libyul/ewasmTranslationTests/difficulty.yul @@ -4,6 +4,6 @@ // ---- // Trace: // Memory dump: -// 20: 0000000000000000000000000000000000000000000000000000000009999999 +// 20: 9999990900000000000000000000000000000000000000000000000000000000 // Storage dump: -// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000009999999 +// 0000000000000000000000000000000000000000000000000000000000000000: 9999990900000000000000000000000000000000000000000000000000000000 diff --git a/test/libyul/ewasmTranslationTests/extcodesize.yul b/test/libyul/ewasmTranslationTests/extcodesize.yul index fb12db5ea70a..99df58cbc90e 100644 --- a/test/libyul/ewasmTranslationTests/extcodesize.yul +++ b/test/libyul/ewasmTranslationTests/extcodesize.yul @@ -4,6 +4,6 @@ // ---- // Trace: // Memory dump: -// 20: 000000000000000000000000000000000000000000000000000000000000077b +// 20: 0000000000000000000000000000000000000000000000000000000000000dd6 // Storage dump: -// 0000000000000000000000000000000000000000000000000000000000000000: 000000000000000000000000000000000000000000000000000000000000077b +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000dd6 diff --git a/test/libyul/ewasmTranslationTests/gasprice.yul b/test/libyul/ewasmTranslationTests/gasprice.yul index 97334e442fac..1d668fa6f932 100644 --- a/test/libyul/ewasmTranslationTests/gasprice.yul +++ b/test/libyul/ewasmTranslationTests/gasprice.yul @@ -4,6 +4,6 @@ // ---- // Trace: // Memory dump: -// 20: 0000000000000000000000006666666600000000000000000000000000000000 +// 20: 6666666600000000000000000000000000000000000000000000000000000000 // Storage dump: -// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000006666666600000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000000: 6666666600000000000000000000000000000000000000000000000000000000 diff --git a/test/libyul/ewasmTranslationTests/memoryguard.yul b/test/libyul/ewasmTranslationTests/memoryguard.yul new file mode 100644 index 000000000000..5aafc88d23b5 --- /dev/null +++ b/test/libyul/ewasmTranslationTests/memoryguard.yul @@ -0,0 +1,12 @@ +{ + mstore(0x40, memoryguard(0x0102030405060708)) + sstore(1, mload(0x40)) +} +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000001 +// 20: 0000000000000000000000000000000000000000000000000102030405060708 +// 80: 0000000000000000000000000000000000000000000000000102030405060708 +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000102030405060708 diff --git a/test/libyul/functionSideEffects/cyclic_graph.yul b/test/libyul/functionSideEffects/cyclic_graph.yul index 58eba0acf483..640bb7057d75 100644 --- a/test/libyul/functionSideEffects/cyclic_graph.yul +++ b/test/libyul/functionSideEffects/cyclic_graph.yul @@ -4,7 +4,7 @@ function c() { b() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: -// c: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop +// b: movable apart from effects, can loop +// c: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/doubly_recursive_function.yul b/test/libyul/functionSideEffects/doubly_recursive_function.yul index 9afcfb0afb1c..aa137abb0294 100644 --- a/test/libyul/functionSideEffects/doubly_recursive_function.yul +++ b/test/libyul/functionSideEffects/doubly_recursive_function.yul @@ -3,6 +3,6 @@ function b() { a() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: -// b: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop +// b: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/empty.yul b/test/libyul/functionSideEffects/empty.yul index 9e85e64b7ef2..e3cc25d670b4 100644 --- a/test/libyul/functionSideEffects/empty.yul +++ b/test/libyul/functionSideEffects/empty.yul @@ -1,4 +1,4 @@ { } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize diff --git a/test/libyul/functionSideEffects/empty_with_sstore.yul b/test/libyul/functionSideEffects/empty_with_sstore.yul index 42a1b564c7f2..b2cd4fa574bb 100644 --- a/test/libyul/functionSideEffects/empty_with_sstore.yul +++ b/test/libyul/functionSideEffects/empty_with_sstore.yul @@ -2,4 +2,4 @@ sstore(0, 1) } // ---- -// : invalidatesStorage +// : writes storage diff --git a/test/libyul/functionSideEffects/memory.yul b/test/libyul/functionSideEffects/memory.yul new file mode 100644 index 000000000000..d39ffce6876d --- /dev/null +++ b/test/libyul/functionSideEffects/memory.yul @@ -0,0 +1,14 @@ +{ + function a() { mstore8(0, 32) } + function f() { a() } + function g() { sstore(0, 1) } // does not affect memory + function h() { pop(mload(0)) } + function i() { pop(msize()) } +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes memory +// f: writes memory +// g: writes storage +// h: movable apart from effects, can be removed if no msize, reads memory +// i: can be removed, can be removed if no msize, reads memory diff --git a/test/libyul/functionSideEffects/mload_in_function.yul b/test/libyul/functionSideEffects/mload_in_function.yul index 170535926b04..d7977e2b59e2 100644 --- a/test/libyul/functionSideEffects/mload_in_function.yul +++ b/test/libyul/functionSideEffects/mload_in_function.yul @@ -7,5 +7,5 @@ sstore(0, mload(0)) } // ---- -// : invalidatesStorage, invalidatesMemory -// foo: invalidatesMemory +// : can loop, writes storage, writes memory +// foo: can loop, writes memory diff --git a/test/libyul/functionSideEffects/multi_calls.yul b/test/libyul/functionSideEffects/multi_calls.yul index f60b15e3dfd7..5d36c10dfaa6 100644 --- a/test/libyul/functionSideEffects/multi_calls.yul +++ b/test/libyul/functionSideEffects/multi_calls.yul @@ -15,8 +15,8 @@ } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: invalidatesStorage -// b: invalidatesStorage -// c: invalidatesStorage, invalidatesMemory -// d: movable, sideEffectFree, sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: can loop, writes storage +// b: can loop, writes storage +// c: can loop, writes storage, writes memory +// d: movable, movable apart from effects, can be removed, can be removed if no msize diff --git a/test/libyul/functionSideEffects/otherImmovables.yul b/test/libyul/functionSideEffects/otherImmovables.yul new file mode 100644 index 000000000000..bb02dba1d8ac --- /dev/null +++ b/test/libyul/functionSideEffects/otherImmovables.yul @@ -0,0 +1,12 @@ +{ + function a() { pop(gas()) } + function f() { a() } + function g() { stop() } + function h() { invalid() } +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: can be removed, can be removed if no msize +// f: can be removed, can be removed if no msize +// g: +// h: diff --git a/test/libyul/functionSideEffects/recursive_function.yul b/test/libyul/functionSideEffects/recursive_function.yul index 875339cbad3f..e65a00c2b09d 100644 --- a/test/libyul/functionSideEffects/recursive_function.yul +++ b/test/libyul/functionSideEffects/recursive_function.yul @@ -2,5 +2,5 @@ function a() { a() } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable apart from effects, can loop diff --git a/test/libyul/functionSideEffects/simple_functions.yul b/test/libyul/functionSideEffects/simple_functions.yul index fb0f5378e72c..61db9d27b155 100644 --- a/test/libyul/functionSideEffects/simple_functions.yul +++ b/test/libyul/functionSideEffects/simple_functions.yul @@ -6,9 +6,9 @@ function i() { let z := mload(0) } } // ---- -// : movable, sideEffectFree, sideEffectFreeIfNoMSize -// a: movable, sideEffectFree, sideEffectFreeIfNoMSize -// f: invalidatesMemory -// g: invalidatesStorage -// h: sideEffectFree, sideEffectFreeIfNoMSize -// i: sideEffectFreeIfNoMSize +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: movable, movable apart from effects, can be removed, can be removed if no msize +// f: writes memory +// g: writes storage +// h: can be removed, can be removed if no msize, reads memory +// i: movable apart from effects, can be removed if no msize, reads memory diff --git a/test/libyul/functionSideEffects/state.yul b/test/libyul/functionSideEffects/state.yul new file mode 100644 index 000000000000..e605ff1e514a --- /dev/null +++ b/test/libyul/functionSideEffects/state.yul @@ -0,0 +1,10 @@ +{ + function a() { pop(call(100, 0x010, 10, 0x00, 32, 0x0100, 32))} + function f() { a() } + function g() { sstore(0, 1) } +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes other state, writes storage, writes memory +// f: writes other state, writes storage, writes memory +// g: writes storage diff --git a/test/libyul/functionSideEffects/storage.yul b/test/libyul/functionSideEffects/storage.yul new file mode 100644 index 000000000000..038c368a29d9 --- /dev/null +++ b/test/libyul/functionSideEffects/storage.yul @@ -0,0 +1,12 @@ +{ + function a() { sstore(0, 1) } + function f() { a() } + function g() { pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32))} + function h() { pop(sload(0))} +} +// ---- +// : movable, movable apart from effects, can be removed, can be removed if no msize +// a: writes storage +// f: writes storage +// g: writes other state, writes storage, writes memory +// h: movable apart from effects, can be removed, can be removed if no msize, reads storage diff --git a/test/libyul/functionSideEffects/structures.yul b/test/libyul/functionSideEffects/structures.yul index de1fa5b0c244..9c844f66fc6f 100644 --- a/test/libyul/functionSideEffects/structures.yul +++ b/test/libyul/functionSideEffects/structures.yul @@ -31,10 +31,10 @@ } } // ---- -// : invalidatesStorage, invalidatesMemory -// f: sideEffectFreeIfNoMSize -// g: invalidatesStorage, invalidatesMemory -// h: invalidatesStorage, invalidatesMemory -// i: invalidatesStorage -// r: movable, sideEffectFree, sideEffectFreeIfNoMSize -// t: invalidatesMemory +// : writes storage, writes memory +// f: movable apart from effects, can be removed if no msize, reads memory +// g: writes storage, writes memory +// h: writes storage, writes memory +// i: writes storage +// r: movable, movable apart from effects, can be removed, can be removed if no msize +// t: writes memory diff --git a/test/libyul/functionSideEffects/with_loop.yul b/test/libyul/functionSideEffects/with_loop.yul index d68aa1fec27d..6e9bc643612e 100644 --- a/test/libyul/functionSideEffects/with_loop.yul +++ b/test/libyul/functionSideEffects/with_loop.yul @@ -4,6 +4,6 @@ pop(f()) } // ---- -// : -// f: -// g: +// : movable apart from effects, can loop +// f: movable apart from effects, can loop +// g: movable apart from effects, can loop diff --git a/test/libyul/objectCompiler/immutable_long_name_does_not_end_up_in_bytecode.yul b/test/libyul/objectCompiler/immutable_long_name_does_not_end_up_in_bytecode.yul index b7058289b519..a208d15a5c16 100644 --- a/test/libyul/objectCompiler/immutable_long_name_does_not_end_up_in_bytecode.yul +++ b/test/libyul/objectCompiler/immutable_long_name_does_not_end_up_in_bytecode.yul @@ -1,6 +1,7 @@ object "a" { code { setimmutable( + 0, "long___name___that___definitely___exceeds___the___thirty___two___byte___limit", 0x1234567890123456789012345678901234567890 ) @@ -8,10 +9,12 @@ object "a" { } // ---- // Assembly: -// /* "source":152:194 */ +// /* "source":167:209 */ // 0x1234567890123456789012345678901234567890 -// /* "source":32:204 */ +// /* "source":58:59 */ +// 0x00 +// /* "source":32:219 */ // assignImmutable("0x85a5b1db611c82c46f5fa18e39ae218397536256c451e5de155a86de843a9ad6") -// Bytecode: 73123456789012345678901234567890123456789050 -// Opcodes: PUSH20 0x1234567890123456789012345678901234567890 POP -// SourceMappings: 152:42:0:-:0;32:172 +// Bytecode: 73123456789012345678901234567890123456789060005050 +// Opcodes: PUSH20 0x1234567890123456789012345678901234567890 PUSH1 0x0 POP POP +// SourceMappings: 167:42:0:-:0;58:1;32:187 diff --git a/test/libyul/objectCompiler/wasm/no_main_function.yul b/test/libyul/objectCompiler/wasm/no_main_function.yul new file mode 100644 index 000000000000..2dd91a166236 --- /dev/null +++ b/test/libyul/objectCompiler/wasm/no_main_function.yul @@ -0,0 +1,22 @@ +{ + function not_main() { + i64.drop(i64.add(0, 1)) + } +} +// ==== +// wasm: true +// ---- +// Text: +// (module +// (memory $memory (export "memory") 1) +// +// (func $not_main +// (block $label_ +// (drop (i64.add (i64.const 0) (i64.const 1))) +// ) +// ) +// +// ) +// +// Binary: +// 0061736d01000000010401600000020100030201000503010001060100070a01066d656d6f727902000a0d010b000240420042017c1a0b0b diff --git a/test/libyul/objectCompiler/wasm/simple.yul b/test/libyul/objectCompiler/wasm/simple.yul new file mode 100644 index 000000000000..b158aea52bf0 --- /dev/null +++ b/test/libyul/objectCompiler/wasm/simple.yul @@ -0,0 +1,23 @@ +{ + function main() { + i64.drop(i64.add(0, 1)) + } +} +// ==== +// wasm: true +// ---- +// Text: +// (module +// (memory $memory (export "memory") 1) +// (export "main" (func $main)) +// +// (func $main +// (block $label_ +// (drop (i64.add (i64.const 0) (i64.const 1))) +// ) +// ) +// +// ) +// +// Binary: +// 0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0d010b000240420042017c1a0b0b diff --git a/test/libyul/objectCompiler/wasm/subObject.yul b/test/libyul/objectCompiler/wasm/subObject.yul new file mode 100644 index 000000000000..683e64be1e98 --- /dev/null +++ b/test/libyul/objectCompiler/wasm/subObject.yul @@ -0,0 +1,22 @@ +object "a" { + code {} + // Unreferenced data is not added to the assembled bytecode. + data "str" "Hello, World!" + object "sub" { code { function main() { i64.drop(11) } } } +} +// ==== +// wasm: true +// ---- +// Text: +// (module +// ;; custom section for sub-module +// ;; The Keccak-256 hash of the text representation of "sub": 78ac3419d75c8d6f42f663717b8e964eeb994d77ff175145133084422dbd23d7 +// ;; (@custom "sub" "0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0a0108000240420b1a0b0b") +// ;; custom section for data +// ;; (@custom "str" "48656c6c6f2c20576f726c6421") +// (memory $memory (export "memory") 1) +// +// ) +// +// Binary: +// 0061736d010000000101000201000301000503010001060100070a01066d656d6f727902000040037375620061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0a0108000240420b1a0b0b00110373747248656c6c6f2c20576f726c64210a0100 diff --git a/test/libyul/objectCompiler/wasm/subObjectAccess.yul b/test/libyul/objectCompiler/wasm/subObjectAccess.yul new file mode 100644 index 000000000000..8acced97f69d --- /dev/null +++ b/test/libyul/objectCompiler/wasm/subObjectAccess.yul @@ -0,0 +1,79 @@ +object "A" { + code { + function main() { + // TODO: support this + // i64.drop(dataoffset("A")) + // i64.drop(datasize("A")) + i64.drop(dataoffset("B")) + i64.drop(datasize("B")) + // TODO: support sub-subobjects + // i64.drop(dataoffset("B.C")) + // i64.drop(datasize("B.C")) + // i64.drop(dataoffset("B.E")) + // i64.drop(datasize("B.E")) + // i64.drop(dataoffset("B.C.D")) + // i64.drop(datasize("B.C.D")) + } + } + + data "data1" "Hello, World!" + + object "B" { + code { + function main() { + i64.drop(dataoffset("C")) + i64.drop(datasize("C")) + i64.drop(dataoffset("E")) + i64.drop(datasize("E")) + // i64.drop(dataoffset("C.D")) + // i64.drop(datasize("C.D")) + } + } + object "C" { + code { + function main() { + i64.drop(dataoffset("D")) + i64.drop(datasize("D")) + } + } + object "D" { + code { + function main() { + unreachable() + } + } + } + } + object "E" { + code { + function main() { + unreachable() + } + } + } + } +} +// ==== +// wasm: true +// ---- +// Text: +// (module +// ;; custom section for sub-module +// ;; The Keccak-256 hash of the text representation of "B": ccfc48ce1c0d0542ffd25ae6858777b2f7b8a6d2b6608f679458182e719f5434 +// ;; (@custom "B" "0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e0000007f01430061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e0000003c01440061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a080106000240000b0b0a0d010b00024042341a423a1a0b0b003c01450061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a080106000240000b0b0a15011300024042341a42fd001a42b5011a423a1a0b0b") +// ;; custom section for data +// ;; (@custom "data1" "48656c6c6f2c20576f726c6421") +// (memory $memory (export "memory") 1) +// (export "main" (func $main)) +// +// (func $main +// (block $label_ +// (drop (dataoffset "B")) +// (drop (datasize "B")) +// ) +// ) +// +// ) +// +// Binary: +// 0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e000000880201420061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e0000007f01430061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e0000003c01440061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a080106000240000b0b0a0d010b00024042341a423a1a0b0b003c01450061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a080106000240000b0b0a15011300024042341a42fd001a42b5011a423a1a0b0b001305646174613148656c6c6f2c20576f726c64210a0e010c00024042351a4286021a0b0b diff --git a/test/libyul/yulInterpreterTests/bounded_recursion.yul b/test/libyul/yulInterpreterTests/bounded_recursion.yul new file mode 100644 index 000000000000..ce22638ca188 --- /dev/null +++ b/test/libyul/yulInterpreterTests/bounded_recursion.yul @@ -0,0 +1,16 @@ +{ + function f(x) -> y { + if lt(x, 150) { + y := f(add(x, 1)) + } + if eq(x, 150) { + y := x + } + } + mstore(0, f(0)) +} +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000096 +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul b/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul new file mode 100644 index 000000000000..c1456b00f355 --- /dev/null +++ b/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul @@ -0,0 +1,14 @@ +{ + function f(x) -> y + { + // 32 nested additions are computed in + // exactly 66 expression evaluation steps + y := add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,x)))))))))))))))))))))))))))))))) + } + mstore(0,f(0)) +} +// ---- +// Trace: +// Maximum expression nesting level reached. +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul b/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul new file mode 100644 index 000000000000..0a347057129b --- /dev/null +++ b/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul @@ -0,0 +1,14 @@ +{ + function f(x) -> y + { + // 31 nested additions are computed in + // exactly 64 expression evaluation steps + y := add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,add(0x1,x))))))))))))))))))))))))))))))) + } + mstore(0,f(0)) +} +// ---- +// Trace: +// Memory dump: +// 0: 000000000000000000000000000000000000000000000000000000000000001f +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/infinite_recursion.yul b/test/libyul/yulInterpreterTests/infinite_recursion.yul new file mode 100644 index 000000000000..0e3109ab5e07 --- /dev/null +++ b/test/libyul/yulInterpreterTests/infinite_recursion.yul @@ -0,0 +1,11 @@ +{ + function f() { + f() + } + f() +} +// ---- +// Trace: +// Interpreter execution step limit reached. +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul new file mode 100644 index 000000000000..45d3cc781c71 --- /dev/null +++ b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul @@ -0,0 +1,44 @@ +{ + function f() { + log0(0x0, 0x0) + f() + } + f() +} +// ---- +// Trace: +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// LOG0(0, 0) +// Trace size limit reached. +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/long_obect_name.yul b/test/libyul/yulInterpreterTests/long_obect_name.yul new file mode 100644 index 000000000000..4105c6e42df4 --- /dev/null +++ b/test/libyul/yulInterpreterTests/long_obect_name.yul @@ -0,0 +1,19 @@ +object "t" { + code { + datacopy(not(datasize("object2.object3.object4.datablock")), 0, 0) + } + object "object2" { + code{} + object "object3" { + code{} + object "object4" { + code{} + data "datablock" "" + } + } + } +} +// ---- +// Trace: +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul new file mode 100644 index 000000000000..4e431a6b182e --- /dev/null +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul @@ -0,0 +1,10 @@ +{ + pop(byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Trace: +// CALL(0, 0, 0, 0, 0, 0, 0) +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul new file mode 100644 index 000000000000..b520b9181cbd --- /dev/null +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul @@ -0,0 +1,11 @@ +{ + function f() -> x { mstore(0, 0x1337) } + pop(byte(0, shr(0x8, f()))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000001337 +// Storage dump: diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul index 0f723c6c18de..c8c4e4e1388a 100644 --- a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/object_access.yul @@ -17,11 +17,14 @@ object "main" { // ---- // step: commonSubexpressionEliminator // -// { -// let r := "abc" -// let a := datasize("abc") -// let x := dataoffset("abc") -// let y := a -// datacopy(r, x, a) -// mstore(a, x) +// object "main" { +// code { +// let r := "abc" +// let a := datasize("abc") +// let x := dataoffset("abc") +// let y := a +// datacopy(r, x, a) +// mstore(a, x) +// } +// data "abc" hex"48656c6c6f2c20576f726c6421" // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul index 7833193954af..595b9c792f49 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/assigned_vars_multi.yul @@ -2,6 +2,7 @@ function f() -> x, z {} let c, d := f() let y := add(d, add(c, 7)) + sstore(y, 20) } // ---- // step: expressionSimplifier @@ -10,5 +11,5 @@ // function f() -> x, z // { } // let c, d := f() -// let y := add(add(d, c), 7) +// sstore(add(add(d, c), 7), 20) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/byte_after_shr_non_mul_of_8.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/byte_after_shr_non_mul_of_8.yul new file mode 100644 index 000000000000..e7c7bbf88103 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/byte_after_shr_non_mul_of_8.yul @@ -0,0 +1,9 @@ +{ + sstore(0, byte(0, shr(0x9, calldataload(0)))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { sstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul index d465600e3c6c..7288ef56f9c8 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and.yul @@ -1,7 +1,11 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let x := calldataload(0) let a := and(0xff, shr(248, shl(248, shr(248, x)))) let b := shr(12, shl(8, and(x, 0xf0f0))) + sstore(a, b) } // ==== // EVMVersion: >byzantium @@ -10,6 +14,6 @@ // // { // let x := calldataload(0) -// let a := shr(248, x) -// let b := and(shr(4, x), 3855) +// let a := and(0xff, and(shr(248, x), 255)) +// sstore(a, shr(12, and(shl(8, x), 15790080))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul index 6649a86b57cb..f57a5108b8e0 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_2.yul @@ -1,4 +1,7 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let x := calldataload(0) let a := and(0xff, shr(248, shl(248, shr(248, and(x, 0xf))))) let b := shl(12, shr(4, and(x, 0xf0f0))) @@ -7,6 +10,13 @@ let e := shl(255, shr(4, and(0xf0f0, x))) let f := shl(12, shr(256, and(0xf0f0, x))) let g := shl(256, shr(4, and(0xf0f0, x))) + sstore(10, a) + sstore(11, b) + sstore(12, c) + sstore(13, d) + sstore(14, e) + sstore(15, f) + sstore(16, g) } // ==== // EVMVersion: >byzantium @@ -15,11 +25,24 @@ // // { // let x := calldataload(0) -// let a := 0 -// let b := and(shl(8, x), 15790080) -// let c := and(shl(8, x), 15790080) -// let d := 0 -// let e := and(shl(251, x), 0x8000000000000000000000000000000000000000000000000000000000000000) +// let _2 := 0xf +// let _5 := and(shr(248, x), 0) +// let _10 := 0xff +// let a := and(_5, 255) +// let _14 := and(shr(4, x), 3855) +// let _15 := 12 +// let b := shl(_15, _14) +// let _19 := and(shr(4, x), 3855) +// let c := shl(_15, _19) +// let d := shl(_15, and(shr(255, x), 0)) +// let e := shl(_10, _19) // let f := 0 // let g := 0 +// sstore(10, a) +// sstore(11, b) +// sstore(_15, c) +// sstore(13, d) +// sstore(14, e) +// sstore(_2, f) +// sstore(16, g) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul index 4211b6afec10..14ff1b375df3 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/combine_shift_and_and_3.yul @@ -1,10 +1,19 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let x := calldataload(0) let a := shl(12, shr(4, x)) let b := shl(4, shr(12, x)) let c := shr(12, shl(4, x)) let d := shr(4, shl(12, x)) let e := shl(150, shr(2, shl(150, x))) + sstore(15, x) + sstore(16, a) + sstore(17, b) + sstore(18, c) + sstore(19, d) + sstore(20, e) } // ==== // EVMVersion: >byzantium @@ -17,5 +26,12 @@ // let b := and(shr(8, x), 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0) // let c := and(shr(8, x), 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) // let d := and(shl(8, x), 0x0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00) -// let e := 0 +// let _14 := 150 +// let e := shl(_14, and(shl(148, x), 0x3ffffffffffffffffffffffffff0000000000000000000000000000000000000)) +// sstore(15, x) +// sstore(16, a) +// sstore(17, b) +// sstore(18, c) +// sstore(19, d) +// sstore(20, e) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul index 2f655efbae59..56d18fb7b3f6 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul @@ -1,5 +1,8 @@ -{ let a := add(7, sub(mload(0), 7)) } +{ + let a := add(7, sub(mload(0), 7)) + mstore(20, a) +} // ---- // step: expressionSimplifier // -// { let a := mload(0) } +// { mstore(20, mload(0)) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul index 8bf04b987ff0..ce2949993104 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul @@ -1,5 +1,8 @@ -{ let a := add(1, mul(3, 4)) } +{ + let a := add(1, mul(3, 4)) + sstore(7, a) +} // ---- // step: expressionSimplifier // -// { let a := 13 } +// { sstore(7, 13) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul index bbda84653601..8a526599227c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create2_and_mask.yul @@ -1,6 +1,10 @@ { + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. let a := and(create2(0, 0, 0x20, 0), 0xffffffffffffffffffffffffffffffffffffffff) let b := and(0xffffffffffffffffffffffffffffffffffffffff, create2(0, 0, 0x20, 0)) + sstore(a, b) } // ==== // EVMVersion: >=constantinople @@ -8,6 +12,9 @@ // step: expressionSimplifier // // { -// let a := create2(0, 0, 0x20, 0) -// let b := create2(0, 0, 0x20, 0) +// let _1 := 0xffffffffffffffffffffffffffffffffffffffff +// let _2 := 0 +// let _3 := 0x20 +// let a := and(create2(_2, _2, _3, _2), _1) +// sstore(a, and(_1, create2(_2, _2, _3, _2))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul index 44a8cabb0b83..8e77b8726bb1 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/create_and_mask.yul @@ -1,11 +1,20 @@ { - let a := and(create(0, 0, 0x20), 0xffffffffffffffffffffffffffffffffffffffff) + // This is not fully simplified on purpose because we + // need another split step in between. The full simplification + // is tested in the fullSuite. + let c := create(0, 0, 0x20) + let a := and(c, 0xffffffffffffffffffffffffffffffffffffffff) let b := and(0xffffffffffffffffffffffffffffffffffffffff, create(0, 0, 0x20)) + sstore(a, b) } // ---- // step: expressionSimplifier // // { -// let a := create(0, 0, 0x20) -// let b := create(0, 0, 0x20) +// let _1 := 0x20 +// let _2 := 0 +// let c := create(_2, _2, _1) +// let _4 := 0xffffffffffffffffffffffffffffffffffffffff +// let a := and(c, _4) +// sstore(a, and(_4, create(_2, _2, _1))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/exp_simplifications.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/exp_simplifications.yul new file mode 100644 index 000000000000..bfc1cbb2b060 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/exp_simplifications.yul @@ -0,0 +1,24 @@ +{ + let t := calldataload(0) + sstore(0, exp(0, t)) + sstore(1, exp(1, t)) + sstore(2, exp(2, t)) + // The following should not be simplified + sstore(3, exp(8, t)) + sstore(4, exp(115792089237316195423570985008687907853269984665640564039457584007913129639935, t)) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { +// let _1 := 0 +// let t := calldataload(_1) +// sstore(_1, iszero(t)) +// sstore(1, 1) +// let _8 := 2 +// sstore(_8, shl(t, 1)) +// sstore(3, exp(8, t)) +// sstore(4, sub(iszero(and(t, 1)), and(t, 1))) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul index 78d51db73903..9698dfdff4db 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/idempotency.yul @@ -3,6 +3,7 @@ let z := calldataload(1) let t := and(and(x, z), x) let w := or(or(x, z), x) + sstore(t, w) } // ---- // step: expressionSimplifier @@ -11,5 +12,5 @@ // let x := calldataload(0) // let z := calldataload(1) // let t := and(x, z) -// let w := or(x, z) +// sstore(t, or(x, z)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul index 38502d22cedb..6bb6a751b814 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul @@ -1,5 +1,8 @@ -{ let a := sub(calldataload(0), calldataload(0)) } +{ + let a := sub(calldataload(0), calldataload(0)) + sstore(0, a) +} // ---- // step: expressionSimplifier // -// { let a := 0 } +// { sstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul index 5f3d268ca561..686ae2b63523 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul @@ -1,7 +1,11 @@ -{ let a := sub(calldataload(1), calldataload(0)) } +{ + let a := sub(calldataload(1), calldataload(0)) + sstore(0, a) +} // ---- // step: expressionSimplifier // // { -// let a := sub(calldataload(1), calldataload(0)) +// let _1 := 0 +// sstore(_1, sub(calldataload(1), calldataload(_1))) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul index d75ceb83148d..57d84ae153d1 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul @@ -1,11 +1,9 @@ { let a := mload(0) let b := sub(a, a) + sstore(0, b) } // ---- // step: expressionSimplifier // -// { -// let a := mload(0) -// let b := 0 -// } +// { sstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul index 73b6bfa545fd..a035494b80c6 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul @@ -1,6 +1,7 @@ { function f() -> a {} let b := add(7, sub(f(), 7)) + sstore(0, b) } // ---- // step: expressionSimplifier @@ -8,5 +9,5 @@ // { // function f() -> a // { } -// let b := f() +// sstore(0, f()) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul index fc75f131f942..d7da5e41fe31 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul @@ -1,11 +1,9 @@ { let a := mload(sub(7, 7)) let b := sub(a, 0) + sstore(0, b) } // ---- // step: expressionSimplifier // -// { -// let a := mload(0) -// let b := a -// } +// { sstore(0, mload(0)) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul index 073db8d4e33c..22bfaf91fd29 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/large_byte_access.yul @@ -4,13 +4,23 @@ let c := byte(20, a) // create cannot be removed. let d := byte(33, create(0, 0, 0x20)) + sstore(7, a) + sstore(8, b) + sstore(9, c) + sstore(10, d) } // ---- // step: expressionSimplifier // // { -// let a := calldataload(0) +// let _1 := 0 +// let a := calldataload(_1) // let b := 0 // let c := byte(20, a) -// let d := byte(33, create(0, 0, 0x20)) +// pop(create(_1, _1, 0x20)) +// let d := 0 +// sstore(7, a) +// sstore(8, b) +// sstore(9, c) +// sstore(10, d) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul index 65a325d6c20b..696d0b11174c 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul @@ -5,5 +5,6 @@ // step: expressionSimplifier // // { -// mstore(0, and(calldataload(0), 255)) +// let _4 := 0 +// mstore(_4, and(calldataload(_4), 255)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul index d4e35af33925..81fe7e117e34 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul @@ -5,5 +5,6 @@ // step: expressionSimplifier // // { -// mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) +// let _4 := 0 +// mstore(_4, and(calldataload(_4), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul index b7bc3e4f99c6..ccb490892bb2 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul @@ -1,6 +1,7 @@ { function f(a) -> b { } let c := sub(f(0), f(1)) + sstore(0, c) } // ---- // step: expressionSimplifier @@ -8,5 +9,7 @@ // { // function f(a) -> b // { } -// let c := sub(f(0), f(1)) +// let _2 := f(1) +// let _3 := 0 +// sstore(_3, sub(f(_3), _2)) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul index b43440128a01..7f44c220806a 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul @@ -2,6 +2,7 @@ function f1() -> a { } function f2() -> b { } let c := sub(f1(), f2()) + sstore(0, c) } // ---- // step: expressionSimplifier @@ -11,5 +12,5 @@ // { } // function f2() -> b // { } -// let c := sub(f1(), f2()) +// sstore(0, sub(f1(), f2())) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul index c0fa43e2c7e5..6ed704aeae47 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul @@ -1,13 +1,14 @@ // Even if the functions pass the equality check, they are not movable. { - function f() -> a { } + function f() -> a { mstore(0, 1) } let b := sub(f(), f()) + sstore(b, 8) } // ---- // step: expressionSimplifier // // { // function f() -> a -// { } -// let b := sub(f(), f()) +// { mstore(a, 1) } +// sstore(sub(f(), f()), 8) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul index d153ae724337..37065cd4dca6 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul @@ -1,11 +1,17 @@ // The first argument of div is not constant. // keccak256 is not movable. { + sstore(0, msize()) let a := div(keccak256(0, 0), 0) + sstore(20, a) } // ---- // step: expressionSimplifier // // { -// let a := div(keccak256(0, 0), 0) +// let _1 := msize() +// let _2 := 0 +// sstore(_2, _1) +// pop(keccak256(_2, _2)) +// sstore(20, 0) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul new file mode 100644 index 000000000000..af34e76bdefb --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_call.yul @@ -0,0 +1,13 @@ +{ + sstore(0, byte(0, shr(0x8, call(0, 0, 0, 0, 0, 0, 0)))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { +// let _1 := 0 +// pop(call(_1, _1, _1, _1, _1, _1, _1)) +// sstore(_1, 0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul new file mode 100644 index 000000000000..0d9513ec2887 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func.yul @@ -0,0 +1,15 @@ +{ + function f() -> x { mstore(0, 1337) } + mstore(0, byte(0, shr(0x8, f()))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { +// function f() -> x +// { mstore(x, 1337) } +// pop(f()) +// mstore(0, 0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul new file mode 100644 index 000000000000..448e071d8fe5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/pop_byte_shr_func_trivial.yul @@ -0,0 +1,10 @@ +{ + function f() -> x {} + sstore(0, byte(0, shr(0x8, f()))) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: expressionSimplifier +// +// { sstore(0, 0) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul index fbc5befd0431..d87b84f5950a 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reassign.yul @@ -7,7 +7,8 @@ // step: expressionSimplifier // // { -// let x := mload(0) -// x := 0 -// mstore(0, 7) +// let _1 := 0 +// let x := mload(_1) +// x := _1 +// mstore(_1, 7) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul index b81ae96197a5..060a4e13ff77 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/remove_redundant_shift_masking.yul @@ -3,6 +3,8 @@ let b := and(shr(248, calldataload(0)), 0xff) let c := and(shr(249, calldataload(0)), 0xfa) let d := and(shr(247, calldataload(0)), 0xff) + sstore(a, b) + sstore(c, d) } // ==== // EVMVersion: >=constantinople @@ -10,8 +12,12 @@ // step: expressionSimplifier // // { -// let a := shr(248, calldataload(0)) -// let b := shr(248, calldataload(0)) -// let c := and(shr(249, calldataload(0)), 0xfa) -// let d := and(shr(247, calldataload(0)), 0xff) +// let _2 := calldataload(0) +// let _5 := 0xff +// let a := shr(248, _2) +// let b := shr(248, _2) +// let c := and(shr(249, _2), 0xfa) +// let d := and(shr(247, _2), _5) +// sstore(a, b) +// sstore(c, d) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul index 550c7becb16c..88263e98e751 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/replace_too_large_shift.yul @@ -3,6 +3,8 @@ let b := shr(299, calldataload(1)) let c := shl(255, calldataload(2)) let d := shr(255, calldataload(3)) + sstore(a, b) + sstore(c, d) } // ==== // EVMVersion: >=constantinople @@ -12,6 +14,10 @@ // { // let a := 0 // let b := 0 -// let c := shl(255, calldataload(2)) -// let d := shr(255, calldataload(3)) +// let _8 := calldataload(2) +// let _9 := 255 +// let c := shl(_9, _8) +// let d := shr(_9, calldataload(3)) +// sstore(a, b) +// sstore(c, d) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul index c1c0093ef4db..9b0087dd79c2 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/return_vars_zero.yul @@ -2,12 +2,15 @@ { function f() -> c, d { let y := add(d, add(c, 7)) + sstore(0, y) } + let t, v := f() } // ---- // step: expressionSimplifier // // { // function f() -> c, d -// { let y := 7 } +// { sstore(d, 7) } +// let t, v := f() // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul index 9ca956c103d2..32c8775721e8 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul @@ -1,7 +1,11 @@ { let a := add(0, mload(0)) + sstore(0, a) } // ---- // step: expressionSimplifier // -// { let a := mload(0) } +// { +// let _1 := 0 +// sstore(_1, mload(_1)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul index 693c05c42829..8a9bb7c199db 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_not_supported.yul @@ -1,9 +1,9 @@ { - let ret := balance(address()) + sstore(0, balance(address())) } // ==== // EVMVersion: =istanbul @@ -9,5 +10,5 @@ // // { // let a := address() -// let ret := selfbalance() +// sstore(a, selfbalance()) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul index 7b0a7524e45f..bc3c6c4d7014 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/selfbalance_supported.yul @@ -1,9 +1,9 @@ { - let ret := balance(address()) + sstore(0, balance(address())) } // ==== // EVMVersion: >=istanbul // ---- // step: expressionSimplifier // -// { let ret := selfbalance() } +// { sstore(0, selfbalance()) } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul index e88877365a9e..b052fe403332 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigend_vars_multi.yul @@ -2,11 +2,12 @@ { let c, d let y := add(d, add(c, 7)) + sstore(0, y) } // ---- // step: expressionSimplifier // // { // let c, d -// let y := 7 +// sstore(d, 7) // } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul index d66d4efb2ce0..9dd4e3365d3f 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/unassigned_vars.yul @@ -3,12 +3,9 @@ let c let d let y := add(d, add(c, 7)) + sstore(8, y) } // ---- // step: expressionSimplifier // -// { -// let c -// let d -// let y := 7 -// } +// { sstore(8, 7) } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul index 60492e027233..bcd6d0f7bba5 100644 --- a/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul +++ b/test/libyul/yulOptimizerTests/expressionSplitter/object_access.yul @@ -12,14 +12,17 @@ object "main" { // ---- // step: expressionSplitter // -// { -// let x := dataoffset("abc") -// let y := datasize("abc") -// let _1 := 2 -// let _2 := mload(_1) -// let _3 := 1 -// let _4 := mload(_3) -// let _5 := 0 -// let _6 := mload(_5) -// datacopy(_6, _4, _2) +// object "main" { +// code { +// let x := dataoffset("abc") +// let y := datasize("abc") +// let _1 := 2 +// let _2 := mload(_1) +// let _3 := 1 +// let _4 := mload(_3) +// let _5 := 0 +// let _6 := mload(_5) +// datacopy(_6, _4, _2) +// } +// data "abc" hex"48656c6c6f2c20576f726c6421" // } diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/connected.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/connected.yul new file mode 100644 index 000000000000..82ab48a513b2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/connected.yul @@ -0,0 +1,62 @@ +{ + mstore(0x40, memoryguard(0)) + function g() -> a, b { + a := 21 + let $c := 1 + b,a,$c := z() + } + function f() -> x { + let $x2 + $x2 := 42 + let $x3, $x4 := g() + x := mul(add($x2, $x3), h($x4)) + sstore($x3, $x4) + } + function h(v) -> a { + let x, $z, y := z() + a, $z, v := z() + } + function z() -> a,b,c { let $x := 0 } + sstore(0, f()) + let x, y := g() +} +// ---- +// step: fakeStackLimitEvader +// +// { +// mstore(0x40, memoryguard(0xa0)) +// function g() -> a, b +// { +// a := 21 +// mstore(0x60, 1) +// let b_1, a_2, $c_3 := z() +// mstore(0x60, $c_3) +// a := a_2 +// b := b_1 +// } +// function f() -> x +// { +// mstore(0x20, 0) +// mstore(0x20, 42) +// let $x3_4, $x4_5 := g() +// mstore(0x00, $x4_5) +// mstore(0x40, $x3_4) +// x := mul(add(mload(0x20), mload(0x40)), h(mload(0x00))) +// sstore(mload(0x40), mload(0x00)) +// } +// function h(v) -> a_1 +// { +// let x_2_6, $z_7, y_8 := z() +// mstore(0x60, $z_7) +// let y := y_8 +// let x_2 := x_2_6 +// let a_1_9, $z_10, v_11 := z() +// mstore(0x60, $z_10) +// v := v_11 +// a_1 := a_1_9 +// } +// function z() -> a_3, b_4, c +// { mstore(0x80, 0) } +// sstore(0, f()) +// let x_5, y_6 := g() +// } diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/function_arg.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/function_arg.yul new file mode 100644 index 000000000000..6ce2e34b4151 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/function_arg.yul @@ -0,0 +1,20 @@ +{ + mstore(0x40, memoryguard(0)) + let $x := 0 + sstore(0, $x) + function h($hx) -> y { + y := $hx + } + sstore(1, h(32)) +} +// ---- +// step: fakeStackLimitEvader +// +// { +// mstore(0x40, memoryguard(0x40)) +// mstore(0x00, 0) +// sstore(0, mload(0x00)) +// function h($hx) -> y +// { y := $hx } +// sstore(1, h(32)) +// } diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/outer_block.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/outer_block.yul new file mode 100644 index 000000000000..023100cddee6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/outer_block.yul @@ -0,0 +1,13 @@ +{ + mstore(0x40, memoryguard(0x80)) + let $x := 42 + sstore(42, $x) +} +// ---- +// step: fakeStackLimitEvader +// +// { +// mstore(0x40, memoryguard(0xa0)) +// mstore(0x80, 42) +// sstore(42, mload(0x80)) +// } diff --git a/test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul new file mode 100644 index 000000000000..d2fcb116ea74 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fakeStackLimitEvader/stub.yul @@ -0,0 +1,88 @@ +{ + mstore(0x40, memoryguard(0)) + function f() { + let $fx + let $fy := 42 + sstore($fx, $fy) + $fx := 21 + } + function g(gx) { + let $gx, $gy := tuple2() + { $gx, $gy := tuple2() } + { $gx, gx := tuple2() } + { gx, $gy := tuple2() } + } + function h(hx, hy, hz, hw) { + let $hx, $hy, $hz, $hw := tuple4() + { hx, $hy, hz, $hw := tuple4() } + { $hx, $hy, hz, hw := tuple4() } + } + function tuple2() -> a, b {} + function tuple4() -> a, b, c, d {} + f() + g(0) + h(1, 2, 3, 4) +} +// ---- +// step: fakeStackLimitEvader +// +// { +// mstore(0x40, memoryguard(0x80)) +// function f() +// { +// mstore(0x40, 0) +// mstore(0x60, 42) +// sstore(mload(0x40), mload(0x60)) +// mstore(0x40, 21) +// } +// function g(gx) +// { +// let $gx_1, $gy_2 := tuple2() +// mstore(0x40, $gy_2) +// mstore(0x60, $gx_1) +// { +// let $gx_3, $gy_4 := tuple2() +// mstore(0x40, $gy_4) +// mstore(0x60, $gx_3) +// } +// { +// let $gx_5, gx_6 := tuple2() +// mstore(0x60, $gx_5) +// gx := gx_6 +// } +// { +// let gx_7, $gy_8 := tuple2() +// mstore(0x40, $gy_8) +// gx := gx_7 +// } +// } +// function h(hx, hy, hz, hw) +// { +// let $hx_9, $hy_10, $hz_11, $hw_12 := tuple4() +// mstore(0x00, $hw_12) +// mstore(0x60, $hz_11) +// mstore(0x40, $hy_10) +// mstore(0x20, $hx_9) +// { +// let hx_13, $hy_14, hz_15, $hw_16 := tuple4() +// mstore(0x00, $hw_16) +// mstore(0x40, $hy_14) +// hz := hz_15 +// hx := hx_13 +// } +// { +// let $hx_17, $hy_18, hz_19, hw_20 := tuple4() +// mstore(0x40, $hy_18) +// mstore(0x20, $hx_17) +// hw := hw_20 +// hz := hz_19 +// } +// } +// function tuple2() -> a, b +// { } +// function tuple4() -> a_1, b_2, c, d +// { } +// f() +// g(0) +// h(1, 2, 3, 4) +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul index 56cac2704e64..a5194457a408 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul @@ -9,22 +9,25 @@ // { // { // let _1 := 7 -// let a_6 := 3 -// let x_7 := 0 -// x_7 := add(a_6, a_6) -// let b_8 := x_7 -// let c_9 := _1 -// let y_10 := 0 -// y_10 := mul(mload(c_9), f(b_8)) -// let y_1 := y_10 +// let a_8 := 3 +// let x_9 := 0 +// x_9 := add(a_8, a_8) +// let b_10 := x_9 +// let c_11 := _1 +// let y_12 := 0 +// let a_6_13 := b_10 +// let x_7_14 := 0 +// x_7_14 := add(a_6_13, a_6_13) +// y_12 := mul(mload(c_11), x_7_14) +// let y_1 := y_12 // } // function f(a) -> x // { x := add(a, a) } // function g(b, c) -> y // { -// let a_13 := b -// let x_14 := 0 -// x_14 := add(a_13, a_13) -// y := mul(mload(c), x_14) +// let a_6 := b +// let x_7 := 0 +// x_7 := add(a_6, a_6) +// y := mul(mload(c), x_7) // } // } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul index 98c8e301065f..14e977dd3a67 100644 --- a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -26,30 +26,19 @@ // step: fullInliner // // { -// { -// let x_8 := 100 -// mstore(0, x_8) -// mstore(7, h()) -// g(10) -// mstore(1, x_8) -// } +// { f(100) } // function f(x) // { // mstore(0, x) -// let t_20 := 0 -// t_20 := 2 -// mstore(7, t_20) -// g(10) +// let t_8 := 0 +// t_8 := 2 +// mstore(7, t_8) +// let x_1_9 := 10 +// f(1) // mstore(1, x) // } // function g(x_1) -// { -// let x_14 := 1 -// mstore(0, x_14) -// mstore(7, h()) -// g(10) -// mstore(1, x_14) -// } +// { f(1) } // function h() -> t // { t := 2 } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index 2f400186aae6..248fcc66102c 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1081,13 +1081,13 @@ // let _2 := mload(0) // if slt(sub(_1, _2), 64) { revert(0, 0) } // sstore(0, and(calldataload(_2), sub(shl(160, 1), 1))) -// let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) +// let x0, x1, x2, x3, x4 := abi_decode_addresst_uint256t_bytes_calldatat_enum$_Operation(mload(7), mload(8)) // sstore(x1, x0) // sstore(x3, x2) // sstore(1, x4) -// pop(abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(mload(30), mload(31), mload(32), mload(33), mload(34), mload(35), mload(36), mload(37), mload(38), mload(39), mload(40), mload(41))) +// pop(abi_encode_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint(mload(30), mload(31), mload(32), mload(33), mload(34), mload(35), mload(36), mload(37), mload(38), mload(39), mload(40), mload(41))) // } -// function abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(headStart, dataEnd) -> value0, value1, value2, value3, value4 +// function abi_decode_addresst_uint256t_bytes_calldatat_enum$_Operation(headStart, dataEnd) -> value0, value1, value2, value3, value4 // { // if slt(sub(dataEnd, headStart), 128) { revert(value4, value4) } // value0 := and(calldataload(headStart), sub(shl(160, 1), 1)) @@ -1106,7 +1106,7 @@ // if iszero(lt(_3, 3)) { revert(value4, value4) } // value4 := _3 // } -// function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart, value10, value9, value8, value7, value6, value5, value4, value3, value2, value1, value0) -> tail +// function abi_encode_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint(headStart, value10, value9, value8, value7, value6, value5, value4, value3, value2, value1, value0) -> tail // { // tail := add(headStart, 352) // mstore(headStart, value0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index 5e6e0305dfa2..44183b555188 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -473,25 +473,25 @@ // let i := _1 // for { } lt(i, length) { i := add(i, 1) } // { -// abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(srcPtr), pos) +// let _3 := mload(srcPtr) +// let pos_1 := pos +// let srcPtr_1 := _3 +// let i_1 := _1 +// for { } lt(i_1, 0x3) { i_1 := add(i_1, 1) } +// { +// mstore(pos_1, and(mload(srcPtr_1), sub(shl(160, 1), 1))) +// srcPtr_1 := add(srcPtr_1, 0x20) +// pos_1 := add(pos_1, 0x20) +// } // srcPtr := add(srcPtr, 0x20) // pos := add(pos, 0x60) // } -// let _3 := mload(64) -// let _4 := mload(0x20) -// if slt(sub(_3, _4), 128) { revert(_1, _1) } -// let offset := calldataload(add(_4, 64)) -// let _5 := 0xffffffffffffffff -// if gt(offset, _5) { revert(_1, _1) } -// let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_4, offset), _3) -// let offset_1 := calldataload(add(_4, 0x60)) -// if gt(offset_1, _5) { revert(_1, _1) } -// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_4, offset_1), _3) -// sstore(calldataload(_4), calldataload(add(_4, 0x20))) -// sstore(value2, value3) +// let a, b, c, d := abi_decode_uint256t_uint256t_array$_t_uint256_$dynt_array$_t_array$_t_uint256_memory_$dyn(mload(0x20), mload(64)) +// sstore(a, b) +// sstore(c, d) // sstore(_1, pos) // } -// function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset, end) -> array +// function abi_decode_t_array$_t_array$_t_uint256_memory_$dyn(offset, end) -> array // { // let _1 := 0x1f // if iszero(slt(add(offset, _1), end)) { revert(array, array) } @@ -508,14 +508,13 @@ // for { } lt(i, length) { i := add(i, 1) } // { // if iszero(slt(add(src, _1), end)) { revert(0, 0) } -// let _4 := 0x2 -// let dst_1 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(_4)) +// let dst_1 := allocateMemory(_3) // let dst_2 := dst_1 // let src_1 := src -// let _5 := add(src, _3) -// if gt(_5, end) { revert(0, 0) } +// let _4 := add(src, _3) +// if gt(_4, end) { revert(0, 0) } // let i_1 := 0 -// for { } lt(i_1, _4) { i_1 := add(i_1, 1) } +// for { } lt(i_1, 0x2) { i_1 := add(i_1, 1) } // { // mstore(dst_1, calldataload(src_1)) // dst_1 := add(dst_1, _2) @@ -523,39 +522,38 @@ // } // mstore(dst, dst_2) // dst := add(dst, _2) -// src := _5 +// src := _4 // } // } -// function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset, end) -> array +// function abi_decode_uint256t_uint256t_array$_t_uint256_$dynt_array$_t_array$_t_uint256_memory_$dyn(headStart, dataEnd) -> value0, value1, value2, value3 // { -// if iszero(slt(add(offset, 0x1f), end)) { revert(array, array) } -// let length := calldataload(offset) -// array := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length)) -// let dst := array -// mstore(array, length) -// let _1 := 0x20 -// dst := add(array, _1) -// let src := add(offset, _1) -// if gt(add(add(offset, mul(length, _1)), _1), end) { revert(0, 0) } -// let i := 0 +// if slt(sub(dataEnd, headStart), 128) { revert(value2, value2) } +// value0 := calldataload(headStart) +// let _1 := 32 +// value1 := calldataload(add(headStart, _1)) +// let offset := calldataload(add(headStart, 64)) +// let _2 := 0xffffffffffffffff +// if gt(offset, _2) { revert(value2, value2) } +// let _3 := add(headStart, offset) +// if iszero(slt(add(_3, 0x1f), dataEnd)) { revert(value2, value2) } +// let length := calldataload(_3) +// let dst := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length)) +// let dst_1 := dst +// mstore(dst, length) +// dst := add(dst, _1) +// let src := add(_3, _1) +// if gt(add(add(_3, mul(length, _1)), _1), dataEnd) { revert(value2, value2) } +// let i := value2 // for { } lt(i, length) { i := add(i, 1) } // { // mstore(dst, calldataload(src)) // dst := add(dst, _1) // src := add(src, _1) // } -// } -// function abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(value, pos) -// { -// let srcPtr := value -// let i := 0 -// for { } lt(i, 0x3) { i := add(i, 1) } -// { -// mstore(pos, and(mload(srcPtr), sub(shl(160, 1), 1))) -// let _1 := 0x20 -// srcPtr := add(srcPtr, _1) -// pos := add(pos, _1) -// } +// value2 := dst_1 +// let offset_1 := calldataload(add(headStart, 96)) +// if gt(offset_1, _2) { revert(value3, value3) } +// value3 := abi_decode_t_array$_t_array$_t_uint256_memory_$dyn(add(headStart, offset_1), dataEnd) // } // function allocateMemory(size) -> memPtr // { @@ -569,9 +567,4 @@ // if gt(length, 0xffffffffffffffff) { revert(size, size) } // size := add(mul(length, 0x20), 0x20) // } -// function array_allocation_size_t_array$_t_uint256_$2_memory(length) -> size -// { -// if gt(length, 0xffffffffffffffff) { revert(size, size) } -// size := mul(length, 0x20) -// } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and.yul new file mode 100644 index 000000000000..1abea23e7802 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and.yul @@ -0,0 +1,17 @@ +{ + let x := calldataload(0) + let a := and(0xff, shr(248, shl(248, shr(248, x)))) + let b := shr(12, shl(8, and(x, 0xf0f0))) + sstore(a, b) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// sstore(shr(248, x), and(shr(4, x), 3855)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_2.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_2.yul new file mode 100644 index 000000000000..513a23aa5066 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_2.yul @@ -0,0 +1,35 @@ +{ + let x := calldataload(0) + let a := and(0xff, shr(248, shl(248, shr(248, and(x, 0xf))))) + let b := shl(12, shr(4, and(x, 0xf0f0))) + let c := shl(12, shr(4, and(0xf0f0, x))) + let d := shl(12, shr(255, and(0xf0f0, x))) + let e := shl(255, shr(4, and(0xf0f0, x))) + let f := shl(12, shr(256, and(0xf0f0, x))) + let g := shl(256, shr(4, and(0xf0f0, x))) + sstore(10, a) + sstore(11, b) + sstore(12, c) + sstore(13, d) + sstore(14, e) + sstore(15, f) + sstore(16, g) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// let b := and(shl(8, x), 15790080) +// sstore(10, 0) +// sstore(11, b) +// sstore(12, b) +// sstore(13, 0) +// sstore(14, and(shl(251, x), shl(255, 1))) +// sstore(0xf, 0) +// sstore(16, 0) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_3.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_3.yul new file mode 100644 index 000000000000..17de07de7cd7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_3.yul @@ -0,0 +1,32 @@ +{ + let x := calldataload(0) + let a := shl(12, shr(4, x)) + let b := shl(4, shr(12, x)) + let c := shr(12, shl(4, x)) + let d := shr(4, shl(12, x)) + let e := shl(150, shr(2, shl(150, x))) + sstore(15, x) + sstore(16, a) + sstore(17, b) + sstore(18, c) + sstore(19, d) + sstore(20, e) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// let _1 := shl(8, x) +// let _2 := shr(8, x) +// sstore(15, x) +// sstore(16, and(_1, not(4095))) +// sstore(17, and(_2, sub(shl(248, 1), 16))) +// sstore(18, and(_2, sub(shl(244, 1), 1))) +// sstore(19, and(_1, sub(shl(252, 1), 256))) +// sstore(20, 0) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_unsplit.yul b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_unsplit.yul new file mode 100644 index 000000000000..923a18f4fd91 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/combine_shift_and_and_unsplit.yul @@ -0,0 +1,19 @@ +{ + let x := calldataload(0) + // This checks that the expression simplifier + // does not modify unsplit expressions. + let a := and(0xff, shr(248, shl(248, shr(248, x)))) + let b := shr(12, shl(8, and(x, 0xf0f0))) + sstore(a, b) +} +// ==== +// EVMVersion: >byzantium +// ---- +// step: fullSuite +// +// { +// { +// let x := calldataload(0) +// sstore(shr(248, x), and(shr(4, x), 3855)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/create2_and_mask.yul b/test/libyul/yulOptimizerTests/fullSuite/create2_and_mask.yul new file mode 100644 index 000000000000..813c2706e3d5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/create2_and_mask.yul @@ -0,0 +1,21 @@ +{ + // This does not optimize the masks away. Due to the way the expression simplifier + // is built, it would have to create another `create2` opcode for the simplification + // which would be fatal. + let a := and(create2(0, 0, 0x20, 0), 0xffffffffffffffffffffffffffffffffffffffff) + let b := and(0xffffffffffffffffffffffffffffffffffffffff, create2(0, 0, 0x20, 0)) + sstore(a, b) +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: fullSuite +// +// { +// { +// let _1 := sub(shl(160, 1), 1) +// let _2 := 0 +// let a := and(create2(_2, _2, 0x20, _2), _1) +// sstore(a, and(_1, create2(_2, _2, 0x20, _2))) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/create_and_mask.yul b/test/libyul/yulOptimizerTests/fullSuite/create_and_mask.yul new file mode 100644 index 000000000000..c89c620bb52d --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/create_and_mask.yul @@ -0,0 +1,20 @@ +{ + // This does not optimize the masks away. Due to the way the expression simplifier + // is built, it would have to create another `create` opcode for the simplification + // which would be fatal. + let a := and(create(0, 0, 0x20), 0xffffffffffffffffffffffffffffffffffffffff) + let b := and(0xffffffffffffffffffffffffffffffffffffffff, create(0, 0, 0x20)) + sstore(a, b) +} +// ==== +// EVMVersion: >=istanbul +// ---- +// step: fullSuite +// +// { +// { +// let _1 := sub(shl(160, 1), 1) +// let a := and(create(0, 0, 0x20), _1) +// sstore(a, and(_1, create(0, 0, 0x20))) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/name_cleaner_reserved.yul b/test/libyul/yulOptimizerTests/fullSuite/name_cleaner_reserved.yul new file mode 100644 index 000000000000..576f5abb9098 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/name_cleaner_reserved.yul @@ -0,0 +1,34 @@ +{ + // This function name can be shortened, the other cannot. + function nonmstore_(x) { + nonmstore_(x) + sstore(10, calldataload(2)) + } + function mstore_(x) -> y { + let t3_3_ := mstore_(x) + y := 8 + sstore(y, calldataload(y)) + } + let t2_ := mstore_(7) + nonmstore_(70) +} +// ---- +// step: fullSuite +// +// { +// { +// pop(mstore_(7)) +// nonmstore(70) +// } +// function nonmstore(x) +// { +// nonmstore(x) +// sstore(10, calldataload(2)) +// } +// function mstore_(x) -> y +// { +// pop(mstore_(x)) +// y := 8 +// sstore(y, calldataload(y)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/reserved_identifiers.yul b/test/libyul/yulOptimizerTests/fullSuite/reserved_identifiers.yul new file mode 100644 index 000000000000..40bcbd64a427 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/reserved_identifiers.yul @@ -0,0 +1,32 @@ +{ + function g_(x) -> z_ { + if calldataload(1) { z_ := g_(x) } + sstore(z_, calldataload(add(x, 1))) + } + function datasize_(x) -> linkersymbol_ { + if calldataload(0) { linkersymbol_ := datasize_(x) } + sstore(linkersymbol_, calldataload(linkersymbol_)) + } + let dataoffset_ := datasize_(7) + let x_ := g_(9) + sstore(dataoffset_, x_) +} +// ---- +// step: fullSuite +// +// { +// { +// let dataoffset_ := datasize_(7) +// sstore(dataoffset_, g(9)) +// } +// function g(x) -> z +// { +// if calldataload(1) { z := g(x) } +// sstore(z, calldataload(add(x, 1))) +// } +// function datasize_(x) -> linkersymbol_ +// { +// if calldataload(linkersymbol_) { linkersymbol_ := datasize_(x) } +// sstore(linkersymbol_, calldataload(linkersymbol_)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul index 78af81f383f2..102a47bc2d63 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul @@ -36,16 +36,16 @@ // // { // { -// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) -// let a_1, b_1 := abi_decode_t_bytes_calldata_ptr(a, b) -// let a_2, b_2 := abi_decode_t_bytes_calldata_ptr(a_1, b_1) -// let a_3, b_3 := abi_decode_t_bytes_calldata_ptr(a_2, b_2) -// let a_4, b_4 := abi_decode_t_bytes_calldata_ptr(a_3, b_3) -// let a_5, b_5 := abi_decode_t_bytes_calldata_ptr(a_4, b_4) -// let a_6, b_6 := abi_decode_t_bytes_calldata_ptr(a_5, b_5) +// let a, b := abi_decode_t_bytes_calldata(mload(0), mload(1)) +// let a_1, b_1 := abi_decode_t_bytes_calldata(a, b) +// let a_2, b_2 := abi_decode_t_bytes_calldata(a_1, b_1) +// let a_3, b_3 := abi_decode_t_bytes_calldata(a_2, b_2) +// let a_4, b_4 := abi_decode_t_bytes_calldata(a_3, b_3) +// let a_5, b_5 := abi_decode_t_bytes_calldata(a_4, b_4) +// let a_6, b_6 := abi_decode_t_bytes_calldata(a_5, b_5) // mstore(a_6, b_6) // } -// function abi_decode_t_bytes_calldata_ptr(offset, end) -> arrayPos, length +// function abi_decode_t_bytes_calldata(offset, end) -> arrayPos, length // { // if iszero(slt(add(offset, 0x1f), end)) { revert(arrayPos, arrayPos) } // length := calldataload(offset) diff --git a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul index 56a2d6cfe0c5..b417991c271e 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul @@ -49,7 +49,7 @@ // sstore(not(gcd(10, 15)), 1) // sstore(0, 0) // sstore(2, 1) -// pop(foo_singlereturn_1(calldataload(0), calldataload(3))) +// extcodecopy(1, msize(), 1, 1) // sstore(0, 0) // sstore(3, 1) // } @@ -59,6 +59,4 @@ // case 0 { out := _a } // default { out := gcd(_b, mod(_a, _b)) } // } -// function foo_singlereturn_1(in, in_1) -> out -// { extcodecopy(1, msize(), 1, 1) } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul new file mode 100644 index 000000000000..b7f0b4afbf80 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul @@ -0,0 +1,34 @@ +{ + let x, y := foo(sload(0),sload(32)) + sstore(0, x) + sstore(0, y) + x, y := foo(sload(32), sload(8)) + + function foo(a, b) -> out1, out2 + { + out1 := mload(32) + out1 := sload(out1) + out2 := add(out1, 1) + extcodecopy(out1, out2, 1, b) + // to prevent foo from getting inlined + if iszero(out1) { leave } + } +} +// ---- +// step: fullSuite +// +// { +// { +// let out1, out2 := foo(sload(32)) +// sstore(0, out1) +// sstore(0, out2) +// let out1_1, out2_1 := foo(sload(8)) +// } +// function foo(b) -> out1, out2 +// { +// out1 := sload(mload(32)) +// out2 := add(out1, 1) +// extcodecopy(out1, out2, 1, b) +// if iszero(out1) { leave } +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_loop.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_loop.yul new file mode 100644 index 000000000000..b531842dbd6d --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_loop.yul @@ -0,0 +1,39 @@ +{ + sstore(f(1), 1) + sstore(f(2), 1) + sstore(f(3), 1) + function f(a) -> x { + for {let b := 10} iszero(b) { b := sub(b, 1) } + { + a := calldataload(0) + mstore(a, x) + // to prevent f from getting inlined + if iszero(a) { leave } + } + } +} +// ---- +// step: fullSuite +// +// { +// { +// f() +// sstore(0, 1) +// f() +// sstore(0, 1) +// f() +// sstore(0, 1) +// } +// function f() +// { +// let b := 10 +// let _1 := 0 +// let a := calldataload(_1) +// let _2 := iszero(a) +// for { } iszero(b) { b := add(b, not(0)) } +// { +// mstore(a, _1) +// if _2 { leave } +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_recursion.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_recursion.yul new file mode 100644 index 000000000000..69b4517eb365 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_recursion.yul @@ -0,0 +1,29 @@ +{ + let j, k, l := f(1, 2, 3) + sstore(0, j) + sstore(1, k) + sstore(1, l) + function f(a, b, c) -> x, y, z + { + x, y, z := f(1, 2, 3) + x := add(x, 1) + } +} +// ---- +// step: fullSuite +// +// { +// { +// let x, y, z := f() +// sstore(0, x) +// sstore(1, y) +// sstore(1, z) +// } +// function f() -> x, y, z +// { +// let x_1, y_1, z_1 := f() +// y := y_1 +// z := z_1 +// x := add(x_1, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul new file mode 100644 index 000000000000..14f0137cb162 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul @@ -0,0 +1,37 @@ +{ + let x, y, z := foo(sload(0),sload(32)) + sstore(0, x) + sstore(0, y) + sstore(0, z) + x, y, z := foo(sload(32), sload(8)) + + // out3 is unassigned. + function foo(a, b) -> out1, out2, out3 + { + out1 := mload(32) + out1 := sload(out1) + out2 := add(out1, 1) + extcodecopy(out1, out1, 1, b) + // to prevent foo from getting inlined + if iszero(out1) { leave } + } +} +// ---- +// step: fullSuite +// +// { +// { +// let out1, out2 := foo(sload(32)) +// sstore(0, out1) +// sstore(0, out2) +// sstore(0, 0) +// let out1_1, out2_1 := foo(sload(8)) +// } +// function foo(b) -> out1, out2 +// { +// out1 := sload(mload(32)) +// out2 := add(out1, 1) +// extcodecopy(out1, out1, 1, b) +// if iszero(out1) { leave } +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_simple.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_simple.yul new file mode 100644 index 000000000000..531b1a6ce992 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_simple.yul @@ -0,0 +1,31 @@ +{ + sstore(f(1), 1) + sstore(f(2), 1) + sstore(f(3), 1) + function f(a) -> x { + // The usage of a is redundant + a := calldataload(0) + mstore(a, x) + // to prevent f from getting inlined + if iszero(a) { leave } + } +} +// ---- +// step: fullSuite +// +// { +// { +// f() +// sstore(0, 1) +// f() +// sstore(0, 1) +// f() +// sstore(0, 1) +// } +// function f() +// { +// let a := calldataload(0) +// mstore(a, 0) +// if iszero(a) { leave } +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul new file mode 100644 index 000000000000..3c11ca65f0db --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/complex_move.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(keccak256(mul(mload(inv), 3), 32)) + a := add(x, 1) + sstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := extcodesize(keccak256(mul(mload(inv), 3), 32)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// sstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul new file mode 100644 index 000000000000..f48cb78f152d --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/create_sload.yul @@ -0,0 +1,30 @@ +{ + function g() -> x { x := create(100, 0, 32) } + function f() -> x { x := mload(0) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + // cannot be moved because of the create call in g() + let q := sload(5) + let r := g() + // This can be moved + let z := f() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := create(100, 0, 32) } +// function f() -> x_1 +// { x_1 := mload(0) } +// let b := 1 +// let a := 1 +// let z := f() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let q := sload(5) +// let r := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul new file mode 100644 index 000000000000..a4506a8e5d34 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_memory_function.yul @@ -0,0 +1,24 @@ +{ + function g() -> x { x := add(sload(mload(x)), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := mload(g()) + let s := keccak256(g(), 32) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := add(sload(mload(x)), 1) } +// let b := 1 +// let a := 1 +// let t := mload(g()) +// let s := keccak256(g(), 32) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul new file mode 100644 index 000000000000..a5117cb61f24 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_state_function.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := mload(g()) } + function g() -> x { x := add(sload(x), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := mload(g()) } +// function g() -> x_1 +// { x_1 := add(sload(x_1), 1) } +// let b := 1 +// let a := 1 +// let t := extcodesize(f()) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul new file mode 100644 index 000000000000..7790c0e6a3d2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/move_storage_function.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := add(x, 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := sload(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { x_1 := add(x_1, 1) } +// let b := 1 +// let a := 1 +// let t := sload(f()) +// let q := g() +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul new file mode 100644 index 000000000000..b08899613870 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_immovables.yul @@ -0,0 +1,29 @@ +{ + let a := 1 + function f() -> x {invalid()} + function g() -> y {return(0, 0)} + for { let i := 1 } iszero(eq(i, 10)) { a := add(i, 1) } { + let b := f() + let c := gas() + let d := g() + let e := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let a := 1 +// function f() -> x +// { invalid() } +// function g() -> y +// { return(0, 0) } +// let i := 1 +// for { } iszero(eq(i, 10)) { a := add(i, 1) } +// { +// let b := f() +// let c := gas() +// let d := g() +// let e := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul new file mode 100644 index 000000000000..942a3c5802cb --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory.yul @@ -0,0 +1,38 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := keccak256(inv, 32) + a := add(x, 1) + mstore(a, inv) + } + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + // mload prevents moving of extcodesize + let x := extcodesize(mload(mul(inv, 3))) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let x := keccak256(inv, 32) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := extcodesize(mload(mul(inv_2, 3))) +// a_1 := add(x_3, 1) +// mstore(a_1, inv_2) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul new file mode 100644 index 000000000000..1ebef6f9cf52 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_loop.yul @@ -0,0 +1,29 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := mload(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := mload(f()) +// let q := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul new file mode 100644 index 000000000000..2033a9be91a4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_memory_msize.yul @@ -0,0 +1,35 @@ +{ + let b := 1 + let c := msize() // prevents moving keccak and mload + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := keccak256(inv, 32) + a := add(x, 1) + } + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(mload(mul(inv, 3))) + a := add(x, 1) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let c := msize() +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let x := keccak256(inv, 32) +// a := add(x, 1) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := extcodesize(mload(mul(inv_2, 3))) +// a_1 := add(x_3, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul new file mode 100644 index 000000000000..fa6c985b4f8a --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state.yul @@ -0,0 +1,62 @@ +{ + let b := 1 + // invalidates state in post + for { let a := 1 } iszero(eq(a, 10)) {pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32))} { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + pop(create(0x08, 0x00, 0x02)) // invalidates state + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + // invalidates state in loop-condition + for { let a := 1 } iszero(create(0x08, 0x00, 0x02)) { a := add(a, 1)} { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } +// iszero(eq(a, 10)) +// { +// pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32)) +// } +// { +// let x := extcodesize(mul(inv, 3)) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// pop(create(0x08, 0x00, 0x02)) +// let x_3 := extcodesize(mul(inv_2, 3)) +// a_1 := add(x_3, 1) +// mstore(a_1, inv_2) +// } +// let a_4 := 1 +// let inv_5 := add(b, 42) +// for { } iszero(create(0x08, 0x00, 0x02)) { a_4 := add(a_4, 1) } +// { +// let x_6 := extcodesize(mul(inv_5, 3)) +// a_4 := add(x_6, 1) +// mstore(a_4, inv_5) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul new file mode 100644 index 000000000000..ab5be62d999d --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_function.yul @@ -0,0 +1,32 @@ +{ + function f() -> x { x := g() } + function g() -> x { + x := add(x, 1) + sstore(0x00, 0x00) + } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// x_1 := add(x_1, 1) +// sstore(0x00, 0x00) +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul new file mode 100644 index 000000000000..d7e16ed680f3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_loop.yul @@ -0,0 +1,29 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := g() + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := g() +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul new file mode 100644 index 000000000000..431453941816 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_state_recursive_function.yul @@ -0,0 +1,26 @@ +{ + function f() -> x { x := g() } + function g() -> x { x := g() } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := extcodesize(f()) + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { x_1 := g() } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// let t := extcodesize(f()) +// let q := sload(g()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul new file mode 100644 index 000000000000..ae20f4904f88 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_staticall_returndatasize.yul @@ -0,0 +1,32 @@ +{ + let b := 1 + // invalidates state in post + for { let a := 1 } iszero(eq(a, 10)) {pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32))} { + let inv := add(b, 42) + let x := returndatasize() + a := add(x, 1) + pop(staticcall(2, 3, 0, 32, 64, 32)) // prevents moving returndatasize + mstore(a, inv) + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } +// iszero(eq(a, 10)) +// { +// pop(call(2, 0x01, 2, 0x00, 32, 0x010, 32)) +// } +// { +// let x := returndatasize() +// a := add(x, 1) +// pop(staticcall(2, 3, 0, 32, 64, 32)) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul new file mode 100644 index 000000000000..0ef2fd78ad40 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage.yul @@ -0,0 +1,57 @@ +{ + let b := 1 + // invalidates storage in post + for { let a := 1 } iszero(eq(a, 10)) { sstore(0x00, 0x01)} { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } + + // invalidates storage in body + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + sstore(a, inv) + } + + // invalidates state in body + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32)) + let x := sload(mul(inv, 3)) + a := add(x, 1) + } + +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// for { } iszero(eq(a, 10)) { sstore(0x00, 0x01) } +// { +// let x := sload(mul(inv, 3)) +// a := add(x, 1) +// mstore(a, inv) +// } +// let a_1 := 1 +// let inv_2 := add(b, 42) +// for { } iszero(eq(a_1, 10)) { a_1 := add(a_1, 1) } +// { +// let x_3 := sload(mul(inv_2, 3)) +// a_1 := add(x_3, 1) +// sstore(a_1, inv_2) +// } +// let a_4 := 1 +// let inv_5 := add(b, 42) +// for { } iszero(eq(a_4, 10)) { a_4 := add(a_4, 1) } +// { +// pop(callcode(100, 0x010, 10, 0x00, 32, 0x0100, 32)) +// let x_6 := sload(mul(inv_5, 3)) +// a_4 := add(x_6, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul new file mode 100644 index 000000000000..f1a2d6283f6e --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_function.yul @@ -0,0 +1,28 @@ +{ + function f() -> x { x := g() } + function g() -> x { + x := add(x, 1) + sstore(0x00, 0x00) + } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let q := sload(g()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// x_1 := add(x_1, 1) +// sstore(0x00, 0x00) +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { let q := sload(g()) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul new file mode 100644 index 000000000000..d799fa6aed66 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_storage_loop.yul @@ -0,0 +1,25 @@ +{ + function f() -> x { x := g() } + function g() -> x { for {} 1 {} {} } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let t := sload(f()) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function f() -> x +// { x := g() } +// function g() -> x_1 +// { +// for { } 1 { } +// { } +// } +// let b := 1 +// let a := 1 +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { let t := sload(f()) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul new file mode 100644 index 000000000000..2840d3445b80 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/not_first.yul @@ -0,0 +1,21 @@ +{ + function g() -> x { x := add(mload(x), 1) } + + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + sstore(0, a) + let q := keccak256(g(), 32) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// function g() -> x +// { x := add(mload(x), 1) } +// let b := 1 +// let a := 1 +// let q := keccak256(g(), 32) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { sstore(0, a) } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul new file mode 100644 index 000000000000..223f54ee706c --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_memory.yul @@ -0,0 +1,25 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := mload(mul(inv, 3)) + let y := keccak256(mul(b, 13), 32) + a := add(x, 1) + sstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := mload(mul(inv, 3)) +// let y := keccak256(mul(b, 13), 32) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// sstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul new file mode 100644 index 000000000000..f8b5a3d53725 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_state.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := extcodesize(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := extcodesize(mul(inv, 3)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul new file mode 100644 index 000000000000..022d66b2da0c --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/simple_storage.yul @@ -0,0 +1,23 @@ +{ + let b := 1 + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + let inv := add(b, 42) + let x := sload(mul(inv, 3)) + a := add(x, 1) + mstore(a, inv) + } +} +// ---- +// step: loopInvariantCodeMotion +// +// { +// let b := 1 +// let a := 1 +// let inv := add(b, 42) +// let x := sload(mul(inv, 3)) +// for { } iszero(eq(a, 10)) { a := add(a, 1) } +// { +// a := add(x, 1) +// mstore(a, inv) +// } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/addmod.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/addmod.yul new file mode 100644 index 000000000000..88c60a0eeebc --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/addmod.yul @@ -0,0 +1,31 @@ +{ + let x := calldataload(0) + let y := calldataload(32) + let z := calldataload(64) + let result := addmod(x, y, z) + + // should be zero + if gt(result, z) { sstore(0, 1) } + + // addmod is equal to mod of sum for small numbers + if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { + if eq(result, mod(add(x, y), z)) { sstore(0, 9) } + } + + // but not in general + if and(and(gt(x, sub(0, 5)), gt(y, sub(0, 2))), eq(z, 3)) { + if eq(result, mod(add(x, y), z)) { sstore(0, 5) } + } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let x := calldataload(0) +// let y := calldataload(32) +// let z := calldataload(64) +// let result := addmod(x, y, z) +// if 0 { } +// if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { if 1 { sstore(0, 9) } } +// if and(and(gt(x, sub(0, 5)), gt(y, sub(0, 2))), eq(z, 3)) { if 0 { } } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith.yul new file mode 100644 index 000000000000..7ff930766342 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith.yul @@ -0,0 +1,13 @@ +{ + let x := 7 + let y := 8 + if eq(add(x, y), 15) { } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let x := 7 +// let y := 8 +// if 1 { } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith_movable.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith_movable.yul new file mode 100644 index 000000000000..2080a678c0bf --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith_movable.yul @@ -0,0 +1,19 @@ +{ + function f() -> z + { + z := 15 + } + let x := 7 + let y := 8 + if eq(add(x, y), f()) { } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// function f() -> z +// { z := 15 } +// let x := 7 +// let y := 8 +// if eq(add(x, y), f()) { } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith_non_movable.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith_non_movable.yul new file mode 100644 index 000000000000..ed50224d02a6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/arith_non_movable.yul @@ -0,0 +1,23 @@ +{ + function f() -> z + { + sstore(1, 15) + z := 15 + } + let x := 7 + let y := 8 + if eq(add(x, y), f()) { } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// function f() -> z +// { +// sstore(1, 15) +// z := 15 +// } +// let x := 7 +// let y := 8 +// if eq(add(x, y), f()) { } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/mulcheck.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/mulcheck.yul new file mode 100644 index 000000000000..31a342339332 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/mulcheck.yul @@ -0,0 +1,32 @@ +{ + let vloc_x := calldataload(0) + let vloc_y := calldataload(1) + if lt(vloc_x, shl(100, 1)) { + if lt(vloc_y, shl(100, 1)) { + if iszero(and(iszero(iszero(vloc_x)), gt(vloc_y, div(not(0), vloc_x)))) { + let vloc := mul(vloc_x, vloc_y) + sstore(0, vloc) + } + } + } +} +// ==== +// EVMVersion: >=constantinople +// ---- +// step: reasoningBasedSimplifier +// +// { +// let vloc_x := calldataload(0) +// let vloc_y := calldataload(1) +// if lt(vloc_x, shl(100, 1)) +// { +// if lt(vloc_y, shl(100, 1)) +// { +// if 1 +// { +// let vloc := mul(vloc_x, vloc_y) +// sstore(0, vloc) +// } +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/mulmod.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/mulmod.yul new file mode 100644 index 000000000000..f5226acf7743 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/mulmod.yul @@ -0,0 +1,31 @@ +{ + let x := calldataload(0) + let y := calldataload(32) + let z := calldataload(64) + let result := mulmod(x, y, z) + + // should be zero + if gt(result, z) { sstore(0, 1) } + + // mulmod is equal to mod of product for small numbers + if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { + if eq(result, mod(mul(x, y), z)) { sstore(0, 9) } + } + + // but not in general + if and(and(gt(x, sub(0, 5)), eq(y, 2)), eq(z, 3)) { + if eq(result, mod(mul(x, y), z)) { sstore(0, 5) } + } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let x := calldataload(0) +// let y := calldataload(32) +// let z := calldataload(64) +// let result := mulmod(x, y, z) +// if 0 { } +// if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { if 1 { sstore(0, 9) } } +// if and(and(gt(x, sub(0, 5)), eq(y, 2)), eq(z, 3)) { if 0 { } } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/negative_rounding.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/negative_rounding.yul new file mode 100644 index 000000000000..6e4b3f5edee3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/negative_rounding.yul @@ -0,0 +1,16 @@ +{ + let x := sub(0, 7) + let y := 2 + // (-7)/2 == -3 on the evm + if iszero(add(sdiv(x, y), 3)) { } + if iszero(add(sdiv(x, y), 4)) { } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let x := sub(0, 7) +// let y := 2 +// if 1 { } +// if 0 { } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/nested.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/nested.yul new file mode 100644 index 000000000000..d7002c99b561 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/nested.yul @@ -0,0 +1,26 @@ +{ + let x := calldataload(2) + let t := lt(x, 20) + if t { + if lt(x, 21) { } + if lt(x, 20) { } + if lt(x, 19) { } + if gt(x, 20) { } + if iszero(gt(x, 20)) { } + } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let x := calldataload(2) +// let t := lt(x, 20) +// if t +// { +// if 1 { } +// if 1 { } +// if lt(x, 19) { } +// if 0 { } +// if 1 { } +// } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/signed_division.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/signed_division.yul new file mode 100644 index 000000000000..1e409079e8a7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/signed_division.yul @@ -0,0 +1,31 @@ +{ + let y := calldataload(0) + let t := calldataload(32) + + if sgt(sub(y, 1), y) { + // y - 1 > y, i.e. y is the most negative value + if eq(sdiv(y, sub(0, 1)), y) { + // should be true: y / -1 == y + sstore(0, 7) + } + if iszero(eq(y, t)) { + // t is not the most negative value + if eq(sdiv(t, sub(0, 1)), sub(0, t)) { + // should be true: t / -1 = 0 - t + sstore(1, 7) + } + } + } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let y := calldataload(0) +// let t := calldataload(32) +// if sgt(sub(y, 1), y) +// { +// if 1 { sstore(0, 7) } +// if iszero(eq(y, t)) { if 1 { sstore(1, 7) } } +// } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/smod.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/smod.yul new file mode 100644 index 000000000000..12f04c857cc7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/smod.yul @@ -0,0 +1,53 @@ +{ + // 7 % 5 == 2 + // 7 % -5 == 2 + // -7 % 5 == -2 + // -7 % -5 == -2 + // -5 % -5 == 0 + let x := calldataload(0) + let y := calldataload(32) + let result := smod(x, y) + if eq(x, 7) { + if eq(y, 5) { + if eq(result, 2) { sstore(0, 7)} + } + if eq(y, sub(0, 5)) { + if eq(result, 2) { sstore(0, 7)} + } + } + if eq(x, sub(0, 7)) { + if eq(y, 5) { + if eq(result, sub(0, 2)) { sstore(0, 7)} + } + if eq(y, sub(0, 5)) { + if eq(result, sub(0, 2)) { sstore(0, 7)} + } + } + if eq(x, sub(0, 5)) { + if eq(y, sub(0, 5)) { + if eq(result, 0) { sstore(0, 7)} + } + } +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let x := calldataload(0) +// let y := calldataload(32) +// let result := smod(x, y) +// if eq(x, 7) +// { +// if eq(y, 5) { if 1 { sstore(0, 7) } } +// if eq(y, sub(0, 5)) { if 1 { sstore(0, 7) } } +// } +// if eq(x, sub(0, 7)) +// { +// if eq(y, 5) { if 1 { sstore(0, 7) } } +// if eq(y, sub(0, 5)) { if 1 { sstore(0, 7) } } +// } +// if eq(x, sub(0, 5)) +// { +// if eq(y, sub(0, 5)) { if 1 { sstore(0, 7) } } +// } +// } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/smoke.yul new file mode 100644 index 000000000000..6f860dc13242 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// step: reasoningBasedSimplifier +// +// { } diff --git a/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/wrapping.yul b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/wrapping.yul new file mode 100644 index 000000000000..69f5c6769757 --- /dev/null +++ b/test/libyul/yulOptimizerTests/reasoningBasedSimplifier/wrapping.yul @@ -0,0 +1,15 @@ +{ + let x := 7 + let y := 8 + if gt(sub(x, y), 20) { } + if eq(sub(x, y), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) {} +} +// ---- +// step: reasoningBasedSimplifier +// +// { +// let x := 7 +// let y := 8 +// if 1 { } +// if 1 { } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/cycle.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle.yul new file mode 100644 index 000000000000..37da7f9b83d6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle.yul @@ -0,0 +1,95 @@ +{ + mstore(0x40, memoryguard(128)) + sstore(0, g(sload(3))) + function g(x) -> v { + v := f() + } + function f() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + sstore(23, g(sload(42))) + } +} +// ---- +// step: stackLimitEvader +// +// { +// mstore(0x40, memoryguard(128)) +// sstore(0, g(sload(3))) +// function g(x) -> v +// { v := f() } +// function f() -> v_1 +// { +// let a1 := calldataload(mul(1, 4)) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// a1 := calldataload(mul(0, 4)) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, a1) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), a1) +// sstore(23, g(sload(42))) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after.yul new file mode 100644 index 000000000000..4480d012e69d --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after.yul @@ -0,0 +1,95 @@ +{ + mstore(0x40, memoryguard(128)) + sstore(0, f()) + function f() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + sstore(23, h()) + } + function h() -> v { + v := h() + } +} +// ---- +// step: stackLimitEvader +// +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(0, f()) +// function f() -> v +// { +// mstore(0x80, calldataload(mul(1, 4))) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// mstore(0x80, calldataload(mul(0, 4))) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, mload(0x80)) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), mload(0x80)) +// sstore(23, h()) +// } +// function h() -> v_1 +// { v_1 := h() } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after_2.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after_2.yul new file mode 100644 index 000000000000..4468165f1e55 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_after_2.yul @@ -0,0 +1,100 @@ +{ + mstore(0x40, memoryguard(128)) + sstore(0, f()) + function f() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + sstore(23, h()) + } + function h() -> v { + v := i() + } + function i() -> v { + v := h() + } +} +// ---- +// step: stackLimitEvader +// +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(0, f()) +// function f() -> v +// { +// mstore(0x80, calldataload(mul(1, 4))) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// mstore(0x80, calldataload(mul(0, 4))) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, mload(0x80)) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), mload(0x80)) +// sstore(23, h()) +// } +// function h() -> v_1 +// { v_1 := i() } +// function i() -> v_2 +// { v_2 := h() } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before.yul new file mode 100644 index 000000000000..e6819ea903e7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before.yul @@ -0,0 +1,103 @@ +{ + mstore(0x40, memoryguard(128)) + sstore(0, g(sload(3))) + function g(x) -> v { + switch lt(x, 3) + case 0 { + v := f() + } + case 1 { + v := g(sub(x,1)) + } + } + function f() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + } +} +// ---- +// step: stackLimitEvader +// +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(0, g(sload(3))) +// function g(x) -> v +// { +// switch lt(x, 3) +// case 0 { v := f() } +// case 1 { v := g(sub(x, 1)) } +// } +// function f() -> v_1 +// { +// mstore(0x80, calldataload(mul(1, 4))) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// mstore(0x80, calldataload(mul(0, 4))) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, mload(0x80)) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), mload(0x80)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before_2.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before_2.yul new file mode 100644 index 000000000000..a0259b1800ef --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before_2.yul @@ -0,0 +1,108 @@ +{ + mstore(0x40, memoryguard(128)) + sstore(0, g(sload(3))) + function g(x) -> v { + switch lt(x, 3) + case 0 { + v := h(x) + } + case 1 { + v := g(sub(x,f())) + } + } + function h(x) -> v { + v := g(x) + } + function f() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + } +} +// ---- +// step: stackLimitEvader +// +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(0, g(sload(3))) +// function g(x) -> v +// { +// switch lt(x, 3) +// case 0 { v := h(x) } +// case 1 { v := g(sub(x, f())) } +// } +// function h(x_1) -> v_2 +// { v_2 := g(x_1) } +// function f() -> v_3 +// { +// mstore(0x80, calldataload(mul(1, 4))) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// mstore(0x80, calldataload(mul(0, 4))) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, mload(0x80)) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), mload(0x80)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before_after.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before_after.yul new file mode 100644 index 000000000000..f5c068d5e382 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/cycle_before_after.yul @@ -0,0 +1,110 @@ +{ + mstore(0x40, memoryguard(128)) + sstore(0, g(sload(0))) + function g(x) -> v { + switch lt(x, 3) + case 0 { + v := f() + } + case 1 { + v := g(sub(x,1)) + } + } + function f() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + sstore(23, h()) + } + function h() -> v { + v := h() + } +} +// ---- +// step: stackLimitEvader +// +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(0, g(sload(0))) +// function g(x) -> v +// { +// switch lt(x, 3) +// case 0 { v := f() } +// case 1 { v := g(sub(x, 1)) } +// } +// function f() -> v_1 +// { +// mstore(0x80, calldataload(mul(1, 4))) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// mstore(0x80, calldataload(mul(0, 4))) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, mload(0x80)) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), mload(0x80)) +// sstore(23, h()) +// } +// function h() -> v_2 +// { v_2 := h() } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/function_arg.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/function_arg.yul new file mode 100644 index 000000000000..7bc4d2e8003c --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/function_arg.yul @@ -0,0 +1,88 @@ +{ + { + mstore(0x40, memoryguard(128)) + sstore(0, f(0)) + } + function f(a1) -> v { + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + } +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(0, f(0)) +// } +// function f(a1) -> v +// { +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, a1) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), a1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul new file mode 100644 index 000000000000..ce12229b6200 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/stub.yul @@ -0,0 +1,98 @@ +{ + { + mstore(0x40, memoryguard(128)) + sstore(g(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16), f()) + } + function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) -> v { + // Should be, but cannot yet be escalated. + v := b16 + } + function f() -> v{ + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + } +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0xa0)) +// sstore(g(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16), f()) +// } +// function g(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16) -> v +// { v := b16 } +// function f() -> v_1 +// { +// mstore(0x80, calldataload(mul(1, 4))) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(mul(4, 4)) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// mstore(0x80, calldataload(mul(0, 4))) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, mload(0x80)) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, 4), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), mload(0x80)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/stackLimitEvader/tree.yul b/test/libyul/yulOptimizerTests/stackLimitEvader/tree.yul new file mode 100644 index 000000000000..8407df5d0497 --- /dev/null +++ b/test/libyul/yulOptimizerTests/stackLimitEvader/tree.yul @@ -0,0 +1,339 @@ +{ + { + mstore(0x40, memoryguard(128)) + sstore(23, f()) + } + function f() -> v{ + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(g()) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,h()), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + } + function g() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + v := i() + } + function h() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(10,4)) + a1 := calldataload(mul(0,4)) + a2 := calldataload(mul(1,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + let a18 := calldataload(mul(18,4)) + let a19 := calldataload(mul(19,4)) + sstore(0, add(a1, a2)) + sstore(mul(17,4), a19) + sstore(mul(17,4), a18) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + v := i() + } + function i() -> v { + let a1 := calldataload(mul(1,4)) + let a2 := calldataload(mul(2,4)) + let a3 := calldataload(mul(3,4)) + let a4 := calldataload(mul(4,4)) + let a5 := calldataload(mul(5,4)) + let a6 := calldataload(mul(6,4)) + let a7 := calldataload(mul(7,4)) + let a8 := calldataload(mul(8,4)) + let a9 := calldataload(mul(9,4)) + a1 := calldataload(mul(0,4)) + let a10 := calldataload(mul(10,4)) + let a11 := calldataload(mul(11,4)) + let a12 := calldataload(mul(12,4)) + let a13 := calldataload(mul(13,4)) + let a14 := calldataload(mul(14,4)) + let a15 := calldataload(mul(15,4)) + let a16 := calldataload(mul(16,4)) + let a17 := calldataload(mul(17,4)) + sstore(0, a1) + sstore(mul(17,4), a17) + sstore(mul(16,4), a16) + sstore(mul(15,4), a15) + sstore(mul(14,4), a14) + sstore(mul(13,4), a13) + sstore(mul(12,4), a12) + sstore(mul(11,4), a11) + sstore(mul(10,4), a10) + sstore(mul(9,4), a9) + sstore(mul(8,4), a8) + sstore(mul(7,4), a7) + sstore(mul(6,4), a6) + sstore(mul(5,4), a5) + sstore(mul(4,4), a4) + sstore(mul(3,4), a3) + sstore(mul(2,4), a2) + sstore(mul(1,4), a1) + v := sload(mul(42,8)) + } +} +// ---- +// step: stackLimitEvader +// +// { +// { +// mstore(0x40, memoryguard(0x0100)) +// sstore(23, f()) +// } +// function f() -> v +// { +// mstore(0x80, calldataload(mul(1, 4))) +// let a2 := calldataload(mul(2, 4)) +// let a3 := calldataload(mul(3, 4)) +// let a4 := calldataload(g()) +// let a5 := calldataload(mul(5, 4)) +// let a6 := calldataload(mul(6, 4)) +// let a7 := calldataload(mul(7, 4)) +// let a8 := calldataload(mul(8, 4)) +// let a9 := calldataload(mul(9, 4)) +// mstore(0x80, calldataload(mul(0, 4))) +// let a10 := calldataload(mul(10, 4)) +// let a11 := calldataload(mul(11, 4)) +// let a12 := calldataload(mul(12, 4)) +// let a13 := calldataload(mul(13, 4)) +// let a14 := calldataload(mul(14, 4)) +// let a15 := calldataload(mul(15, 4)) +// let a16 := calldataload(mul(16, 4)) +// let a17 := calldataload(mul(17, 4)) +// sstore(0, mload(0x80)) +// sstore(mul(17, 4), a17) +// sstore(mul(16, 4), a16) +// sstore(mul(15, 4), a15) +// sstore(mul(14, 4), a14) +// sstore(mul(13, 4), a13) +// sstore(mul(12, 4), a12) +// sstore(mul(11, 4), a11) +// sstore(mul(10, 4), a10) +// sstore(mul(9, 4), a9) +// sstore(mul(8, h()), a8) +// sstore(mul(7, 4), a7) +// sstore(mul(6, 4), a6) +// sstore(mul(5, 4), a5) +// sstore(mul(4, 4), a4) +// sstore(mul(3, 4), a3) +// sstore(mul(2, 4), a2) +// sstore(mul(1, 4), mload(0x80)) +// } +// function g() -> v_1 +// { +// mstore(0xc0, calldataload(mul(1, 4))) +// let a2_3 := calldataload(mul(2, 4)) +// let a3_4 := calldataload(mul(3, 4)) +// let a4_5 := calldataload(mul(4, 4)) +// let a5_6 := calldataload(mul(5, 4)) +// let a6_7 := calldataload(mul(6, 4)) +// let a7_8 := calldataload(mul(7, 4)) +// let a8_9 := calldataload(mul(8, 4)) +// let a9_10 := calldataload(mul(9, 4)) +// mstore(0xc0, calldataload(mul(0, 4))) +// let a10_11 := calldataload(mul(10, 4)) +// let a11_12 := calldataload(mul(11, 4)) +// let a12_13 := calldataload(mul(12, 4)) +// let a13_14 := calldataload(mul(13, 4)) +// let a14_15 := calldataload(mul(14, 4)) +// let a15_16 := calldataload(mul(15, 4)) +// let a16_17 := calldataload(mul(16, 4)) +// let a17_18 := calldataload(mul(17, 4)) +// sstore(0, mload(0xc0)) +// sstore(mul(17, 4), a17_18) +// sstore(mul(16, 4), a16_17) +// sstore(mul(15, 4), a15_16) +// sstore(mul(14, 4), a14_15) +// sstore(mul(13, 4), a13_14) +// sstore(mul(12, 4), a12_13) +// sstore(mul(11, 4), a11_12) +// sstore(mul(10, 4), a10_11) +// sstore(mul(9, 4), a9_10) +// sstore(mul(8, 4), a8_9) +// sstore(mul(7, 4), a7_8) +// sstore(mul(6, 4), a6_7) +// sstore(mul(5, 4), a5_6) +// sstore(mul(4, 4), a4_5) +// sstore(mul(3, 4), a3_4) +// sstore(mul(2, 4), a2_3) +// sstore(mul(1, 4), mload(0xc0)) +// v_1 := i() +// } +// function h() -> v_19 +// { +// mstore(0xa0, calldataload(mul(1, 4))) +// mstore(0xc0, calldataload(mul(2, 4))) +// let a3_22 := calldataload(mul(3, 4)) +// let a4_23 := calldataload(mul(4, 4)) +// let a5_24 := calldataload(mul(5, 4)) +// let a6_25 := calldataload(mul(6, 4)) +// let a7_26 := calldataload(mul(7, 4)) +// let a8_27 := calldataload(mul(8, 4)) +// let a9_28 := calldataload(mul(9, 4)) +// let a10_29 := calldataload(mul(10, 4)) +// let a11_30 := calldataload(mul(10, 4)) +// mstore(0xa0, calldataload(mul(0, 4))) +// mstore(0xc0, calldataload(mul(1, 4))) +// let a12_31 := calldataload(mul(12, 4)) +// let a13_32 := calldataload(mul(13, 4)) +// let a14_33 := calldataload(mul(14, 4)) +// let a15_34 := calldataload(mul(15, 4)) +// let a16_35 := calldataload(mul(16, 4)) +// let a17_36 := calldataload(mul(17, 4)) +// let a18 := calldataload(mul(18, 4)) +// let a19 := calldataload(mul(19, 4)) +// sstore(0, add(mload(0xa0), mload(0xc0))) +// sstore(mul(17, 4), a19) +// sstore(mul(17, 4), a18) +// sstore(mul(17, 4), a17_36) +// sstore(mul(16, 4), a16_35) +// sstore(mul(15, 4), a15_34) +// sstore(mul(14, 4), a14_33) +// sstore(mul(13, 4), a13_32) +// sstore(mul(12, 4), a12_31) +// sstore(mul(11, 4), a11_30) +// sstore(mul(10, 4), a10_29) +// sstore(mul(9, 4), a9_28) +// sstore(mul(8, 4), a8_27) +// sstore(mul(7, 4), a7_26) +// sstore(mul(6, 4), a6_25) +// sstore(mul(5, 4), a5_24) +// sstore(mul(4, 4), a4_23) +// sstore(mul(3, 4), a3_22) +// sstore(mul(2, 4), mload(0xc0)) +// sstore(mul(1, 4), mload(0xa0)) +// v_19 := i() +// } +// function i() -> v_37 +// { +// mstore(0xe0, calldataload(mul(1, 4))) +// let a2_39 := calldataload(mul(2, 4)) +// let a3_40 := calldataload(mul(3, 4)) +// let a4_41 := calldataload(mul(4, 4)) +// let a5_42 := calldataload(mul(5, 4)) +// let a6_43 := calldataload(mul(6, 4)) +// let a7_44 := calldataload(mul(7, 4)) +// let a8_45 := calldataload(mul(8, 4)) +// let a9_46 := calldataload(mul(9, 4)) +// mstore(0xe0, calldataload(mul(0, 4))) +// let a10_47 := calldataload(mul(10, 4)) +// let a11_48 := calldataload(mul(11, 4)) +// let a12_49 := calldataload(mul(12, 4)) +// let a13_50 := calldataload(mul(13, 4)) +// let a14_51 := calldataload(mul(14, 4)) +// let a15_52 := calldataload(mul(15, 4)) +// let a16_53 := calldataload(mul(16, 4)) +// let a17_54 := calldataload(mul(17, 4)) +// sstore(0, mload(0xe0)) +// sstore(mul(17, 4), a17_54) +// sstore(mul(16, 4), a16_53) +// sstore(mul(15, 4), a15_52) +// sstore(mul(14, 4), a14_51) +// sstore(mul(13, 4), a13_50) +// sstore(mul(12, 4), a12_49) +// sstore(mul(11, 4), a11_48) +// sstore(mul(10, 4), a10_47) +// sstore(mul(9, 4), a9_46) +// sstore(mul(8, 4), a8_45) +// sstore(mul(7, 4), a7_44) +// sstore(mul(6, 4), a6_43) +// sstore(mul(5, 4), a5_42) +// sstore(mul(4, 4), a4_41) +// sstore(mul(3, 4), a3_40) +// sstore(mul(2, 4), a2_39) +// sstore(mul(1, 4), mload(0xe0)) +// v_37 := sload(mul(42, 8)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/LiteralRematerialiser.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/LiteralRematerialiser.yul new file mode 100644 index 000000000000..e11ea2a96f9e --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/LiteralRematerialiser.yul @@ -0,0 +1,27 @@ +{ + let a := f(mload(1)) + let b := f(a) + sstore(a, b) + function f(x) -> y + { + // Test if LiteralRematerializer can convert `y` to `0` and therefore allowing us to + // rewrite the function f + if iszero(x) { revert(y, y)} + if iszero(add(x, 1)) { revert(y, y)} + } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// let a := f_1(mload(1)) +// let b := f_1(a) +// sstore(a, b) +// function f(x) +// { +// if iszero(x) { revert(0, 0) } +// if iszero(add(x, 1)) { revert(0, 0) } +// } +// function f_1(x_2) -> y_3 +// { f(x_2) } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/multiple_param.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/multiple_param.yul new file mode 100644 index 000000000000..f2a036459817 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/multiple_param.yul @@ -0,0 +1,24 @@ +{ + let d, e, i := f(1, 2, 3) + sstore(d, 0) + // b and x are unused + function f(a, b, c) -> x, y, z + { + y := mload(a) + z := mload(c) + } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// let d, e, i := f_1(1, 2, 3) +// sstore(d, 0) +// function f(a, c) -> y, z +// { +// y := mload(a) +// z := mload(c) +// } +// function f_1(a_2, b_3, c_4) -> x_5, y_6, z_7 +// { y_6, z_7 := f(a_2, c_4) } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/multiple_return.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/multiple_return.yul new file mode 100644 index 000000000000..51612a699b0f --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/multiple_return.yul @@ -0,0 +1,24 @@ +{ + let a, b, c := f(sload(0)) + sstore(a, 0) + // d is unused, x is unassigned + function f(d) -> x, y, z + { + y := mload(1) + z := mload(2) + } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// let a, b, c := f_1(sload(0)) +// sstore(a, 0) +// function f() -> y, z +// { +// y := mload(1) +// z := mload(2) +// } +// function f_1(d_2) -> x_3, y_4, z_5 +// { y_4, z_5 := f() } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/nested_function.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/nested_function.yul new file mode 100644 index 000000000000..d09ba44a6634 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/nested_function.yul @@ -0,0 +1,56 @@ +// Test case to see if the step applies for nested functions. The function `j` has an unused argument. +{ + sstore(f(1), 0) + sstore(h(1), 0) + + function f(a) -> x + { + x := g(1) + x := add(x, 1) + function g(b) -> y + { + b := add(b, 1) + y := mload(b) + } + } + + function h(c) -> u + { + u := j(c) + // d is unused + function j(d) -> w + { + w := 13 + w := add(w, 1) + } + } + +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// sstore(f_1(1), 0) +// sstore(h(1), 0) +// function g(b) -> y +// { +// b := add(b, 1) +// y := mload(b) +// } +// function f() -> x +// { +// x := g(1) +// x := add(x, 1) +// } +// function f_1(a_3) -> x_4 +// { x_4 := f() } +// function j() -> w +// { +// w := 13 +// w := add(13, 1) +// } +// function j_2(d_5) -> w_6 +// { w_6 := j() } +// function h(c) -> u +// { u := j_2(c) } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/nested_function_name_collision.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/nested_function_name_collision.yul new file mode 100644 index 000000000000..8ce80f140edb --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/nested_function_name_collision.yul @@ -0,0 +1,52 @@ +// Test case where the name `g` occurs at two different places. Test to see if name-collision can +// cause issues. +{ + sstore(h(1), 0) + sstore(f(1), 0) + + function f(c) -> u + { + u := g(c) + function g(d) -> w + { + w := 13 + sstore(0, w) + } + } + + function h(c) -> u + { + u := g(c) + function g(d) -> w + { + w := 13 + sstore(0, w) + } + } + +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// sstore(h(1), 0) +// sstore(f(1), 0) +// function g() -> w +// { +// w := 13 +// sstore(0, 13) +// } +// function g_1(d_3) -> w_4 +// { w_4 := g() } +// function f(c) -> u +// { u := g_1(c) } +// function g_3() -> w_5 +// { +// w_5 := 13 +// sstore(0, 13) +// } +// function g_3_2(d_4_5) -> w_5_6 +// { w_5_6 := g_3() } +// function h(c_1) -> u_2 +// { u_2 := g_3_2(c_1) } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/no_return.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/no_return.yul new file mode 100644 index 000000000000..1cf059da2979 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/no_return.yul @@ -0,0 +1,21 @@ +{ + f(1, 2) + function f(a, b) + { + sstore(a, 0) + a := add(a, 1) + } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// f_1(1, 2) +// function f(a) +// { +// sstore(a, 0) +// a := add(a, 1) +// } +// function f_1(a_2, b_3) +// { f(a_2) } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/no_unused.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/no_unused.yul new file mode 100644 index 000000000000..ebbb70536acc --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/no_unused.yul @@ -0,0 +1,16 @@ +// A test where all parameters are used +{ + sstore(f(1), 0) + function f(a) -> x { x := a } + function g(b) -> y { y := g(b) } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// sstore(f(1), 0) +// function f(a) -> x +// { x := a } +// function g(b) -> y +// { y := g(b) } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/recursion.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/recursion.yul new file mode 100644 index 000000000000..4a2c5b6ccc30 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/recursion.yul @@ -0,0 +1,21 @@ +{ + let a1, b1, c1 := f(1, 2, 3) + function f(a, b, c) -> x, y, z + { + x, y, z := f(1, 2, 3) + x := add(x, 1) + } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// let a1, b1, c1 := f_1(1, 2, 3) +// function f() -> x, y, z +// { +// x, y, z := f_1(1, 2, 3) +// x := add(x, 1) +// } +// function f_1(a_2, b_3, c_4) -> x_5, y_6, z_7 +// { x_5, y_6, z_7 := f() } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/simple.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/simple.yul new file mode 100644 index 000000000000..873eef9777ab --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/simple.yul @@ -0,0 +1,21 @@ +{ + sstore(f(1), 0) + function f(x) -> y + { + let w := mload(1) + y := mload(w) + } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// sstore(f_1(1), 0) +// function f() -> y +// { +// let w := mload(1) +// y := mload(w) +// } +// function f_1(x_2) -> y_3 +// { y_3 := f() } +// } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/smoke.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/smoke.yul new file mode 100644 index 000000000000..1e0f9eb6103d --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/smoke.yul @@ -0,0 +1,5 @@ +{} +// ---- +// step: unusedFunctionParameterPruner +// +// { } diff --git a/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/too_many_arguments.yul b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/too_many_arguments.yul new file mode 100644 index 000000000000..720489d57328 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedFunctionParameterPruner/too_many_arguments.yul @@ -0,0 +1,23 @@ +{ + let a, b := f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18) + sstore(a, b) + function f(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18) -> y, z + { + y := mload(x3) + z := mload(x7) + } +} +// ---- +// step: unusedFunctionParameterPruner +// +// { +// let a, b := f_1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18) +// sstore(a, b) +// function f(x3, x7) -> y, z +// { +// y := mload(x3) +// z := mload(x7) +// } +// function f_1(x1_2, x2_3, x3_4, x4_5, x5_6, x6_7, x7_8, x8_9, x9_10, x10_11, x11_12, x12_13, x13_14, x14_15, x15_16, x16_17, x17_18, x18_19) -> y_20, z_21 +// { y_20, z_21 := f(x3_4, x7_8) } +// } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul index b6ee0377f807..43c30137a869 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/builtins.yul @@ -4,4 +4,4 @@ // ---- // step: varNameCleaner // -// { let datasize_1 := 1 } +// { { let datasize_1 := 1 } } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul index ea4ca9ea221d..c8b2bef2429f 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_names.yul @@ -1,14 +1,16 @@ { - let f_2 function f() { let f_1 } + let f_2 let f_10 } // ---- // step: varNameCleaner // // { -// let f_1 +// { +// let f_1 +// let f_2 +// } // function f() // { let f_1 } -// let f_2 // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul index 1608dd6fb714..c0656056eec0 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_parameters.yul @@ -12,12 +12,14 @@ // step: varNameCleaner // // { -// let f_1 +// { +// let f_1 +// let f_2 +// } // function f(x) -> x_1, y // { // let y_1 := x // y := y_1 // x_1 := y // } -// let f_2 // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul index c7d86ef98776..2882459dbe93 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/function_scopes.yul @@ -6,6 +6,7 @@ // step: varNameCleaner // // { +// { } // function f() // { let x := 0 } // function g() diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul index 2d7dbeb4a0ac..325ec7d8414a 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/instructions.yul @@ -4,4 +4,4 @@ // ---- // step: varNameCleaner // -// { let mul_1 := 1 } +// { { let mul_1 := 1 } } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul index cd32d8530f20..7d7d763c5424 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/name_stripping.yul @@ -8,8 +8,10 @@ // step: varNameCleaner // // { -// let a := 1 -// let a_1 := 2 -// let a_2 := 0xdeadbeef -// let _1 := 21718 +// { +// let a := 1 +// let a_1 := 2 +// let a_2 := 0xdeadbeef +// let _1 := 21718 +// } // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul index 100e5ccc6b09..237ab8612e92 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling-inverse.yul @@ -8,8 +8,10 @@ // step: varNameCleaner // // { -// let x := 1 -// let x_1 := 2 -// let x_2 := 3 -// let x_3 := 4 +// { +// let x := 1 +// let x_1 := 2 +// let x_2 := 3 +// let x_3 := 4 +// } // } diff --git a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul index d197d4ff352a..0df1715d739b 100644 --- a/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul +++ b/test/libyul/yulOptimizerTests/varNameCleaner/reshuffling.yul @@ -7,7 +7,9 @@ // step: varNameCleaner // // { -// let x := 1 -// let x_1 := 2 -// let x_2 := 3 +// { +// let x := 1 +// let x_1 := 2 +// let x_2 := 3 +// } // } diff --git a/test/libyul/yulSyntaxTests/assign_from_stack.yul b/test/libyul/yulSyntaxTests/assign_from_stack.yul new file mode 100644 index 000000000000..44f50ab5e5f2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/assign_from_stack.yul @@ -0,0 +1,3 @@ +{ =: x:u256 } +// ---- +// ParserError 1856: (2-3): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/assignment.yul b/test/libyul/yulSyntaxTests/assignment.yul new file mode 100644 index 000000000000..263e8e2dc103 --- /dev/null +++ b/test/libyul/yulSyntaxTests/assignment.yul @@ -0,0 +1,4 @@ +{ + let x:u256 := 2:u256 + let y:u256 := x +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/assignment_of_function.yul b/test/libyul/yulSyntaxTests/assignment_of_function.yul new file mode 100644 index 000000000000..e1ddfd604bac --- /dev/null +++ b/test/libyul/yulSyntaxTests/assignment_of_function.yul @@ -0,0 +1,6 @@ +{ + function f() {} + let x := f +} +// ---- +// TypeError 6041: (35-36): Function f used without being called. diff --git a/test/libyul/yulSyntaxTests/blocks.yul b/test/libyul/yulSyntaxTests/blocks.yul new file mode 100644 index 000000000000..069dc463bc09 --- /dev/null +++ b/test/libyul/yulSyntaxTests/blocks.yul @@ -0,0 +1,9 @@ +{ + let x:u256 := 7:u256 + { + let y:u256 := 3:u256 + } + { + let z:u256 := 2:u256 + } +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/break_outside_of_for_loop.yul b/test/libyul/yulSyntaxTests/break_outside_of_for_loop.yul new file mode 100644 index 000000000000..1d0f997e61d0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/break_outside_of_for_loop.yul @@ -0,0 +1,8 @@ +{ + let x:bool + if x { break } +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 2592: (22-27): Keyword "break" needs to be inside a for-loop body. diff --git a/test/libyul/yulSyntaxTests/builtin_function_literal.yul b/test/libyul/yulSyntaxTests/builtin_function_literal.yul new file mode 100644 index 000000000000..81d2c1ed5119 --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_function_literal.yul @@ -0,0 +1,7 @@ +{ + datasize(x,1) +} +// ---- +// TypeError 7000: (4-12): Function expects 1 arguments but got 2. +// TypeError 9114: (4-12): Function expects direct literals as arguments. +// DeclarationError 8198: (13-14): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/builtin_identifier_1.yul b/test/libyul/yulSyntaxTests/builtin_identifier_1.yul new file mode 100644 index 000000000000..3fbe25c088f4 --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_identifier_1.yul @@ -0,0 +1,5 @@ +{ + let add := 6 +} +// ---- +// ParserError 5568: (7-10): Cannot use builtin function name "add" as identifier name. diff --git a/test/libyul/yulSyntaxTests/builtin_identifier_2.yul b/test/libyul/yulSyntaxTests/builtin_identifier_2.yul new file mode 100644 index 000000000000..3c9f60955b02 --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_identifier_2.yul @@ -0,0 +1,5 @@ +{ + function add() {} +} +// ---- +// ParserError 5568: (12-15): Cannot use builtin function name "add" as identifier name. diff --git a/test/libyul/yulSyntaxTests/builtin_identifier_3.yul b/test/libyul/yulSyntaxTests/builtin_identifier_3.yul new file mode 100644 index 000000000000..c7969b8ef2ad --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_identifier_3.yul @@ -0,0 +1,5 @@ +{ + function f(x) { f(add) } +} +// ---- +// ParserError 7104: (21-24): Builtin function "add" must be called. diff --git a/test/libyul/yulSyntaxTests/builtin_identifier_4.yul b/test/libyul/yulSyntaxTests/builtin_identifier_4.yul new file mode 100644 index 000000000000..4e1bcc080cb7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_identifier_4.yul @@ -0,0 +1,5 @@ +{ + function f(add) {} +} +// ---- +// ParserError 5568: (14-17): Cannot use builtin function name "add" as identifier name. diff --git a/test/libyul/yulSyntaxTests/builtin_identifier_5.yul b/test/libyul/yulSyntaxTests/builtin_identifier_5.yul new file mode 100644 index 000000000000..314cc8bbb783 --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_identifier_5.yul @@ -0,0 +1,5 @@ +{ + function f() -> add {} +} +// ---- +// ParserError 5568: (19-22): Cannot use builtin function name "add" as identifier name. diff --git a/test/libyul/yulSyntaxTests/builtin_identifier_6.yul b/test/libyul/yulSyntaxTests/builtin_identifier_6.yul new file mode 100644 index 000000000000..e261354fcc6f --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_identifier_6.yul @@ -0,0 +1,5 @@ +{ + add := 1 +} +// ---- +// ParserError 6272: (7-9): Cannot assign to builtin function "add". diff --git a/test/libyul/yulSyntaxTests/builtin_identifier_7.yul b/test/libyul/yulSyntaxTests/builtin_identifier_7.yul new file mode 100644 index 000000000000..c34da03638e1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_identifier_7.yul @@ -0,0 +1,6 @@ +{ + function f() -> a, b {} + add, mul := f() +} +// ---- +// ParserError 6272: (31-32): Cannot assign to builtin function "add". diff --git a/test/libyul/yulSyntaxTests/builtin_types.yul b/test/libyul/yulSyntaxTests/builtin_types.yul new file mode 100644 index 000000000000..ebf653b50bc2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/builtin_types.yul @@ -0,0 +1,15 @@ +{ + let x1:bool := true:bool + let x2:u8 := 1:u8 + let x3:s8 := 1:s8 + let x4:u32 := 1:u32 + let x5:s32 := 1:s32 + let x6:u64 := 1:u64 + let x7:s64 := 1:s64 + let x8:u128 := 1:u128 + let x9:s128 := 1:s128 + let x10:u256 := 1:u256 + let x11:s256 := 1:s256 +} +// ==== +// dialect: yul \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/call_literal.yul b/test/libyul/yulSyntaxTests/call_literal.yul new file mode 100644 index 000000000000..d1516f0ec578 --- /dev/null +++ b/test/libyul/yulSyntaxTests/call_literal.yul @@ -0,0 +1,5 @@ +{ + 1() +} +// ---- +// ParserError 9980: (7-8): Function name expected. diff --git a/test/libyul/yulSyntaxTests/constants.yul b/test/libyul/yulSyntaxTests/constants.yul new file mode 100644 index 000000000000..b1713aeea14c --- /dev/null +++ b/test/libyul/yulSyntaxTests/constants.yul @@ -0,0 +1,3 @@ +{ + pop(mul(7, 8)) +} diff --git a/test/libyul/yulSyntaxTests/continue_outside_of_for_loop.yul b/test/libyul/yulSyntaxTests/continue_outside_of_for_loop.yul new file mode 100644 index 000000000000..c12f7945d453 --- /dev/null +++ b/test/libyul/yulSyntaxTests/continue_outside_of_for_loop.yul @@ -0,0 +1,8 @@ +{ + let x:bool + if x { continue } +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 2592: (22-30): Keyword "continue" needs to be inside a for-loop body. diff --git a/test/libyul/yulSyntaxTests/datacopy_shadowing.yul b/test/libyul/yulSyntaxTests/datacopy_shadowing.yul new file mode 100644 index 000000000000..3f14f47226a9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/datacopy_shadowing.yul @@ -0,0 +1,5 @@ +{ + function datacopy(a, b, c) {} +} +// ---- +// ParserError 5568: (15-23): Cannot use builtin function name "datacopy" as identifier name. diff --git a/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul b/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul new file mode 100644 index 000000000000..fb32fb61c17a --- /dev/null +++ b/test/libyul/yulSyntaxTests/dataoffset_shadowing.yul @@ -0,0 +1,5 @@ +{ + function dataoffset(a) -> b {} +} +// ---- +// ParserError 5568: (15-25): Cannot use builtin function name "dataoffset" as identifier name. diff --git a/test/libyul/yulSyntaxTests/datasize_shadowing.yul b/test/libyul/yulSyntaxTests/datasize_shadowing.yul new file mode 100644 index 000000000000..379401754cac --- /dev/null +++ b/test/libyul/yulSyntaxTests/datasize_shadowing.yul @@ -0,0 +1,5 @@ +{ + function datasize(a) -> b {} +} +// ---- +// ParserError 5568: (15-23): Cannot use builtin function name "datasize" as identifier name. diff --git a/test/libyul/yulSyntaxTests/empty_call.yul b/test/libyul/yulSyntaxTests/empty_call.yul new file mode 100644 index 000000000000..30491c114dc9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/empty_call.yul @@ -0,0 +1,3 @@ +{ () } +// ---- +// ParserError 1856: (2-3): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/for_expr_invalid_1.yul b/test/libyul/yulSyntaxTests/for_expr_invalid_1.yul new file mode 100644 index 000000000000..00f0a4d58f89 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_expr_invalid_1.yul @@ -0,0 +1,5 @@ +{ + for {} {} {} {} +} +// ---- +// ParserError 1856: (10-11): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/for_expr_invalid_2.yul b/test/libyul/yulSyntaxTests/for_expr_invalid_2.yul new file mode 100644 index 000000000000..d4d801204759 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_expr_invalid_2.yul @@ -0,0 +1,5 @@ +{ + for 1 1 {} {} +} +// ---- +// ParserError 2314: (7-8): Expected '{' but got 'Number' diff --git a/test/libyul/yulSyntaxTests/for_expr_invalid_3.yul b/test/libyul/yulSyntaxTests/for_expr_invalid_3.yul new file mode 100644 index 000000000000..8f82686599b2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_expr_invalid_3.yul @@ -0,0 +1,5 @@ +{ + for {} 1 1 {} +} +// ---- +// ParserError 2314: (12-13): Expected '{' but got 'Number' diff --git a/test/libyul/yulSyntaxTests/for_expr_invalid_4.yul b/test/libyul/yulSyntaxTests/for_expr_invalid_4.yul new file mode 100644 index 000000000000..abd2260d7e1b --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_expr_invalid_4.yul @@ -0,0 +1,5 @@ +{ + for {} 1 {} 1 +} +// ---- +// ParserError 2314: (15-16): Expected '{' but got 'Number' diff --git a/test/libyul/yulSyntaxTests/for_expr_invalid_5.yul b/test/libyul/yulSyntaxTests/for_expr_invalid_5.yul new file mode 100644 index 000000000000..5d739e81ea12 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_expr_invalid_5.yul @@ -0,0 +1,5 @@ +{ + for {} mload {} {} +} +// ---- +// ParserError 7104: (10-15): Builtin function "mload" must be called. diff --git a/test/libyul/yulSyntaxTests/for_expr_invalid_6.yul b/test/libyul/yulSyntaxTests/for_expr_invalid_6.yul new file mode 100644 index 000000000000..42883052e78d --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_expr_invalid_6.yul @@ -0,0 +1,7 @@ +{ + for {} mstore(1, 1) {} {} +} +// ==== +// dialect: evm +// ---- +// TypeError 3950: (10-22): Expected expression to evaluate to one value, but got 0 values instead. diff --git a/test/libyul/yulSyntaxTests/for_statement.yul b/test/libyul/yulSyntaxTests/for_statement.yul new file mode 100644 index 000000000000..7f180ea8f5a2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement.yul @@ -0,0 +1,7 @@ +{ + let x:bool + for {let i := 0} x {i := add(i, 1)} {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_2.yul b/test/libyul/yulSyntaxTests/for_statement_2.yul new file mode 100644 index 000000000000..1c74f4239ff1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_2.yul @@ -0,0 +1,10 @@ +{ + + { for {} 1 {} {} } + { for { let i := 1 } lt(i, 5) { i := add(i, 1) } {} } + { for {} 1 {} {} } + { let x := calldatasize() for { let i := 0} lt(i, x) { i := add(i, 1) } { mstore(i, 2) } } +} +// ==== +// dialect: evm +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_break.yul b/test/libyul/yulSyntaxTests/for_statement_break.yul new file mode 100644 index 000000000000..4e432557baf9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_break.yul @@ -0,0 +1,10 @@ +{ + let x:bool + for {let i := 0} x {i := add(i, 1)} + { + break + } +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_break_init.yul b/test/libyul/yulSyntaxTests/for_statement_break_init.yul new file mode 100644 index 000000000000..7fb15faf0046 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_break_init.yul @@ -0,0 +1,8 @@ +{ + let x:bool + for {let i := 0 break} x {i := add(i, 1)} {} +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 9615: (31-36): Keyword "break" in for-loop init block is not allowed. diff --git a/test/libyul/yulSyntaxTests/for_statement_break_nested_body_in_init.yul b/test/libyul/yulSyntaxTests/for_statement_break_nested_body_in_init.yul new file mode 100644 index 000000000000..9ffe751cc0b1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_break_nested_body_in_init.yul @@ -0,0 +1,7 @@ +{ + for {let x:bool for {} x {} { break }} 1:bool {} + {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_break_nested_body_in_post.yul b/test/libyul/yulSyntaxTests/for_statement_break_nested_body_in_post.yul new file mode 100644 index 000000000000..ab012a73fa0f --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_break_nested_body_in_post.yul @@ -0,0 +1,7 @@ +{ + for {} 1:bool {let x:bool for {} x {} { break }} + {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_break_post.yul b/test/libyul/yulSyntaxTests/for_statement_break_post.yul new file mode 100644 index 000000000000..93d1475d0696 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_break_post.yul @@ -0,0 +1,8 @@ +{ + let x:bool + for {let i := 0 } x {i := add(i, 1) break} {} +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 2461: (51-56): Keyword "break" in for-loop post block is not allowed. diff --git a/test/libyul/yulSyntaxTests/for_statement_continue.yul b/test/libyul/yulSyntaxTests/for_statement_continue.yul new file mode 100644 index 000000000000..b7c2b02515af --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_continue.yul @@ -0,0 +1,10 @@ +{ + let x:bool + for {let i := 0} x {i := add(i, 1)} + { + continue + } +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_continue_fail_init.yul b/test/libyul/yulSyntaxTests/for_statement_continue_fail_init.yul new file mode 100644 index 000000000000..eca0d46928ea --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_continue_fail_init.yul @@ -0,0 +1,10 @@ +{ + let x:bool + for {let i := 0 continue} x {i := add(i, 1)} + { + } +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 9615: (31-39): Keyword "continue" in for-loop init block is not allowed. diff --git a/test/libyul/yulSyntaxTests/for_statement_continue_fail_post.yul b/test/libyul/yulSyntaxTests/for_statement_continue_fail_post.yul new file mode 100644 index 000000000000..7362b38b285a --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_continue_fail_post.yul @@ -0,0 +1,8 @@ +{ + let x:bool + for {let i := 0} x {i := add(i, 1) continue} {} +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 2461: (50-58): Keyword "continue" in for-loop post block is not allowed. diff --git a/test/libyul/yulSyntaxTests/for_statement_continue_nested_body_in_init.yul b/test/libyul/yulSyntaxTests/for_statement_continue_nested_body_in_init.yul new file mode 100644 index 000000000000..305cdd36d4dc --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_continue_nested_body_in_init.yul @@ -0,0 +1,8 @@ +{ + for {let x:bool for {} x {} { continue }} 1:bool {} + { + } +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_continue_nested_body_in_post.yul b/test/libyul/yulSyntaxTests/for_statement_continue_nested_body_in_post.yul new file mode 100644 index 000000000000..e33bd2457b04 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_continue_nested_body_in_post.yul @@ -0,0 +1,7 @@ +{ + for {} 1:bool {let x:bool for {} x {} { continue }} + {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/for_statement_continue_nested_init_in_body.yul b/test/libyul/yulSyntaxTests/for_statement_continue_nested_init_in_body.yul new file mode 100644 index 000000000000..1ff20943fbb9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_continue_nested_init_in_body.yul @@ -0,0 +1,11 @@ +{ + for {} 1 {} + { + let x:bool + for { continue } x {} {} + } +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 9615: (39-47): Keyword "continue" in for-loop init block is not allowed. diff --git a/test/libyul/yulSyntaxTests/for_statement_nested_break.yul b/test/libyul/yulSyntaxTests/for_statement_nested_break.yul new file mode 100644 index 000000000000..ae3fd9da4732 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_nested_break.yul @@ -0,0 +1,11 @@ +{ + let x:bool + for {let i := 0} x {} + { + function f() { break } + } +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 2592: (57-62): Keyword "break" needs to be inside a for-loop body. diff --git a/test/libyul/yulSyntaxTests/for_statement_nested_continue.yul b/test/libyul/yulSyntaxTests/for_statement_nested_continue.yul new file mode 100644 index 000000000000..8e7eca428e24 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_statement_nested_continue.yul @@ -0,0 +1,10 @@ +{ + for {let i := 0} iszero(eq(i, 10)) {} + { + function f() { continue } + } +} +// ==== +// dialect: evmTyped +// ---- +// SyntaxError 2592: (61-69): Keyword "continue" needs to be inside a for-loop body. diff --git a/test/libyul/yulSyntaxTests/for_visibility_1.yul b/test/libyul/yulSyntaxTests/for_visibility_1.yul new file mode 100644 index 000000000000..05d876335e31 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_1.yul @@ -0,0 +1,5 @@ +{ + for { let i := 1 } i { pop(i) } { pop(i) } +} +// ==== +// dialect: evm diff --git a/test/libyul/yulSyntaxTests/for_visibility_2.yul b/test/libyul/yulSyntaxTests/for_visibility_2.yul new file mode 100644 index 000000000000..62cc90c0c482 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_2.yul @@ -0,0 +1,7 @@ +{ + for {} i { let i := 1 } {} +} +// ==== +// dialect: evm +// ---- +// DeclarationError 8198: (10-11): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/for_visibility_3.yul b/test/libyul/yulSyntaxTests/for_visibility_3.yul new file mode 100644 index 000000000000..54af2f854ded --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_3.yul @@ -0,0 +1,7 @@ +{ + for {} 1 { let i := 1 } { pop(i) } +} +// ==== +// dialect: evm +// ---- +// DeclarationError 8198: (33-34): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/for_visibility_4.yul b/test/libyul/yulSyntaxTests/for_visibility_4.yul new file mode 100644 index 000000000000..68e81f3b4a33 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_4.yul @@ -0,0 +1,7 @@ +{ + for {} 1 { pop(i) } { let i := 1 } +} +// ==== +// dialect: evm +// ---- +// DeclarationError 8198: (18-19): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/for_visibility_5.yul b/test/libyul/yulSyntaxTests/for_visibility_5.yul new file mode 100644 index 000000000000..2785394d81c9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_5.yul @@ -0,0 +1,7 @@ +{ + for { pop(i) } 1 { let i := 1 } {} +} +// ==== +// dialect: evm +// ---- +// DeclarationError 8198: (13-14): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/for_visibility_6.yul b/test/libyul/yulSyntaxTests/for_visibility_6.yul new file mode 100644 index 000000000000..75cb6d5313e4 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_6.yul @@ -0,0 +1,7 @@ +{ + for { pop(i) } 1 { } { let i := 1 } +} +// ==== +// dialect: evm +// ---- +// DeclarationError 8198: (13-14): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/for_visibility_7.yul b/test/libyul/yulSyntaxTests/for_visibility_7.yul new file mode 100644 index 000000000000..00e289714304 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_7.yul @@ -0,0 +1,7 @@ +{ + for {} i {} { let i := 1 } +} +// ==== +// dialect: evm +// ---- +// DeclarationError 8198: (10-11): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/for_visibility_8.yul b/test/libyul/yulSyntaxTests/for_visibility_8.yul new file mode 100644 index 000000000000..68e81f3b4a33 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_8.yul @@ -0,0 +1,7 @@ +{ + for {} 1 { pop(i) } { let i := 1 } +} +// ==== +// dialect: evm +// ---- +// DeclarationError 8198: (18-19): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/for_visibility_9.yul b/test/libyul/yulSyntaxTests/for_visibility_9.yul new file mode 100644 index 000000000000..4cda1b165fd9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_9.yul @@ -0,0 +1,7 @@ +{ + for { let x := 1 } 1 { let x := 1 } {} +} +// ==== +// dialect: yul +// ---- +// DeclarationError 1395: (26-36): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/for_visibility_A.yul b/test/libyul/yulSyntaxTests/for_visibility_A.yul new file mode 100644 index 000000000000..a5761f52a919 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_A.yul @@ -0,0 +1,7 @@ +{ + for { let x := 1 } 1 {} { let x := 1 } +} +// ==== +// dialect: yul +// ---- +// DeclarationError 1395: (29-39): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/for_visibility_B.yul b/test/libyul/yulSyntaxTests/for_visibility_B.yul new file mode 100644 index 000000000000..d315a611d71a --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_B.yul @@ -0,0 +1,5 @@ +{ + let x := 1 for { let x := 1 } 1 {} {} +} +// ---- +// DeclarationError 1395: (20-30): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/for_visibility_C.yul b/test/libyul/yulSyntaxTests/for_visibility_C.yul new file mode 100644 index 000000000000..81741fe7b512 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_C.yul @@ -0,0 +1,5 @@ +{ + let x := 1 for {} 1 { let x := 1 } {} +} +// ---- +// DeclarationError 1395: (25-35): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/for_visibility_D.yul b/test/libyul/yulSyntaxTests/for_visibility_D.yul new file mode 100644 index 000000000000..409b10399bf0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_D.yul @@ -0,0 +1,5 @@ +{ + let x := 1 for {} 1 {} { let x := 1 } +} +// ---- +// DeclarationError 1395: (28-38): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/for_visibility_E.yul b/test/libyul/yulSyntaxTests/for_visibility_E.yul new file mode 100644 index 000000000000..1f09cd34aee3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/for_visibility_E.yul @@ -0,0 +1,6 @@ +{ + // Check that body and post are not sub-scopes of each other. + for {} 1 { let x := 1 } { let x := 1 } +} +// ==== +// dialect: evm diff --git a/test/libyul/yulSyntaxTests/function_calls.yul b/test/libyul/yulSyntaxTests/function_calls.yul new file mode 100644 index 000000000000..50238f798e88 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_calls.yul @@ -0,0 +1,8 @@ +{ + function f(a:u256) -> b:u256 {} + function g(a:u256, b:u256, c:u256) {} + function x() { + g(1:u256, 2:u256, f(3:u256)) + x() + } +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/function_calls_2.yul b/test/libyul/yulSyntaxTests/function_calls_2.yul new file mode 100644 index 000000000000..d50c27a72d9a --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_calls_2.yul @@ -0,0 +1,17 @@ +{ + { function f() {} } + { function f() { let y := 2 } } + { function f() -> z { let y := 2 } } + { function f(a) { let y := 2 } } + { function f(a) { let y := a } } + { function f() -> x, y, z {} } + { function f(x, y, z) {} } + { function f(a, b) -> x, y, z { y := a } } + { function f() {} f() } + { function f() -> x, y { x := 1 y := 2} let a, b := f() } + { function f(a, b) -> x, y { x := b y := a } let a, b := f(2, 3) } + { function rec(a) { rec(sub(a, 1)) } rec(2) } + { let r := 2 function f() -> x, y { x := 1 y := 2} let a, b := f() b := r } + { function f() { g() } function g() { f() } } + { function f(a) -> b {} function g(a, b, c) {} function x() { g(1, 2, f(mul(2, 3))) x() } } +} diff --git a/test/libyul/yulSyntaxTests/function_def_multiple_args.yul b/test/libyul/yulSyntaxTests/function_def_multiple_args.yul new file mode 100644 index 000000000000..5d526559f0f9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_def_multiple_args.yul @@ -0,0 +1,4 @@ +{ + function f(a, d) { } + function g(a, d) -> x, y { } +} diff --git a/test/libyul/yulSyntaxTests/function_defined_in_init_block_1.yul b/test/libyul/yulSyntaxTests/function_defined_in_init_block_1.yul new file mode 100644 index 000000000000..23970fdbf351 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_defined_in_init_block_1.yul @@ -0,0 +1,3 @@ +{ + for { } 1:bool { function f() {} } {} +} diff --git a/test/libyul/yulSyntaxTests/function_defined_in_init_block_2.yul b/test/libyul/yulSyntaxTests/function_defined_in_init_block_2.yul new file mode 100644 index 000000000000..871fd540eeb5 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_defined_in_init_block_2.yul @@ -0,0 +1,3 @@ +{ + for { } 1:bool {} { function f() {} } +} diff --git a/test/libyul/yulSyntaxTests/function_defined_in_init_block_3.yul b/test/libyul/yulSyntaxTests/function_defined_in_init_block_3.yul new file mode 100644 index 000000000000..6e616630a112 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_defined_in_init_block_3.yul @@ -0,0 +1,5 @@ +{ + for { function f() {} } 1:bool {} {} +} +// ---- +// SyntaxError 3441: (9-17): Functions cannot be defined inside a for-loop init block. diff --git a/test/libyul/yulSyntaxTests/function_defined_in_init_nested_1.yul b/test/libyul/yulSyntaxTests/function_defined_in_init_nested_1.yul new file mode 100644 index 000000000000..5486e8c610a8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_defined_in_init_nested_1.yul @@ -0,0 +1,7 @@ +{ + for { + for {} 1:bool { function f() {} } + {} + } 1:bool {} + {} +} diff --git a/test/libyul/yulSyntaxTests/function_defined_in_init_nested_2.yul b/test/libyul/yulSyntaxTests/function_defined_in_init_nested_2.yul new file mode 100644 index 000000000000..f5f3accd248f --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_defined_in_init_nested_2.yul @@ -0,0 +1,5 @@ +{ + for { for {function foo() {}} 1:bool {} {} } 1:bool {} {} +} +// ---- +// SyntaxError 3441: (14-22): Functions cannot be defined inside a for-loop init block. diff --git a/test/libyul/yulSyntaxTests/function_defined_in_init_nested_3.yul b/test/libyul/yulSyntaxTests/function_defined_in_init_nested_3.yul new file mode 100644 index 000000000000..d3a794c0afaf --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_defined_in_init_nested_3.yul @@ -0,0 +1,8 @@ +{ + for {} + 1:bool + {for {function foo() {}} 1:bool {} {} } + {} +} +// ---- +// SyntaxError 3441: (27-35): Functions cannot be defined inside a for-loop init block. diff --git a/test/libyul/yulSyntaxTests/function_definition.yul b/test/libyul/yulSyntaxTests/function_definition.yul new file mode 100644 index 000000000000..1323e7620850 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_definition.yul @@ -0,0 +1,7 @@ +{ + function f (a, b , c ) -> y,x,z { + } + + function g() { } + function h(a) -> x { } +} diff --git a/test/libyul/yulSyntaxTests/function_definition_whitespace.yul b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul new file mode 100644 index 000000000000..1c397982b9d6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_definition_whitespace.yul @@ -0,0 +1,6 @@ +{ + function f (a, b , c ) - > y,x,z { + } +} +// ---- +// ParserError 2314: (30-31): Expected '{' but got '-' diff --git a/test/libyul/yulSyntaxTests/function_definitions.yul b/test/libyul/yulSyntaxTests/function_definitions.yul new file mode 100644 index 000000000000..ad22684b5391 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_definitions.yul @@ -0,0 +1,4 @@ +{ + function f() { } + function g(a:u256) -> x:u256 { } +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/function_embedded.yul b/test/libyul/yulSyntaxTests/function_embedded.yul new file mode 100644 index 000000000000..e836da2b42af --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_embedded.yul @@ -0,0 +1,7 @@ +{ + function f(r, s) -> x { + function g(a) -> b { } + x := g(2) + } + let x := f(2, 3) +} diff --git a/test/libyul/yulSyntaxTests/function_literal.yul b/test/libyul/yulSyntaxTests/function_literal.yul new file mode 100644 index 000000000000..b9652e3541f2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_literal.yul @@ -0,0 +1,6 @@ +{ + function f(a,b) {} + f(x,1) +} +// ---- +// DeclarationError 8198: (27-28): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/function_literal_valid.yul b/test/libyul/yulSyntaxTests/function_literal_valid.yul new file mode 100644 index 000000000000..b36928ad49e2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_literal_valid.yul @@ -0,0 +1,6 @@ +{ + let x + function f(a,b) {} + f(x,1) +} +// ---- diff --git a/test/libyul/yulSyntaxTests/function_shadowing_outside_vars_1.yul b/test/libyul/yulSyntaxTests/function_shadowing_outside_vars_1.yul new file mode 100644 index 000000000000..b242f6f69f71 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_shadowing_outside_vars_1.yul @@ -0,0 +1,4 @@ +{ + { let x:u256 } + function f() -> x:u256 {} +} diff --git a/test/libyul/yulSyntaxTests/function_shadowing_outside_vars_2.yul b/test/libyul/yulSyntaxTests/function_shadowing_outside_vars_2.yul new file mode 100644 index 000000000000..f7fb87278545 --- /dev/null +++ b/test/libyul/yulSyntaxTests/function_shadowing_outside_vars_2.yul @@ -0,0 +1,6 @@ +{ + let x:u256 + function f() -> x:u256 {} +} +// ---- +// DeclarationError 1395: (15-40): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/functional.yul b/test/libyul/yulSyntaxTests/functional.yul new file mode 100644 index 000000000000..1dbbe0199914 --- /dev/null +++ b/test/libyul/yulSyntaxTests/functional.yul @@ -0,0 +1,3 @@ +{ + let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) +} diff --git a/test/libyul/yulSyntaxTests/functional_assign_complex.yul b/test/libyul/yulSyntaxTests/functional_assign_complex.yul new file mode 100644 index 000000000000..1dbbe0199914 --- /dev/null +++ b/test/libyul/yulSyntaxTests/functional_assign_complex.yul @@ -0,0 +1,3 @@ +{ + let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) +} diff --git a/test/libyul/yulSyntaxTests/functional_assignment.yul b/test/libyul/yulSyntaxTests/functional_assignment.yul new file mode 100644 index 000000000000..7d2e6624aa04 --- /dev/null +++ b/test/libyul/yulSyntaxTests/functional_assignment.yul @@ -0,0 +1,5 @@ +{ + let x := 2 + x := 7 +} + diff --git a/test/libyul/yulSyntaxTests/functional_partial.yul b/test/libyul/yulSyntaxTests/functional_partial.yul new file mode 100644 index 000000000000..ea28283d4ac6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/functional_partial.yul @@ -0,0 +1,5 @@ +{ + let x := byte +} +// ---- +// ParserError 7104: (15-19): Builtin function "byte" must be called. diff --git a/test/libyul/yulSyntaxTests/functional_partial_success.yul b/test/libyul/yulSyntaxTests/functional_partial_success.yul new file mode 100644 index 000000000000..177407aef14b --- /dev/null +++ b/test/libyul/yulSyntaxTests/functional_partial_success.yul @@ -0,0 +1,3 @@ +{ + let x := byte(1, 2) +} diff --git a/test/libyul/yulSyntaxTests/functional_returndatacopy.yul b/test/libyul/yulSyntaxTests/functional_returndatacopy.yul new file mode 100644 index 000000000000..84f65b09da6c --- /dev/null +++ b/test/libyul/yulSyntaxTests/functional_returndatacopy.yul @@ -0,0 +1,5 @@ +{ + returndatacopy(0, 32, 64) +} +// ==== +// EVMVersion: >=byzantium diff --git a/test/libyul/yulSyntaxTests/functions_in_parallel_scopes.yul b/test/libyul/yulSyntaxTests/functions_in_parallel_scopes.yul new file mode 100644 index 000000000000..05f3a28613c7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/functions_in_parallel_scopes.yul @@ -0,0 +1,4 @@ +{ + { function g() {} } + { function g() {} } +} diff --git a/test/libyul/yulSyntaxTests/functions_multiple_args.yul b/test/libyul/yulSyntaxTests/functions_multiple_args.yul new file mode 100644 index 000000000000..84468a4a931a --- /dev/null +++ b/test/libyul/yulSyntaxTests/functions_multiple_args.yul @@ -0,0 +1,4 @@ +{ + function f(a:u256, d:u256) { } + function g(a:u256, d:u256) -> x:u256, y:u256 { } +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/hex_assignment.yul b/test/libyul/yulSyntaxTests/hex_assignment.yul new file mode 100644 index 000000000000..2131639167df --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_assignment.yul @@ -0,0 +1,5 @@ +{ + let x := hex"0011" +} +// ---- +// ParserError 3772: (15-24): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/hex_expression.yul b/test/libyul/yulSyntaxTests/hex_expression.yul new file mode 100644 index 000000000000..191fdf085bf1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_expression.yul @@ -0,0 +1,5 @@ +{ + pop(hex"2233") +} +// ---- +// ParserError 3772: (10-19): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/hex_switch_case.yul b/test/libyul/yulSyntaxTests/hex_switch_case.yul new file mode 100644 index 000000000000..87ba6a4a9b62 --- /dev/null +++ b/test/libyul/yulSyntaxTests/hex_switch_case.yul @@ -0,0 +1,7 @@ +{ + switch codesize() + case hex"00" {} + case hex"1122" {} +} +// ---- +// ParserError 3772: (33-40): Hex literals are not valid in this context. diff --git a/test/libyul/yulSyntaxTests/if_statement.yul b/test/libyul/yulSyntaxTests/if_statement.yul new file mode 100644 index 000000000000..6865a1b42135 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement.yul @@ -0,0 +1,8 @@ +{ + { if 42 {} } + { if 42 { let x := 3 } } + { function f() -> x {} if f() { pop(f()) } } + { let x := 0 if eq(calldatasize(), 0) { x := 1 } mstore(0, x) } +} +// ==== +// dialect: evm diff --git a/test/libyul/yulSyntaxTests/if_statement_1.yul b/test/libyul/yulSyntaxTests/if_statement_1.yul new file mode 100644 index 000000000000..b631f8a953e0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_1.yul @@ -0,0 +1,6 @@ +{ + if true:bool {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/if_statement_2.yul b/test/libyul/yulSyntaxTests/if_statement_2.yul new file mode 100644 index 000000000000..20be9b525cb0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_2.yul @@ -0,0 +1,9 @@ +{ + if false:bool + { + let x:u256 := 3:u256 + } +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/if_statement_3.yul b/test/libyul/yulSyntaxTests/if_statement_3.yul new file mode 100644 index 000000000000..2ba20d39b916 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_3.yul @@ -0,0 +1,11 @@ +{ + function f() -> x:bool {} + + if f() + { + let b:bool := f() + } +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/if_statement_fail_1.yul b/test/libyul/yulSyntaxTests/if_statement_fail_1.yul new file mode 100644 index 000000000000..6fc409ce3863 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_fail_1.yul @@ -0,0 +1,5 @@ +{ if let x:u256 {} } +// ==== +// dialect: evmTyped +// ---- +// ParserError 1856: (5-8): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/if_statement_fail_2.yul b/test/libyul/yulSyntaxTests/if_statement_fail_2.yul new file mode 100644 index 000000000000..97ce0e9f9f2f --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_fail_2.yul @@ -0,0 +1,5 @@ +{ if true:bool let x:u256 := 3:u256 } +// ==== +// dialect: evmTyped +// ---- +// ParserError 2314: (15-18): Expected '{' but got reserved keyword 'let' diff --git a/test/libyul/yulSyntaxTests/if_statement_fail_3.yul b/test/libyul/yulSyntaxTests/if_statement_fail_3.yul new file mode 100644 index 000000000000..51e36790b6e9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_fail_3.yul @@ -0,0 +1,5 @@ +{ if 42:u256 { } } +// ==== +// dialect: evmTyped +// ---- +// TypeError 1733: (5-12): Expected a value of boolean type "bool" but got "u256" diff --git a/test/libyul/yulSyntaxTests/if_statement_invalid_1.yul b/test/libyul/yulSyntaxTests/if_statement_invalid_1.yul new file mode 100644 index 000000000000..0a4f338447b4 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_invalid_1.yul @@ -0,0 +1,5 @@ +{ + if mload {} +} +// ---- +// ParserError 7104: (9-14): Builtin function "mload" must be called. diff --git a/test/libyul/yulSyntaxTests/if_statement_invalid_2.yul b/test/libyul/yulSyntaxTests/if_statement_invalid_2.yul new file mode 100644 index 000000000000..4e7970490002 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_invalid_2.yul @@ -0,0 +1,7 @@ +{ + if mstore(1, 1) {} +} +// ==== +// dialect: evm +// ---- +// TypeError 3950: (6-18): Expected expression to evaluate to one value, but got 0 values instead. diff --git a/test/libyul/yulSyntaxTests/if_statement_invalid_3.yul b/test/libyul/yulSyntaxTests/if_statement_invalid_3.yul new file mode 100644 index 000000000000..fa396456fd1a --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_invalid_3.yul @@ -0,0 +1,7 @@ +{ + if 32 let x := 3 +} +// ==== +// dialect: yul +// ---- +// ParserError 2314: (12-15): Expected '{' but got reserved keyword 'let' diff --git a/test/libyul/yulSyntaxTests/if_statement_invalid_4.yul b/test/libyul/yulSyntaxTests/if_statement_invalid_4.yul new file mode 100644 index 000000000000..b84004a8d802 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_invalid_4.yul @@ -0,0 +1,5 @@ +{ + if calldatasize {} +} +// ---- +// ParserError 7104: (9-21): Builtin function "calldatasize" must be called. diff --git a/test/libyul/yulSyntaxTests/if_statement_scope_1.yul b/test/libyul/yulSyntaxTests/if_statement_scope_1.yul new file mode 100644 index 000000000000..3b49e4703ea7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_scope_1.yul @@ -0,0 +1,6 @@ +{ + let x := 2 + if 42 { x := 3 } +} +// ==== +// dialect: evm diff --git a/test/libyul/yulSyntaxTests/if_statement_scope_2.yul b/test/libyul/yulSyntaxTests/if_statement_scope_2.yul new file mode 100644 index 000000000000..e86578812e0b --- /dev/null +++ b/test/libyul/yulSyntaxTests/if_statement_scope_2.yul @@ -0,0 +1,8 @@ +{ + if 32 { let x := 3 } + x := 2 +} +// ==== +// dialect: evm +// ---- +// DeclarationError 4634: (25-26): Variable not found or variable not lvalue. diff --git a/test/libyul/yulSyntaxTests/instructions.yul b/test/libyul/yulSyntaxTests/instructions.yul new file mode 100644 index 000000000000..3d600812fb12 --- /dev/null +++ b/test/libyul/yulSyntaxTests/instructions.yul @@ -0,0 +1,5 @@ +{ pop } +// ==== +// dialect: yul +// ---- +// ParserError 6913: (6-7): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/instructions_too_few_args_1.yul b/test/libyul/yulSyntaxTests/instructions_too_few_args_1.yul new file mode 100644 index 000000000000..e2b0774239fa --- /dev/null +++ b/test/libyul/yulSyntaxTests/instructions_too_few_args_1.yul @@ -0,0 +1,5 @@ +{ + pop(mul()) +} +// ---- +// TypeError 7000: (7-10): Function expects 2 arguments but got 0. diff --git a/test/libyul/yulSyntaxTests/instructions_too_few_args_2.yul b/test/libyul/yulSyntaxTests/instructions_too_few_args_2.yul new file mode 100644 index 000000000000..a43a19959f4d --- /dev/null +++ b/test/libyul/yulSyntaxTests/instructions_too_few_args_2.yul @@ -0,0 +1,5 @@ +{ + pop(mul(1)) +} +// ---- +// TypeError 7000: (7-10): Function expects 2 arguments but got 1. diff --git a/test/libyul/yulSyntaxTests/instructions_too_many_args.yul b/test/libyul/yulSyntaxTests/instructions_too_many_args.yul new file mode 100644 index 000000000000..eafa3d902cf2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/instructions_too_many_args.yul @@ -0,0 +1,5 @@ +{ + pop(mul(1, 2, 3)) +} +// ---- +// TypeError 7000: (7-10): Function expects 2 arguments but got 3. diff --git a/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul new file mode 100644 index 000000000000..ffcd60c3ef4a --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/dup_disallowed.yul @@ -0,0 +1,39 @@ +{ + dup0() + dup1() + dup2() + dup3() + dup4() + dup5() + dup6() + dup7() + dup8() + dup9() + dup10() + dup11() + dup12() + dup13() + dup14() + dup15() + dup16() + dup32() +} +// ---- +// DeclarationError 4619: (6-10): Function not found. +// DeclarationError 4619: (17-21): Function not found. +// DeclarationError 4619: (28-32): Function not found. +// DeclarationError 4619: (39-43): Function not found. +// DeclarationError 4619: (50-54): Function not found. +// DeclarationError 4619: (61-65): Function not found. +// DeclarationError 4619: (72-76): Function not found. +// DeclarationError 4619: (83-87): Function not found. +// DeclarationError 4619: (94-98): Function not found. +// DeclarationError 4619: (105-109): Function not found. +// DeclarationError 4619: (116-121): Function not found. +// DeclarationError 4619: (128-133): Function not found. +// DeclarationError 4619: (140-145): Function not found. +// DeclarationError 4619: (152-157): Function not found. +// DeclarationError 4619: (164-169): Function not found. +// DeclarationError 4619: (176-181): Function not found. +// DeclarationError 4619: (188-193): Function not found. +// DeclarationError 4619: (200-205): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul b/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul new file mode 100644 index 000000000000..446c29315078 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/invalid_octal_number.yul @@ -0,0 +1,5 @@ +{ + let x := 0100 +} +// ---- +// ParserError 1465: (15-16): Illegal token: Octal numbers not allowed. diff --git a/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul new file mode 100644 index 000000000000..5ab50a4c686c --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jump_disallowed.yul @@ -0,0 +1,5 @@ +{ + jump(2) +} +// ---- +// DeclarationError 4619: (6-10): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul new file mode 100644 index 000000000000..579f34892065 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jumpdest_disallowed.yul @@ -0,0 +1,5 @@ +{ + jumpdest() +} +// ---- +// DeclarationError 4619: (6-14): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul new file mode 100644 index 000000000000..e22a86ce195c --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/jumpi_disallowed.yul @@ -0,0 +1,5 @@ +{ + jumpi(2, 1) +} +// ---- +// DeclarationError 4619: (6-11): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul new file mode 100644 index 000000000000..63762bd318b7 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/label_disallowed.yul @@ -0,0 +1,5 @@ +{ + label: +} +// ---- +// ParserError 6913: (11-12): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/invalid/leave_items_on_stack.yul b/test/libyul/yulSyntaxTests/invalid/leave_items_on_stack.yul new file mode 100644 index 000000000000..dca68b7a03cd --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/leave_items_on_stack.yul @@ -0,0 +1,5 @@ +{ + mload(0) +} +// ---- +// TypeError 3083: (6-14): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them. diff --git a/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul new file mode 100644 index 000000000000..f57faf32a1ab --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed.yul @@ -0,0 +1,5 @@ +{ + 1 +} +// ---- +// ParserError 6913: (8-9): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/pc.yul b/test/libyul/yulSyntaxTests/invalid/pc_disallowed.yul similarity index 100% rename from test/libyul/yulSyntaxTests/pc.yul rename to test/libyul/yulSyntaxTests/invalid/pc_disallowed.yul diff --git a/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul new file mode 100644 index 000000000000..c863e58c8bfc --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/push_disallowed.yul @@ -0,0 +1,69 @@ +{ + push0() + push1() + push2() + push3() + push4() + push5() + push6() + push7() + push8() + push9() + push10() + push11() + push12() + push13() + push14() + push15() + push16() + push17() + push18() + push19() + push20() + push21() + push22() + push23() + push24() + push25() + push26() + push27() + push28() + push29() + push30() + push31() + push32() +} +// ---- +// DeclarationError 4619: (6-11): Function not found. +// DeclarationError 4619: (18-23): Function not found. +// DeclarationError 4619: (30-35): Function not found. +// DeclarationError 4619: (42-47): Function not found. +// DeclarationError 4619: (54-59): Function not found. +// DeclarationError 4619: (66-71): Function not found. +// DeclarationError 4619: (78-83): Function not found. +// DeclarationError 4619: (90-95): Function not found. +// DeclarationError 4619: (102-107): Function not found. +// DeclarationError 4619: (114-119): Function not found. +// DeclarationError 4619: (126-132): Function not found. +// DeclarationError 4619: (139-145): Function not found. +// DeclarationError 4619: (152-158): Function not found. +// DeclarationError 4619: (165-171): Function not found. +// DeclarationError 4619: (178-184): Function not found. +// DeclarationError 4619: (191-197): Function not found. +// DeclarationError 4619: (204-210): Function not found. +// DeclarationError 4619: (217-223): Function not found. +// DeclarationError 4619: (230-236): Function not found. +// DeclarationError 4619: (243-249): Function not found. +// DeclarationError 4619: (256-262): Function not found. +// DeclarationError 4619: (269-275): Function not found. +// DeclarationError 4619: (282-288): Function not found. +// DeclarationError 4619: (295-301): Function not found. +// DeclarationError 4619: (308-314): Function not found. +// DeclarationError 4619: (321-327): Function not found. +// DeclarationError 4619: (334-340): Function not found. +// DeclarationError 4619: (347-353): Function not found. +// DeclarationError 4619: (360-366): Function not found. +// DeclarationError 4619: (373-379): Function not found. +// DeclarationError 4619: (386-392): Function not found. +// DeclarationError 4619: (399-405): Function not found. +// DeclarationError 4619: (412-418): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul b/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul new file mode 100644 index 000000000000..4103cab6f0b2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/swap_disallowed.yul @@ -0,0 +1,39 @@ +{ + swap0() + swap1() + swap2() + swap3() + swap4() + swap5() + swap6() + swap7() + swap8() + swap9() + swap10() + swap11() + swap12() + swap13() + swap14() + swap15() + swap16() + swap32() +} +// ---- +// DeclarationError 4619: (6-11): Function not found. +// DeclarationError 4619: (18-23): Function not found. +// DeclarationError 4619: (30-35): Function not found. +// DeclarationError 4619: (42-47): Function not found. +// DeclarationError 4619: (54-59): Function not found. +// DeclarationError 4619: (66-71): Function not found. +// DeclarationError 4619: (78-83): Function not found. +// DeclarationError 4619: (90-95): Function not found. +// DeclarationError 4619: (102-107): Function not found. +// DeclarationError 4619: (114-119): Function not found. +// DeclarationError 4619: (126-132): Function not found. +// DeclarationError 4619: (139-145): Function not found. +// DeclarationError 4619: (152-158): Function not found. +// DeclarationError 4619: (165-171): Function not found. +// DeclarationError 4619: (178-184): Function not found. +// DeclarationError 4619: (191-197): Function not found. +// DeclarationError 4619: (204-210): Function not found. +// DeclarationError 4619: (217-223): Function not found. diff --git a/test/libyul/yulSyntaxTests/invalid/unicode_comment_direction_override.sol b/test/libyul/yulSyntaxTests/invalid/unicode_comment_direction_override.sol new file mode 100644 index 000000000000..7fb5547f7d49 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/unicode_comment_direction_override.sol @@ -0,0 +1,6 @@ +{ + // pop 1 + // underflow ‬ +} +// ---- +// ParserError 1465: (19-32): Illegal token: Unicode direction override underflow in comment or string literal. diff --git a/test/libyul/yulSyntaxTests/invalid/unicode_string_direction_override.sol b/test/libyul/yulSyntaxTests/invalid/unicode_string_direction_override.sol new file mode 100644 index 000000000000..2dba69450b49 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/unicode_string_direction_override.sol @@ -0,0 +1,6 @@ +{ + // pop 1 + let s := unicode"underflow ‬"; +} +// ---- +// ParserError 1465: (35-47): Illegal token: Invalid character in string. diff --git a/test/libyul/yulSyntaxTests/invalid_tuple_assignment.yul b/test/libyul/yulSyntaxTests/invalid_tuple_assignment.yul new file mode 100644 index 000000000000..94805f0fd71c --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid_tuple_assignment.yul @@ -0,0 +1,5 @@ +{ + let x, y := 1 +} +// ---- +// DeclarationError 3812: (3-16): Variable count mismatch: 2 variables and 1 values. diff --git a/test/libyul/yulSyntaxTests/keywords.yul b/test/libyul/yulSyntaxTests/keywords.yul new file mode 100644 index 000000000000..00aec0c569ac --- /dev/null +++ b/test/libyul/yulSyntaxTests/keywords.yul @@ -0,0 +1,4 @@ +{ + return (byte(1, 2), 2) + pop(address()) // this is valid (but unreachable) code +} diff --git a/test/libyul/yulSyntaxTests/linkersymbol_bad_literal.yul b/test/libyul/yulSyntaxTests/linkersymbol_bad_literal.yul new file mode 100644 index 000000000000..15ac5dccb336 --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_bad_literal.yul @@ -0,0 +1,11 @@ +{ + pop(linkersymbol(0)) + pop(linkersymbol(true)) + pop(linkersymbol(false)) +} +// ==== +// dialect: evm +// ---- +// TypeError 5859: (23-24): Function expects string literal. +// TypeError 5859: (48-52): Function expects string literal. +// TypeError 5859: (76-81): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/linkersymbol_evmtyped.yul b/test/libyul/yulSyntaxTests/linkersymbol_evmtyped.yul index 1cadcee0c0fb..aa9cc3adbf1a 100644 --- a/test/libyul/yulSyntaxTests/linkersymbol_evmtyped.yul +++ b/test/libyul/yulSyntaxTests/linkersymbol_evmtyped.yul @@ -1,6 +1,8 @@ { let addr:u256 := linkersymbol("contract/library.sol:L") + function linkersymbol(x) {} } // ==== // dialect: evmTyped // ---- +// ParserError 5568: (75-87): Cannot use builtin function name "linkersymbol" as identifier name. diff --git a/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul b/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul new file mode 100644 index 000000000000..68ce920e1fb3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/linkersymbol_shadowing.yul @@ -0,0 +1,7 @@ +{ + function linkersymbol(a) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-27): Cannot use builtin function name "linkersymbol" as identifier name. diff --git a/test/libyul/yulSyntaxTests/literal_invalid_type.yul b/test/libyul/yulSyntaxTests/literal_invalid_type.yul new file mode 100644 index 000000000000..07a51eb887d3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/literal_invalid_type.yul @@ -0,0 +1,7 @@ +{ + let x:bool := true:unhappy +} +// ---- +// TypeError 5473: (20-32): "unhappy" is not a valid type (user defined types are not yet supported). +// TypeError 5170: (20-32): Invalid type "unhappy" for literal "true". +// TypeError 3947: (10-16): Assigning value of type "unhappy" to variable of type "bool". diff --git a/test/libyul/yulSyntaxTests/loadimmutable_bad_literal.yul b/test/libyul/yulSyntaxTests/loadimmutable_bad_literal.yul new file mode 100644 index 000000000000..2aadd2f208f0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/loadimmutable_bad_literal.yul @@ -0,0 +1,11 @@ +{ + pop(loadimmutable(0)) + pop(loadimmutable(true)) + pop(loadimmutable(false)) +} +// ==== +// dialect: evm +// ---- +// TypeError 5859: (24-25): Function expects string literal. +// TypeError 5859: (50-54): Function expects string literal. +// TypeError 5859: (79-84): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul b/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul new file mode 100644 index 000000000000..8711d0b17757 --- /dev/null +++ b/test/libyul/yulSyntaxTests/loadimmutable_shadowing.yul @@ -0,0 +1,7 @@ +{ + function loadimmutable(a) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-28): Cannot use builtin function name "loadimmutable" as identifier name. diff --git a/test/libyul/yulSyntaxTests/multiple_assignment_1.yul b/test/libyul/yulSyntaxTests/multiple_assignment_1.yul new file mode 100644 index 000000000000..b619007a55ea --- /dev/null +++ b/test/libyul/yulSyntaxTests/multiple_assignment_1.yul @@ -0,0 +1,9 @@ +{ + let x:u256 + function f() -> a:u256, b:u256 {} + 123:u256, x := f() +} +// ==== +// dialect: evmTyped +// ---- +// ParserError 2856: (58-59): Variable name must precede "," in multiple assignment. diff --git a/test/libyul/yulSyntaxTests/multiple_assignment_2.yul b/test/libyul/yulSyntaxTests/multiple_assignment_2.yul new file mode 100644 index 000000000000..852502447f08 --- /dev/null +++ b/test/libyul/yulSyntaxTests/multiple_assignment_2.yul @@ -0,0 +1,9 @@ +{ + let x:u256 + function f() -> a:u256, b:u256 {} + x, 123:u256 := f() +} +// ==== +// dialect: evmTyped +// ---- +// ParserError 2856: (62-64): Variable name must precede ":=" in assignment. diff --git a/test/libyul/yulSyntaxTests/multiple_assignment_3.yul b/test/libyul/yulSyntaxTests/multiple_assignment_3.yul new file mode 100644 index 000000000000..c7ac5eb8e5aa --- /dev/null +++ b/test/libyul/yulSyntaxTests/multiple_assignment_3.yul @@ -0,0 +1,12 @@ +{ + function f(a:u256) -> r1:u256, r2:u256 { + r1 := a + r2 := 7:u256 + } + let x:u256 := 9:u256 + let y:u256 := 2:u256 + x, y := f(x) +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/name_clash_function_var_subscope.yul b/test/libyul/yulSyntaxTests/name_clash_function_var_subscope.yul new file mode 100644 index 000000000000..f35913215c9d --- /dev/null +++ b/test/libyul/yulSyntaxTests/name_clash_function_var_subscope.yul @@ -0,0 +1,7 @@ +{ + function g() { + let g := 0 + } +} +// ---- +// DeclarationError 1395: (20-30): Variable name g already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/name_clash_function_var_subscope_reverse.yul b/test/libyul/yulSyntaxTests/name_clash_function_var_subscope_reverse.yul new file mode 100644 index 000000000000..548c72d31b1b --- /dev/null +++ b/test/libyul/yulSyntaxTests/name_clash_function_var_subscope_reverse.yul @@ -0,0 +1,6 @@ +{ + { let g := 0 } + function g() {} +} +// ---- +// DeclarationError 1395: (5-15): Variable name g already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/name_clash_sub_scope.yul b/test/libyul/yulSyntaxTests/name_clash_sub_scope.yul new file mode 100644 index 000000000000..f671d034888f --- /dev/null +++ b/test/libyul/yulSyntaxTests/name_clash_sub_scope.yul @@ -0,0 +1,7 @@ +{ + function g() { + function g() {} + } +} +// ---- +// DeclarationError 6052: (20-35): Function name g already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/name_clash_sub_scope_reverse.yul b/test/libyul/yulSyntaxTests/name_clash_sub_scope_reverse.yul new file mode 100644 index 000000000000..3fd2015a451b --- /dev/null +++ b/test/libyul/yulSyntaxTests/name_clash_sub_scope_reverse.yul @@ -0,0 +1,8 @@ +{ + { + function g() {} + } + function g() {} +} +// ---- +// DeclarationError 6052: (7-22): Function name g already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/name_clashes.yul b/test/libyul/yulSyntaxTests/name_clashes.yul new file mode 100644 index 000000000000..19c5c9549881 --- /dev/null +++ b/test/libyul/yulSyntaxTests/name_clashes.yul @@ -0,0 +1,6 @@ +{ + let g := 2 + function g() { } +} +// ---- +// DeclarationError 1395: (6-16): Variable name g already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/number_literal_1.yul b/test/libyul/yulSyntaxTests/number_literal_1.yul new file mode 100644 index 000000000000..456b9b9bd5d1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literal_1.yul @@ -0,0 +1 @@ +{ let x:u256 := 1:u256 } \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/number_literal_2.yul b/test/libyul/yulSyntaxTests/number_literal_2.yul new file mode 100644 index 000000000000..94c5929b5381 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literal_2.yul @@ -0,0 +1,3 @@ +{ let x:u256 := .1:u256 } +// ---- +// ParserError 4828: (16-18): Invalid number literal. diff --git a/test/libyul/yulSyntaxTests/number_literal_3.yul b/test/libyul/yulSyntaxTests/number_literal_3.yul new file mode 100644 index 000000000000..4dbb6879a9e5 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literal_3.yul @@ -0,0 +1,3 @@ +{ let x:u256 := 1e5:u256 } +// ---- +// ParserError 4828: (16-19): Invalid number literal. diff --git a/test/libyul/yulSyntaxTests/number_literal_4.yul b/test/libyul/yulSyntaxTests/number_literal_4.yul new file mode 100644 index 000000000000..7de784e88c20 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literal_4.yul @@ -0,0 +1,3 @@ +{ let x:u256 := 67.235:u256 } +// ---- +// ParserError 4828: (16-22): Invalid number literal. diff --git a/test/libyul/yulSyntaxTests/number_literal_5.yul b/test/libyul/yulSyntaxTests/number_literal_5.yul new file mode 100644 index 000000000000..df41e69eca0c --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literal_5.yul @@ -0,0 +1,3 @@ +{ let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 } +// ---- +// TypeError 6708: (16-88): Number literal too large (> 256 bits) diff --git a/test/libyul/yulSyntaxTests/number_literals_1.yul b/test/libyul/yulSyntaxTests/number_literals_1.yul new file mode 100644 index 000000000000..edfcc41b4e26 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literals_1.yul @@ -0,0 +1,3 @@ +{ + let x := 1 +} diff --git a/test/libyul/yulSyntaxTests/number_literals_2.yul b/test/libyul/yulSyntaxTests/number_literals_2.yul new file mode 100644 index 000000000000..7b054f0d3fe6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literals_2.yul @@ -0,0 +1,5 @@ +{ + let x := .1 +} +// ---- +// ParserError 4828: (12-14): Invalid number literal. diff --git a/test/libyul/yulSyntaxTests/number_literals_3.yul b/test/libyul/yulSyntaxTests/number_literals_3.yul new file mode 100644 index 000000000000..acdffda51368 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literals_3.yul @@ -0,0 +1,5 @@ +{ + let x := 1e5 +} +// ---- +// ParserError 4828: (12-15): Invalid number literal. diff --git a/test/libyul/yulSyntaxTests/number_literals_4.yul b/test/libyul/yulSyntaxTests/number_literals_4.yul new file mode 100644 index 000000000000..58b25bf57229 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literals_4.yul @@ -0,0 +1,5 @@ +{ + let x := 67.235 +} +// ---- +// ParserError 4828: (12-18): Invalid number literal. diff --git a/test/libyul/yulSyntaxTests/number_literals_5.yul b/test/libyul/yulSyntaxTests/number_literals_5.yul new file mode 100644 index 000000000000..5fd87743db09 --- /dev/null +++ b/test/libyul/yulSyntaxTests/number_literals_5.yul @@ -0,0 +1,5 @@ +{ + let x := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +} +// ---- +// TypeError 6708: (12-79): Number literal too large (> 256 bits) diff --git a/test/libyul/yulSyntaxTests/objects/basic_subobject.yul b/test/libyul/yulSyntaxTests/objects/basic_subobject.yul new file mode 100644 index 000000000000..031198868701 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/basic_subobject.yul @@ -0,0 +1,7 @@ +object "A" { + code {} + + object "B" { + code {} + } +} diff --git a/test/libyul/yulSyntaxTests/objects/code.yul b/test/libyul/yulSyntaxTests/objects/code.yul new file mode 100644 index 000000000000..89caa8e505d2 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/code.yul @@ -0,0 +1,18 @@ +object "A" { + code { + function f() -> offset, len { + offset := dataoffset("A") + len := datasize("A") + } + + let offset, len := f() + codecopy(0, offset, len) + } + + data "hello" "hello world text" + object "hello" { + code {} + } +} +// ---- +// ParserError 8794: (226-233): Object name "hello" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/code_without_object.yul b/test/libyul/yulSyntaxTests/objects/code_without_object.yul new file mode 100644 index 000000000000..e67c5c36d48a --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/code_without_object.yul @@ -0,0 +1,4 @@ +code { +} +// ---- +// ParserError 4294: (0-4): Expected keyword "object". diff --git a/test/libyul/yulSyntaxTests/objects/complex_subobject.yul b/test/libyul/yulSyntaxTests/objects/complex_subobject.yul new file mode 100644 index 000000000000..de48bc549483 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/complex_subobject.yul @@ -0,0 +1,35 @@ +object "A" { + code {} + data "1" hex"001122" + + object "B" { + code {} + data "2" "" + + object "B_C" { + code {} + + object "B_C_1" { + code {} + } + object "B_C_2" { + code {} + } + } + } + + object "C" { + code {} + } + + object "D" { + code {} + + object "D_1" { + code {} + } + object "D_2" { + code {} + } + } +} diff --git a/test/libyul/yulSyntaxTests/objects/conflict_data_data.yul b/test/libyul/yulSyntaxTests/objects/conflict_data_data.yul new file mode 100644 index 000000000000..e09bd8b37745 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_data_data.yul @@ -0,0 +1,8 @@ +object "A" { + code {} + + data "B" "" + data "B" hex"00" +} +// ---- +// ParserError 8794: (45-48): Object name "B" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_data_parent.yul b/test/libyul/yulSyntaxTests/objects/conflict_data_parent.yul new file mode 100644 index 000000000000..5edb1bd3a902 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_data_parent.yul @@ -0,0 +1,9 @@ +object "A" { + code {} + + object "A" { + code {} + } +} +// ---- +// ParserError 8311: (33-36): Object name cannot be the same as the name of the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_object_data.yul b/test/libyul/yulSyntaxTests/objects/conflict_object_data.yul new file mode 100644 index 000000000000..fd64093ad7e8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_object_data.yul @@ -0,0 +1,10 @@ +object "A" { + code {} + + data "B" "" + object "B" { + code {} + } +} +// ---- +// ParserError 8794: (47-50): Object name "B" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_object_object.yul b/test/libyul/yulSyntaxTests/objects/conflict_object_object.yul new file mode 100644 index 000000000000..1b83be4f75c0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_object_object.yul @@ -0,0 +1,12 @@ +object "A" { + code {} + + object "B" { + code {} + } + object "B" { + code {} + } +} +// ---- +// ParserError 8794: (64-67): Object name "B" already exists inside the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/conflict_object_parent.yul b/test/libyul/yulSyntaxTests/objects/conflict_object_parent.yul new file mode 100644 index 000000000000..44a3d3a2d17a --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/conflict_object_parent.yul @@ -0,0 +1,7 @@ +object "A" { + code {} + + data "A" "" +} +// ---- +// ParserError 8311: (31-34): Object name cannot be the same as the name of the containing object. diff --git a/test/libyul/yulSyntaxTests/objects/data.yul b/test/libyul/yulSyntaxTests/objects/data.yul new file mode 100644 index 000000000000..47d7b59c7f0a --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data.yul @@ -0,0 +1,9 @@ +object "A" { + code { + datacopy(0, dataoffset("3"), datasize("3")) + } + data "1" "" + data "2" hex"0011" + data "3" "hello world this is longer than 32 bytes and should still work" +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/data_access.yul b/test/libyul/yulSyntaxTests/objects/data_access.yul new file mode 100644 index 000000000000..e9455fafab33 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_access.yul @@ -0,0 +1,9 @@ +object "A" { + code { + pop(dataoffset("B")) + pop(datasize("B")) + } + + data "B" hex"00" +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/data_first.yul b/test/libyul/yulSyntaxTests/objects/data_first.yul new file mode 100644 index 000000000000..018674765248 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_first.yul @@ -0,0 +1,6 @@ +object "A" { + data "B" "" + code {} +} +// ---- +// ParserError 4846: (15-19): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/data_hex_name.yul b/test/libyul/yulSyntaxTests/objects/data_hex_name.yul new file mode 100644 index 000000000000..28f7282d701a --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_hex_name.yul @@ -0,0 +1,6 @@ +object "A" { + code {} + data hex"11" "" +} +// ---- +// ParserError 2314: (30-37): Expected 'StringLiteral' but got 'HexStringLiteral' diff --git a/test/libyul/yulSyntaxTests/objects/data_invalid_hex1.yul b/test/libyul/yulSyntaxTests/objects/data_invalid_hex1.yul new file mode 100644 index 000000000000..c80f82c202da --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_invalid_hex1.yul @@ -0,0 +1,8 @@ +object "A" { + code { + } + data "1" hex"0" + data "1" hex"wronghexencoding" +} +// ---- +// ParserError 2314: (37-41): Expected 'StringLiteral' but got 'ILLEGAL' diff --git a/test/libyul/yulSyntaxTests/objects/data_invalid_hex2.yul b/test/libyul/yulSyntaxTests/objects/data_invalid_hex2.yul new file mode 100644 index 000000000000..17029e4f77d6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/data_invalid_hex2.yul @@ -0,0 +1,8 @@ +object "A" { + code { + } + data "1" hex"wronghexencoding" + data "2" hex"0" +} +// ---- +// ParserError 2314: (37-41): Expected 'StringLiteral' but got 'ILLEGAL' diff --git a/test/libyul/yulSyntaxTests/objects/datacopy.yul b/test/libyul/yulSyntaxTests/objects/datacopy.yul new file mode 100644 index 000000000000..6e36025fcbd1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datacopy.yul @@ -0,0 +1,9 @@ +{ + datacopy(0, 1, 2) + + // datacopy also accepts pretty much anything which can be turned into a number + let x := 0 + let s := "" + datacopy(x, "11", s) +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/dataoffset_nonliteral.yul b/test/libyul/yulSyntaxTests/objects/dataoffset_nonliteral.yul new file mode 100644 index 000000000000..d53e26281eae --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/dataoffset_nonliteral.yul @@ -0,0 +1,10 @@ +object "A" { + code { + let x := "B" + pop(dataoffset(x)) + } + + data "B" hex"00" +} +// ---- +// TypeError 9114: (47-57): Function expects direct literals as arguments. diff --git a/test/libyul/yulSyntaxTests/objects/dataoffset_nonstring.yul b/test/libyul/yulSyntaxTests/objects/dataoffset_nonstring.yul new file mode 100644 index 000000000000..93a981d924e0 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/dataoffset_nonstring.yul @@ -0,0 +1,7 @@ +object "A" { + code { + pop(dataoffset(0)) + } +} +// ---- +// TypeError 5859: (41-42): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/objects/dataoffset_notfound.yul b/test/libyul/yulSyntaxTests/objects/dataoffset_notfound.yul new file mode 100644 index 000000000000..14df40201418 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/dataoffset_notfound.yul @@ -0,0 +1,8 @@ +object "A" { + code { + pop(dataoffset("C")) + } + data "B" "" +} +// ---- +// TypeError 3517: (41-44): Unknown data object "C". diff --git a/test/libyul/yulSyntaxTests/objects/datasize_nonliteral.yul b/test/libyul/yulSyntaxTests/objects/datasize_nonliteral.yul new file mode 100644 index 000000000000..837408b9a00b --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datasize_nonliteral.yul @@ -0,0 +1,10 @@ +object "A" { + code { + let x := "B" + pop(datasize(x)) + } + + data "B" hex"00" +} +// ---- +// TypeError 9114: (47-55): Function expects direct literals as arguments. diff --git a/test/libyul/yulSyntaxTests/objects/datasize_nonstring.yul b/test/libyul/yulSyntaxTests/objects/datasize_nonstring.yul new file mode 100644 index 000000000000..19042a2df738 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datasize_nonstring.yul @@ -0,0 +1,7 @@ +object "A" { + code { + pop(datasize(0)) + } +} +// ---- +// TypeError 5859: (39-40): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/objects/datasize_notfound.yul b/test/libyul/yulSyntaxTests/objects/datasize_notfound.yul new file mode 100644 index 000000000000..48a2c268e205 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/datasize_notfound.yul @@ -0,0 +1,8 @@ +object "A" { + code { + pop(datasize("C")) + } + data "B" "" +} +// ---- +// TypeError 3517: (39-42): Unknown data object "C". diff --git a/test/libyul/yulSyntaxTests/objects/empty_code.yul b/test/libyul/yulSyntaxTests/objects/empty_code.yul new file mode 100644 index 000000000000..a32efaaffe53 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_code.yul @@ -0,0 +1,3 @@ +object "A" { + code { } +} diff --git a/test/libyul/yulSyntaxTests/objects/empty_data.yul b/test/libyul/yulSyntaxTests/objects/empty_data.yul new file mode 100644 index 000000000000..b2456a7aeef3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_data.yul @@ -0,0 +1,5 @@ +object "A" { + data "tmp" "" +} +// ---- +// ParserError 4846: (15-19): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/empty_object.yul b/test/libyul/yulSyntaxTests/objects/empty_object.yul new file mode 100644 index 000000000000..cc9019e55ba9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_object.yul @@ -0,0 +1,4 @@ +object "A" { +} +// ---- +// ParserError 4846: (13-14): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/empty_object_name.yul b/test/libyul/yulSyntaxTests/objects/empty_object_name.yul new file mode 100644 index 000000000000..653bf4a0ffae --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/empty_object_name.yul @@ -0,0 +1,5 @@ +object "" { +} +// ---- +// ParserError 3287: (7-9): Object name cannot be empty. +// ParserError 4846: (12-13): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/incomplete1.yul b/test/libyul/yulSyntaxTests/objects/incomplete1.yul new file mode 100644 index 000000000000..6698f45a2dfa --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/incomplete1.yul @@ -0,0 +1,3 @@ +object { +// ---- +// ParserError 2314: (7-8): Expected 'StringLiteral' but got '{' diff --git a/test/libyul/yulSyntaxTests/objects/incomplete2.yul b/test/libyul/yulSyntaxTests/objects/incomplete2.yul new file mode 100644 index 000000000000..f421074f5a25 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/incomplete2.yul @@ -0,0 +1,7 @@ +object "A" { + code {} +} + +object +// ---- +// ParserError 2314: (26-32): Expected end of source but got identifier diff --git a/test/libyul/yulSyntaxTests/objects/multiple_code.yul b/test/libyul/yulSyntaxTests/objects/multiple_code.yul new file mode 100644 index 000000000000..fd3347600389 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/multiple_code.yul @@ -0,0 +1,6 @@ +object "A" { + code { } + code { } +} +// ---- +// ParserError 8143: (26-30): Expected keyword "data" or "object" or "}". diff --git a/test/libyul/yulSyntaxTests/objects/multiple_data.yul b/test/libyul/yulSyntaxTests/objects/multiple_data.yul new file mode 100644 index 000000000000..5a48edd914fe --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/multiple_data.yul @@ -0,0 +1,6 @@ +object "A" { + code { } + data "one" "" + data "two" "" +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/multiple_root_object.yul b/test/libyul/yulSyntaxTests/objects/multiple_root_object.yul new file mode 100644 index 000000000000..842cfcfb5cd1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/multiple_root_object.yul @@ -0,0 +1,8 @@ +object "A" { + code { } +} +object "B" { + code { } +} +// ---- +// ParserError 2314: (26-32): Expected end of source but got identifier diff --git a/test/libyul/yulSyntaxTests/objects/nested_object.yul b/test/libyul/yulSyntaxTests/objects/nested_object.yul new file mode 100644 index 000000000000..426cc5586085 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/nested_object.yul @@ -0,0 +1,10 @@ +object "outer" { + code { let x := mload(0) } + data "x" "stringdata" + object "inner" { + code { mstore(0, 1) } + object "inner inner" { code {} } + data "innerx" "abc" + data "innery" "def" + } +} diff --git a/test/libyul/yulSyntaxTests/objects/object_hex_name.yul b/test/libyul/yulSyntaxTests/objects/object_hex_name.yul new file mode 100644 index 000000000000..c58b5fd191ed --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/object_hex_name.yul @@ -0,0 +1,5 @@ +object hex"11" { + code {} +} +// ---- +// ParserError 2314: (7-14): Expected 'StringLiteral' but got 'HexStringLiteral' diff --git a/test/libyul/yulSyntaxTests/objects/subobject_access.yul b/test/libyul/yulSyntaxTests/objects/subobject_access.yul new file mode 100644 index 000000000000..59426a0aa631 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/subobject_access.yul @@ -0,0 +1,11 @@ +object "A" { + code { + pop(dataoffset("B")) + pop(datasize("B")) + } + + object "B" { + code {} + } +} +// ---- diff --git a/test/libyul/yulSyntaxTests/objects/subobject_first.yul b/test/libyul/yulSyntaxTests/objects/subobject_first.yul new file mode 100644 index 000000000000..369e7180e745 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/subobject_first.yul @@ -0,0 +1,9 @@ +object "A" { + object "B" { + code {} + } + + code {} +} +// ---- +// ParserError 4846: (15-21): Expected keyword "code". diff --git a/test/libyul/yulSyntaxTests/objects/subobject_hex_name.yul b/test/libyul/yulSyntaxTests/objects/subobject_hex_name.yul new file mode 100644 index 000000000000..af0581067872 --- /dev/null +++ b/test/libyul/yulSyntaxTests/objects/subobject_hex_name.yul @@ -0,0 +1,8 @@ +object "A" { + code {} + object hex"11" { + code {} + } +} +// ---- +// ParserError 2314: (32-39): Expected 'StringLiteral' but got 'HexStringLiteral' diff --git a/test/libyul/yulSyntaxTests/opcode_for_function_args_1.yul b/test/libyul/yulSyntaxTests/opcode_for_function_args_1.yul new file mode 100644 index 000000000000..00c357bb44aa --- /dev/null +++ b/test/libyul/yulSyntaxTests/opcode_for_function_args_1.yul @@ -0,0 +1,5 @@ +{ + function f(gas) {} +} +// ---- +// ParserError 5568: (14-17): Cannot use builtin function name "gas" as identifier name. diff --git a/test/libyul/yulSyntaxTests/opcode_for_function_args_2.yul b/test/libyul/yulSyntaxTests/opcode_for_function_args_2.yul new file mode 100644 index 000000000000..989cfce9e007 --- /dev/null +++ b/test/libyul/yulSyntaxTests/opcode_for_function_args_2.yul @@ -0,0 +1,5 @@ +{ + function f() -> gas {} +} +// ---- +// ParserError 5568: (19-22): Cannot use builtin function name "gas" as identifier name. diff --git a/test/libyul/yulSyntaxTests/opcode_for_functions.yul b/test/libyul/yulSyntaxTests/opcode_for_functions.yul new file mode 100644 index 000000000000..0efc7cbccedd --- /dev/null +++ b/test/libyul/yulSyntaxTests/opcode_for_functions.yul @@ -0,0 +1,5 @@ +{ + function gas() {} +} +// ---- +// ParserError 5568: (12-15): Cannot use builtin function name "gas" as identifier name. diff --git a/test/libyul/yulSyntaxTests/optional_types.yul b/test/libyul/yulSyntaxTests/optional_types.yul new file mode 100644 index 000000000000..c3433485b034 --- /dev/null +++ b/test/libyul/yulSyntaxTests/optional_types.yul @@ -0,0 +1,8 @@ +{ + let x := 1:u256 + let y:u256 := 1 + function f(a) {} + function g(a:u256) -> b {} +} +// ==== +// dialect: yul \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/passing_builtin_with_literal_argument_into_literal_argument.yul b/test/libyul/yulSyntaxTests/passing_builtin_with_literal_argument_into_literal_argument.yul index 4884fc265031..433d36683dfe 100644 --- a/test/libyul/yulSyntaxTests/passing_builtin_with_literal_argument_into_literal_argument.yul +++ b/test/libyul/yulSyntaxTests/passing_builtin_with_literal_argument_into_literal_argument.yul @@ -1,5 +1,5 @@ { - setimmutable(loadimmutable("abc"), "abc") + setimmutable(0, loadimmutable("abc"), "abc") } // ==== // dialect: evm diff --git a/test/libyul/yulSyntaxTests/period_in_identifier.yul b/test/libyul/yulSyntaxTests/period_in_identifier.yul new file mode 100644 index 000000000000..1827e6ac237e --- /dev/null +++ b/test/libyul/yulSyntaxTests/period_in_identifier.yul @@ -0,0 +1 @@ +{ let x.y:u256 := 2:u256 } \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/period_in_identifier_spaced_1.yul b/test/libyul/yulSyntaxTests/period_in_identifier_spaced_1.yul new file mode 100644 index 000000000000..dc889a2fbc25 --- /dev/null +++ b/test/libyul/yulSyntaxTests/period_in_identifier_spaced_1.yul @@ -0,0 +1,3 @@ +{ let x. y:u256 } +// ---- +// ParserError 6913: (10-11): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/period_in_identifier_spaced_2.yul b/test/libyul/yulSyntaxTests/period_in_identifier_spaced_2.yul new file mode 100644 index 000000000000..5602cbcebc49 --- /dev/null +++ b/test/libyul/yulSyntaxTests/period_in_identifier_spaced_2.yul @@ -0,0 +1,3 @@ +{ let x .y:u256 } +// ---- +// ParserError 1856: (8-9): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/period_in_identifier_spaced_3.yul b/test/libyul/yulSyntaxTests/period_in_identifier_spaced_3.yul new file mode 100644 index 000000000000..003341b339c8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/period_in_identifier_spaced_3.yul @@ -0,0 +1,3 @@ +{ let x . y:u256 } +// ---- +// ParserError 1856: (8-9): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/period_in_identifier_start.yul b/test/libyul/yulSyntaxTests/period_in_identifier_start.yul new file mode 100644 index 000000000000..1c19455d6e08 --- /dev/null +++ b/test/libyul/yulSyntaxTests/period_in_identifier_start.yul @@ -0,0 +1,4 @@ +{ + x.y(2:u256) + function x.y(a:u256) {} +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/period_in_identifier_start_with_comment.yul b/test/libyul/yulSyntaxTests/period_in_identifier_start_with_comment.yul new file mode 100644 index 000000000000..ed47b09b720c --- /dev/null +++ b/test/libyul/yulSyntaxTests/period_in_identifier_start_with_comment.yul @@ -0,0 +1,2 @@ +/// comment +{ x.y(2:u256) function x.y(a:u256) {} } \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/period_not_as_identifier_start.yul b/test/libyul/yulSyntaxTests/period_not_as_identifier_start.yul new file mode 100644 index 000000000000..0002b76fdb8b --- /dev/null +++ b/test/libyul/yulSyntaxTests/period_not_as_identifier_start.yul @@ -0,0 +1,3 @@ +{ let .y:u256 } +// ---- +// ParserError 2314: (6-7): Expected identifier but got '.' diff --git a/test/libyul/yulSyntaxTests/push.yul b/test/libyul/yulSyntaxTests/push.yul new file mode 100644 index 000000000000..f3dab144d54e --- /dev/null +++ b/test/libyul/yulSyntaxTests/push.yul @@ -0,0 +1,3 @@ +{ 0x42:u256 } +// ---- +// ParserError 6913: (12-13): Call or assignment expected. diff --git a/test/libyul/yulSyntaxTests/recursion_depth.yul b/test/libyul/yulSyntaxTests/recursion_depth.yul new file mode 100644 index 000000000000..9c65013912e5 --- /dev/null +++ b/test/libyul/yulSyntaxTests/recursion_depth.yul @@ -0,0 +1,7 @@ +{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ +let x:u256 := 0:u256 +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +// ==== +// dialect: evmTyped +// ---- +// ParserError 7319: (600-601): Maximum recursion depth reached during parsing. diff --git a/test/libyul/yulSyntaxTests/selfdestruct.yul b/test/libyul/yulSyntaxTests/selfdestruct.yul new file mode 100644 index 000000000000..deb6d1dae390 --- /dev/null +++ b/test/libyul/yulSyntaxTests/selfdestruct.yul @@ -0,0 +1,3 @@ +{ + selfdestruct(0x02) +} diff --git a/test/libyul/yulSyntaxTests/setimmutable.yul b/test/libyul/yulSyntaxTests/setimmutable.yul index 3c37c90ff201..e1d6a74919c9 100644 --- a/test/libyul/yulSyntaxTests/setimmutable.yul +++ b/test/libyul/yulSyntaxTests/setimmutable.yul @@ -1,5 +1,5 @@ { - setimmutable("address", 0x1234567890123456789012345678901234567890) + setimmutable(0, "address", 0x1234567890123456789012345678901234567890) } // ==== // dialect: evm diff --git a/test/libyul/yulSyntaxTests/setimmutable_bad_literal.yul b/test/libyul/yulSyntaxTests/setimmutable_bad_literal.yul new file mode 100644 index 000000000000..9e4bc8032c78 --- /dev/null +++ b/test/libyul/yulSyntaxTests/setimmutable_bad_literal.yul @@ -0,0 +1,11 @@ +{ + setimmutable(0, 0, 0x1234567890123456789012345678901234567890) + setimmutable(0, true, 0x1234567890123456789012345678901234567890) + setimmutable(0, false, 0x1234567890123456789012345678901234567890) +} +// ==== +// dialect: evm +// ---- +// TypeError 5859: (22-23): Function expects string literal. +// TypeError 5859: (89-93): Function expects string literal. +// TypeError 5859: (159-164): Function expects string literal. diff --git a/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul b/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul new file mode 100644 index 000000000000..b76a5de9b729 --- /dev/null +++ b/test/libyul/yulSyntaxTests/setimmutable_shadowing.yul @@ -0,0 +1,7 @@ +{ + function setimmutable(a, b) {} +} +// ==== +// dialect: evm +// ---- +// ParserError 5568: (15-27): Cannot use builtin function name "setimmutable" as identifier name. diff --git a/test/libyul/yulSyntaxTests/simple_instructions.yul b/test/libyul/yulSyntaxTests/simple_instructions.yul new file mode 100644 index 000000000000..d3f9f023b978 --- /dev/null +++ b/test/libyul/yulSyntaxTests/simple_instructions.yul @@ -0,0 +1,3 @@ +{ + let y := mul(0x10, mul(0x20, mload(0x40))) +} diff --git a/test/libyul/yulSyntaxTests/smoke.yul b/test/libyul/yulSyntaxTests/smoke.yul new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/test/libyul/yulSyntaxTests/smoke.yul @@ -0,0 +1 @@ +{} diff --git a/test/libyul/yulSyntaxTests/smoke_test.yul b/test/libyul/yulSyntaxTests/smoke_test.yul new file mode 100644 index 000000000000..f87fddff52bc --- /dev/null +++ b/test/libyul/yulSyntaxTests/smoke_test.yul @@ -0,0 +1 @@ +{ { } } diff --git a/test/libyul/yulSyntaxTests/solidity_keywords.yul b/test/libyul/yulSyntaxTests/solidity_keywords.yul new file mode 100644 index 000000000000..1f1dafc53aae --- /dev/null +++ b/test/libyul/yulSyntaxTests/solidity_keywords.yul @@ -0,0 +1,109 @@ +{ + // These are keywords of Solidity -- a copy from liblangutil/Token.h. + let abstract := 1 + let anonymous := 1 + let as := 1 + let assembly := 1 + // break is Yul keyword + let catch := 1 + let constant := 1 + let constructor := 1 + // continue is Yul keyword + let contract := 1 + let do := 1 + let else := 1 + let enum := 1 + let emit := 1 + let event := 1 + let external := 1 + let fallback := 1 + // for is a Yul keyword + // function is a Yul keyword + // hex is a Yul keyword + // if is a Yul keyword + let indexed := 1 + let interface := 1 + let internal := 1 + let immutable := 1 + let import := 1 + let is := 1 + let library := 1 + let mapping := 1 + let memory := 1 + let modifier := 1 + let new := 1 + let override := 1 + let payable := 1 + let public := 1 + let pragma := 1 + let private := 1 + let pure := 1 + let receive := 1 + // return is a builtin in EVMDialect + return(0, 0) + let returns := 1 + let storage := 1 + let calldata := 1 + let struct := 1 + let throw := 1 + let try := 1 + let type := 1 + let unicode := 1 + let using := 1 + let view := 1 + let virtual := 1 + let while := 1 + let wei := 1 + let gwei := 1 + let ether := 1 + let seconds := 1 + let minutes := 1 + let hours := 1 + let days := 1 + let weeks := 1 + let years := 1 + let int := 1 + let uint := 1 + let bytes := 1 + // byte is a builtin in EVMDialect + pop(byte(1, 1)) + let string := 1 + // address is a builtin in EVMDialect + pop(address()) + let bool := 1 + let fixed := 1 + let ufixed := 1 + let after := 1 + let alias := 1 + let apply := 1 + let auto := 1 + // case is a Yul keyword + let copyof := 1 + // default is a Yul keyword + let define := 1 + let final := 1 + let implements := 1 + let in := 1 + let inline := 1 + // let is a Yul keyword + let macro := 1 + let match := 1 + let mutable := 1 + let null := 1 + let of := 1 + let partial := 1 + let promise := 1 + let reference := 1 + let relocatable := 1 + let sealed := 1 + let sizeof := 1 + let static := 1 + let supports := 1 + // switch is a Yul keyword + let typedef := 1 + let typeof := 1 + let unchecked := 1 + let var := 1 +} +// ==== +// dialect: evm diff --git a/test/libyul/yulSyntaxTests/string_literal_switch_case.yul b/test/libyul/yulSyntaxTests/string_literal_switch_case.yul new file mode 100644 index 000000000000..02d1045c3e53 --- /dev/null +++ b/test/libyul/yulSyntaxTests/string_literal_switch_case.yul @@ -0,0 +1,5 @@ +{ + switch codesize() + case "1" {} + case "2" {} +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/string_literal_too_long_immutable.yul b/test/libyul/yulSyntaxTests/string_literal_too_long_immutable.yul index 37ce835df403..98707451e600 100644 --- a/test/libyul/yulSyntaxTests/string_literal_too_long_immutable.yul +++ b/test/libyul/yulSyntaxTests/string_literal_too_long_immutable.yul @@ -1,5 +1,6 @@ { setimmutable( + 0, "long___name___that___definitely___exceeds___the___thirty___two___byte___limit", 0x1234567890123456789012345678901234567890 ) diff --git a/test/libyul/yulSyntaxTests/surplus_input.yul b/test/libyul/yulSyntaxTests/surplus_input.yul new file mode 100644 index 000000000000..3db8feec986f --- /dev/null +++ b/test/libyul/yulSyntaxTests/surplus_input.yul @@ -0,0 +1,4 @@ +{} +{} +// ---- +// ParserError 2314: (3-4): Expected end of source but got '{' diff --git a/test/libyul/yulSyntaxTests/switch_case.yul b/test/libyul/yulSyntaxTests/switch_case.yul new file mode 100644 index 000000000000..88bda992946a --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_case.yul @@ -0,0 +1,8 @@ +{ + switch 0:u256 + case 42:u256 {} + case 0x42:u256 {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/switch_case_different_literal.yul b/test/libyul/yulSyntaxTests/switch_case_different_literal.yul new file mode 100644 index 000000000000..8514b835d2fc --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_case_different_literal.yul @@ -0,0 +1,8 @@ +{ + switch 1:u256 + case "1":u256 {} + case "2":u256 {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/switch_case_string_literal_too_long.yul b/test/libyul/yulSyntaxTests/switch_case_string_literal_too_long.yul new file mode 100644 index 000000000000..dceb9db16092 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_case_string_literal_too_long.yul @@ -0,0 +1,9 @@ +{ + let x:u256 + switch x + case "012345678901234567890123456789012":u256 {} +} +// ==== +// dialect: evmTyped +// ---- +// TypeError 3069: (30-70): String literal too long (33 > 32) diff --git a/test/libyul/yulSyntaxTests/switch_case_string_literal_very_long.yul b/test/libyul/yulSyntaxTests/switch_case_string_literal_very_long.yul new file mode 100644 index 000000000000..55f25124b2ad --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_case_string_literal_very_long.yul @@ -0,0 +1,8 @@ +{ + let x:u256 + switch x + case "01234567890123456789012345678901":u256 {} +} +// ==== +// dialect: evmTyped +// ---- diff --git a/test/libyul/yulSyntaxTests/switch_default_before_case.yul b/test/libyul/yulSyntaxTests/switch_default_before_case.yul new file mode 100644 index 000000000000..14434b7acd2f --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_default_before_case.yul @@ -0,0 +1,7 @@ +{ + switch 42 + default {} + case 1 {} +} +// ---- +// ParserError 4904: (35-39): Case not allowed after default case. diff --git a/test/libyul/yulSyntaxTests/switch_duplicate_case.yul b/test/libyul/yulSyntaxTests/switch_duplicate_case.yul new file mode 100644 index 000000000000..47fb88701d3a --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_duplicate_case.yul @@ -0,0 +1,9 @@ +{ + switch 0:u256 + case 0:u256 {} + case 0x0:u256 {} +} +// ==== +// dialect: evmTyped +// ---- +// DeclarationError 6792: (34-50): Duplicate case defined. diff --git a/test/libyul/yulSyntaxTests/switch_duplicate_case_different_literal.yul b/test/libyul/yulSyntaxTests/switch_duplicate_case_different_literal.yul new file mode 100644 index 000000000000..b190124bcf85 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_duplicate_case_different_literal.yul @@ -0,0 +1,9 @@ +{ + switch 0:u256 + case 0:u256 {} + case "":u256 {} +} +// ==== +// dialect: evmTyped +// ---- +// DeclarationError 6792: (34-49): Duplicate case defined. diff --git a/test/libyul/yulSyntaxTests/switch_duplicate_default.yul b/test/libyul/yulSyntaxTests/switch_duplicate_default.yul new file mode 100644 index 000000000000..4b73953fe8e4 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_duplicate_default.yul @@ -0,0 +1,7 @@ +{ + switch 42 + default {} + default {} +} +// ---- +// ParserError 6931: (35-42): Only one default case allowed. diff --git a/test/libyul/yulSyntaxTests/switch_invalid_body.yul b/test/libyul/yulSyntaxTests/switch_invalid_body.yul new file mode 100644 index 000000000000..b6c4816fb169 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_invalid_body.yul @@ -0,0 +1,8 @@ +{ + switch 42 + case 1 mul + case 2 {} + default {} +} +// ---- +// ParserError 2314: (27-30): Expected '{' but got identifier diff --git a/test/libyul/yulSyntaxTests/switch_invalid_case.yul b/test/libyul/yulSyntaxTests/switch_invalid_case.yul new file mode 100644 index 000000000000..0aa52d371e75 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_invalid_case.yul @@ -0,0 +1,8 @@ +{ + switch 42 + case mul(1, 2) {} + case 2 {} + default {} +} +// ---- +// ParserError 4805: (28-29): Literal expected. diff --git a/test/libyul/yulSyntaxTests/switch_invalid_expr_1.yul b/test/libyul/yulSyntaxTests/switch_invalid_expr_1.yul new file mode 100644 index 000000000000..6609a9ef3f00 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_invalid_expr_1.yul @@ -0,0 +1,7 @@ +{ + switch {} + case 1 {} + default {} +} +// ---- +// ParserError 1856: (13-14): Literal or identifier expected. diff --git a/test/libyul/yulSyntaxTests/switch_invalid_expr_2.yul b/test/libyul/yulSyntaxTests/switch_invalid_expr_2.yul new file mode 100644 index 000000000000..9378297ca05c --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_invalid_expr_2.yul @@ -0,0 +1,7 @@ +{ + switch mload + case 1 {} + default {} +} +// ---- +// ParserError 7104: (13-18): Builtin function "mload" must be called. diff --git a/test/libyul/yulSyntaxTests/switch_invalid_expr_3.yul b/test/libyul/yulSyntaxTests/switch_invalid_expr_3.yul new file mode 100644 index 000000000000..6ab68d7ea9a3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_invalid_expr_3.yul @@ -0,0 +1,9 @@ +{ + switch mstore(1, 1) + case 1 {} + default {} +} +// ==== +// dialect: evm +// ---- +// TypeError 3950: (10-22): Expected expression to evaluate to one value, but got 0 values instead. diff --git a/test/libyul/yulSyntaxTests/switch_statement_1.yul b/test/libyul/yulSyntaxTests/switch_statement_1.yul new file mode 100644 index 000000000000..13115a922930 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_statement_1.yul @@ -0,0 +1,3 @@ +{ switch 42 default {} } +// ---- +// Warning 9592: (2-22): "switch" statement with only a default case. diff --git a/test/libyul/yulSyntaxTests/switch_statement_2.yul b/test/libyul/yulSyntaxTests/switch_statement_2.yul new file mode 100644 index 000000000000..f58eb3a7a6e6 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_statement_2.yul @@ -0,0 +1,8 @@ +{ + { switch 42 case 1 {} } + { switch 42 case 1 {} case 2 {} } + { switch 42 case 1 {} default {} } + { switch 42 case 1 {} case 2 {} default {} } + { switch mul(1, 2) case 1 {} case 2 {} default {} } + { function f() -> x {} switch f() case 1 {} case 2 {} default {} } +} diff --git a/test/libyul/yulSyntaxTests/switch_statement_duplicate_case.yul b/test/libyul/yulSyntaxTests/switch_statement_duplicate_case.yul new file mode 100644 index 000000000000..5c9bbe045ae9 --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_statement_duplicate_case.yul @@ -0,0 +1,10 @@ +{ + switch 42 + case 1 {} + case 1 {} + default {} +} +// ==== +// dialect: evm +// ---- +// DeclarationError 6792: (25-34): Duplicate case defined. diff --git a/test/libyul/yulSyntaxTests/switch_statement_no_access.yul b/test/libyul/yulSyntaxTests/switch_statement_no_access.yul new file mode 100644 index 000000000000..4f40f264036d --- /dev/null +++ b/test/libyul/yulSyntaxTests/switch_statement_no_access.yul @@ -0,0 +1,5 @@ +{ + switch 42 +} +// ---- +// ParserError 2418: (16-17): Switch statement without any cases. diff --git a/test/libyul/yulSyntaxTests/token_as_identifier.yul b/test/libyul/yulSyntaxTests/token_as_identifier.yul new file mode 100644 index 000000000000..4af1ea2130aa --- /dev/null +++ b/test/libyul/yulSyntaxTests/token_as_identifier.yul @@ -0,0 +1,8 @@ +{ + let return:u256 := 1:u256 + let byte:u256 := 1:u256 + let address:u256 := 1:u256 + let bool:u256 := 1:u256 +} +// ==== +// dialect: yul diff --git a/test/libyul/yulSyntaxTests/tuple_assignment.yul b/test/libyul/yulSyntaxTests/tuple_assignment.yul new file mode 100644 index 000000000000..dbaccd097d83 --- /dev/null +++ b/test/libyul/yulSyntaxTests/tuple_assignment.yul @@ -0,0 +1,4 @@ +{ + function f() -> a:u256, b:u256, c:u256 {} + let x:u256, y:u256, z:u256 := f() +} \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/vardecl.yul b/test/libyul/yulSyntaxTests/vardecl.yul new file mode 100644 index 000000000000..64fd69d6fed1 --- /dev/null +++ b/test/libyul/yulSyntaxTests/vardecl.yul @@ -0,0 +1,5 @@ +{ + let x := 7 +} +// ==== +// dialect: yul diff --git a/test/libyul/yulSyntaxTests/vardecl_bool.yul b/test/libyul/yulSyntaxTests/vardecl_bool.yul new file mode 100644 index 000000000000..93ec762b0519 --- /dev/null +++ b/test/libyul/yulSyntaxTests/vardecl_bool.yul @@ -0,0 +1,6 @@ +{ + let x := true + let y := false +} +// ==== +// dialect: evm diff --git a/test/libyul/yulSyntaxTests/vardecl_complex.yul b/test/libyul/yulSyntaxTests/vardecl_complex.yul new file mode 100644 index 000000000000..117e1d9975b3 --- /dev/null +++ b/test/libyul/yulSyntaxTests/vardecl_complex.yul @@ -0,0 +1,4 @@ +{ + let y := 2 + let x := add(add(7, mul(6, y)), mul(7, 8)) +} diff --git a/test/libyul/yulSyntaxTests/vardecl_empty.yul b/test/libyul/yulSyntaxTests/vardecl_empty.yul new file mode 100644 index 000000000000..dc536c67ac99 --- /dev/null +++ b/test/libyul/yulSyntaxTests/vardecl_empty.yul @@ -0,0 +1,3 @@ +{ + let x +} diff --git a/test/libyul/yulSyntaxTests/vardecl_multi.yul b/test/libyul/yulSyntaxTests/vardecl_multi.yul new file mode 100644 index 000000000000..abf04b7461e4 --- /dev/null +++ b/test/libyul/yulSyntaxTests/vardecl_multi.yul @@ -0,0 +1,4 @@ +{ + function f() -> x, y {} + let x, y := f() +} diff --git a/test/libyul/yulSyntaxTests/vardecl_multi_conflict.yul b/test/libyul/yulSyntaxTests/vardecl_multi_conflict.yul new file mode 100644 index 000000000000..c40c79740188 --- /dev/null +++ b/test/libyul/yulSyntaxTests/vardecl_multi_conflict.yul @@ -0,0 +1,6 @@ +{ + function f() -> x, y {} + let x, x := f() +} +// ---- +// DeclarationError 1395: (28-43): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/vardecl_name_clashes.yul b/test/libyul/yulSyntaxTests/vardecl_name_clashes.yul new file mode 100644 index 000000000000..2906078cf622 --- /dev/null +++ b/test/libyul/yulSyntaxTests/vardecl_name_clashes.yul @@ -0,0 +1,6 @@ +{ + let x := 1 + let x := 2 +} +// ---- +// DeclarationError 1395: (15-25): Variable name x already taken in this scope. diff --git a/test/libyul/yulSyntaxTests/variable_access_cross_funcs.yul b/test/libyul/yulSyntaxTests/variable_access_cross_funcs.yul new file mode 100644 index 000000000000..7ddbacb8d1b8 --- /dev/null +++ b/test/libyul/yulSyntaxTests/variable_access_cross_funcs.yul @@ -0,0 +1,8 @@ +{ + let x := 2 + function g() { + pop(x) + } +} +// ---- +// DeclarationError 8198: (36-37): Identifier not found. diff --git a/test/libyul/yulSyntaxTests/variable_declaration.yul b/test/libyul/yulSyntaxTests/variable_declaration.yul new file mode 100644 index 000000000000..5e2ced34aa94 --- /dev/null +++ b/test/libyul/yulSyntaxTests/variable_declaration.yul @@ -0,0 +1,3 @@ +{ let x:u256 := 7:u256 } +// ==== +// dialect: evmTyped diff --git a/test/libyul/yulSyntaxTests/variable_declaration_bool.yul b/test/libyul/yulSyntaxTests/variable_declaration_bool.yul new file mode 100644 index 000000000000..8ef371eefe4c --- /dev/null +++ b/test/libyul/yulSyntaxTests/variable_declaration_bool.yul @@ -0,0 +1,6 @@ +{ + let x:bool := true:bool + let y:bool := false:bool +} +// ==== +// dialect: evmTyped diff --git a/test/libyul/yulSyntaxTests/variable_declaration_complex.yul b/test/libyul/yulSyntaxTests/variable_declaration_complex.yul new file mode 100644 index 000000000000..a1eab2fd666d --- /dev/null +++ b/test/libyul/yulSyntaxTests/variable_declaration_complex.yul @@ -0,0 +1,7 @@ +{ + function add(a:u256, b:u256) -> c:u256 {} + let y:u256 := 2:u256 + let x:u256 := add(7:u256, add(6:u256, y)) +} +// ==== +// dialect: yul \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/variable_declaration_empty.yul b/test/libyul/yulSyntaxTests/variable_declaration_empty.yul new file mode 100644 index 000000000000..31f51090e9e5 --- /dev/null +++ b/test/libyul/yulSyntaxTests/variable_declaration_empty.yul @@ -0,0 +1 @@ +{ let x:u256 } \ No newline at end of file diff --git a/test/libyul/yulSyntaxTests/variable_use_before_decl_1.yul b/test/libyul/yulSyntaxTests/variable_use_before_decl_1.yul new file mode 100644 index 000000000000..76bcd1d0d626 --- /dev/null +++ b/test/libyul/yulSyntaxTests/variable_use_before_decl_1.yul @@ -0,0 +1,6 @@ +{ + x := 2 + let x := 3 +} +// ---- +// DeclarationError 1133: (3-4): Variable x used before it was declared. diff --git a/test/libyul/yulSyntaxTests/variable_use_before_decl_2.yul b/test/libyul/yulSyntaxTests/variable_use_before_decl_2.yul new file mode 100644 index 000000000000..c6612fd8cb70 --- /dev/null +++ b/test/libyul/yulSyntaxTests/variable_use_before_decl_2.yul @@ -0,0 +1,6 @@ +{ + let x := mul(2, x) +} + +// ---- +// DeclarationError 4990: (19-20): Variable x used before it was declared. diff --git a/test/stopAfterParseTests.sh b/test/stopAfterParseTests.sh new file mode 100755 index 000000000000..8320aeb75540 --- /dev/null +++ b/test/stopAfterParseTests.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +set -e + +READLINK=readlink +if [[ "$OSTYPE" == "darwin"* ]]; then + READLINK=greadlink +fi +REPO_ROOT=$(${READLINK} -f "$(dirname "$0")"/..) +SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build} +SOLC=${SOLIDITY_BUILD_DIR}/solc/solc +SPLITSOURCES=${REPO_ROOT}/scripts/splitSources.py + +FILETMP=$(mktemp -d) +cd "$FILETMP" || exit 1 + + +function testFile() +{ + set +e + ALLOUTPUT=$($SOLC --combined-json ast,compact-format --pretty-json "$@" --stop-after parsing 2>&1) + local RESULT=$? + set -e + if test ${RESULT} -ne 0; then + # solc returned failure. Compilation errors and unimplemented features + # are okay, everything else is a failed test (segfault) + if ! echo "$ALLOUTPUT" | grep -e "Unimplemented feature:" -e "Error:" -q; then + echo -n "Test failed on " + echo "$@" + echo "$ALLOUTPUT" + return 1 + fi + else + echo -n . + fi + + return 0; +} + +while read -r file; do + set +e + OUTPUT=$($SPLITSOURCES "$file") + RETURN_CODE=$? + set -e + FAILED=0 + + if [ $RETURN_CODE -eq 0 ] + then + # shellcheck disable=SC2086 + testFile $OUTPUT + FAILED=$? + rm -r "${FILETMP:?}"/* + elif [ $RETURN_CODE -eq 1 ] + then + testFile "$file" + FAILED=$? + elif [ $RETURN_CODE -eq 2 ] + then + echo -n "" + else + echo "Received unexpected return code $RETURN_CODE while processing $file: " + echo "-----" + echo "$OUTPUT" + exit 3 + fi + + if [ $FAILED -eq 1 ] + then + echo -n "Failure on " + echo "$file" + exit 1 + fi +done < <(find "${REPO_ROOT}/test" -iname "*.sol" -and -not -name "documentation.sol") +echo diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index c8add4ce7ab1..dc0d58bcec61 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -31,7 +31,6 @@ add_executable(isoltest ../libsolidity/ABIJsonTest.cpp ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerTest.cpp - ../libsolidity/SMTCheckerJSONTest.cpp ../libyul/Common.cpp ../libyul/EwasmTranslationTest.cpp ../libyul/FunctionSideEffects.cpp diff --git a/test/tools/IsolTestOptions.cpp b/test/tools/IsolTestOptions.cpp index 84e2babe921e..763a0b3b7b63 100644 --- a/test/tools/IsolTestOptions.cpp +++ b/test/tools/IsolTestOptions.cpp @@ -35,6 +35,9 @@ namespace po = boost::program_options; namespace solidity::test { +namespace +{ + auto const description = R"(isoltest, tool for interactively managing test contracts. Usage: isoltest [Options] Interactively validates test contracts. @@ -51,6 +54,8 @@ std::string editorPath() return std::string{}; } +} + IsolTestOptions::IsolTestOptions(std::string* _editor): CommonOptions(description) { @@ -77,7 +82,7 @@ bool IsolTestOptions::parse(int _argc, char const* const* _argv) void IsolTestOptions::validate() const { - static std::string filterString{"[a-zA-Z1-9_/*]*"}; + static std::string filterString{"[a-zA-Z0-9_/*]*"}; static std::regex filterExpression{filterString}; assertThrow( regex_match(testFilter, filterExpression), diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 3bdb16b0f4e9..0d6a92e5bfaa 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -161,6 +161,7 @@ TestTool::Result TestTool::process() m_test = m_testCaseCreator(TestCase::Config{ m_path.string(), m_options.evmVersion(), + m_options.vmPaths, m_options.enforceViaYul }); if (m_test->shouldRun()) @@ -424,15 +425,20 @@ int main(int argc, char const *argv[]) auto& options = dynamic_cast(solidity::test::CommonOptions::get()); - bool disableSemantics = !solidity::test::EVMHost::getVM(options.evmonePath.string()); - if (disableSemantics) + bool disableSemantics = true; + try { - cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using --evmonepath ." << endl; - cout << "You can download it at" << endl; - cout << solidity::test::evmoneDownloadLink << endl; - cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + disableSemantics = !solidity::test::EVMHost::checkVmPaths(options.vmPaths); + } + catch (std::runtime_error const& _exception) + { + cerr << "Error: " << _exception.what() << endl; + return 1; } + if (disableSemantics) + cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + TestStats global_stats{0, 0}; cout << "Running tests..." << endl << endl; @@ -472,7 +478,7 @@ int main(int argc, char const *argv[]) cout << "." << endl; if (disableSemantics) - cout << "\nNOTE: Skipped semantics tests because " << solidity::test::evmoneFilename << " could not be found.\n" << endl; + cout << "\nNOTE: Skipped semantics tests because no evmc vm could be found.\n" << endl; return global_stats ? 0 : 1; } diff --git a/test/tools/ossfuzz/AbiV2IsabelleFuzzer.cpp b/test/tools/ossfuzz/AbiV2IsabelleFuzzer.cpp new file mode 100644 index 000000000000..4d99890122f9 --- /dev/null +++ b/test/tools/ossfuzz/AbiV2IsabelleFuzzer.cpp @@ -0,0 +1,107 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +#include +#include + +using namespace solidity::test::abiv2fuzzer; +using namespace solidity::test; +using namespace solidity::util; +using namespace solidity; +using namespace std; + +static constexpr size_t abiCoderHeapSize = 1024 * 512; +static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; +/// Expected output value is decimal 0 +static vector const expectedOutput(32, 0); + +DEFINE_PROTO_FUZZER(Contract const& _contract) +{ + ProtoConverter converter; + string contractSource = converter.contractToString(_contract); + + if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) + { + // With libFuzzer binary run this to generate the solidity source file x.sol from a proto input: + // PROTO_FUZZER_DUMP_PATH=x.sol ./a.out proto-input + ofstream of(dump_path); + of << contractSource; + } + + string typeString = converter.isabelleTypeString(); + string valueString = converter.isabelleValueString(); + abicoder::ABICoder coder(abiCoderHeapSize); + if (!typeString.empty() && converter.coderFunction()) + { + auto [encodeStatus, encodedData] = coder.encode(typeString, valueString); + solAssert(encodeStatus, "Isabelle abicoder fuzzer: Encoding failed"); + + // Raw runtime byte code generated by solidity + bytes byteCode; + string hexEncodedInput; + + try + { + // Compile contract generated by the proto fuzzer + SolidityCompilationFramework solCompilationFramework; + string contractName = ":C"; + byteCode = solCompilationFramework.compileContract(contractSource, contractName); + Json::Value methodIdentifiers = solCompilationFramework.getMethodIdentifiers(); + // We always call the second function from the list of alphabetically + // sorted interface functions + hexEncodedInput = (++methodIdentifiers.begin())->asString() + encodedData.substr(2, encodedData.size()); + } + // Ignore stack too deep errors during compilation + catch (solidity::evmasm::StackTooDeepException const&) + { + return; + } + + // We target the default EVM which is the latest + solidity::langutil::EVMVersion version; + EVMHost hostContext(version, evmone); + + // Deploy contract and signal failure if deployment failed + evmc::result createResult = AbiV2Utility::deployContract(hostContext, byteCode); + solAssert( + createResult.status_code == EVMC_SUCCESS, + "Proto ABIv2 Fuzzer: Contract creation failed" + ); + + // Execute test function and signal failure if EVM reverted or + // did not return expected output on successful execution. + evmc::result callResult = AbiV2Utility::executeContract( + hostContext, + fromHex(hexEncodedInput), + createResult.create_address + ); + + // We don't care about EVM One failures other than EVMC_REVERT + solAssert(callResult.status_code != EVMC_REVERT, "Proto ABIv2 fuzzer: EVM One reverted"); + if (callResult.status_code == EVMC_SUCCESS) + solAssert( + AbiV2Utility::isOutputExpected(callResult.output_data, callResult.output_size, expectedOutput), + "Proto ABIv2 fuzzer: ABIv2 coding failure found" + ); + + } + return; +} diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index a799bd736e03..21d1e19dce2b 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -1,36 +1,65 @@ add_custom_target(ossfuzz) add_dependencies(ossfuzz solc_opt_ossfuzz + solc_opt_mutator_ossfuzz solc_noopt_ossfuzz + solc_noopt_mutator_ossfuzz const_opt_ossfuzz strictasm_diff_ossfuzz strictasm_opt_ossfuzz strictasm_assembly_ossfuzz - ) - +) if (OSSFUZZ) add_custom_target(ossfuzz_proto) add_dependencies(ossfuzz_proto - sol_proto_ossfuzz - yul_proto_ossfuzz - yul_proto_diff_ossfuzz - yul_proto_diff_custom_mutate_ossfuzz + sol_proto_ossfuzz + yul_proto_ossfuzz + yul_proto_diff_ossfuzz + yul_proto_diff_custom_mutate_ossfuzz ) add_custom_target(ossfuzz_abiv2) - add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz) + add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz abiv2_isabelle_ossfuzz) endif() if (OSSFUZZ) - add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp) + add_executable(solc_opt_ossfuzz + solc_opt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + ) target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm) set_target_properties(solc_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - add_executable(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp ../../TestCaseReader.cpp) + add_executable(solc_opt_mutator_ossfuzz + solc_opt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + SolidityGenerator.cpp + SolidityCustomMutatorInterface.cpp + ) + target_link_libraries(solc_opt_mutator_ossfuzz PRIVATE libsolc evmasm) + set_target_properties(solc_opt_mutator_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + + add_executable(solc_noopt_ossfuzz + solc_noopt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + ) target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm) set_target_properties(solc_noopt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + add_executable(solc_noopt_mutator_ossfuzz + solc_noopt_ossfuzz.cpp + ../fuzzer_common.cpp + ../../TestCaseReader.cpp + SolidityGenerator.cpp + SolidityCustomMutatorInterface.cpp + ) + target_link_libraries(solc_noopt_mutator_ossfuzz PRIVATE libsolc evmasm) + set_target_properties(solc_noopt_mutator_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + add_executable(const_opt_ossfuzz const_opt_ossfuzz.cpp ../fuzzer_common.cpp) target_link_libraries(const_opt_ossfuzz PRIVATE libsolc evmasm) set_target_properties(const_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) @@ -55,7 +84,7 @@ if (OSSFUZZ) protobuf.a ) set_target_properties(yul_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - target_compile_options(yul_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion) + target_compile_options(yul_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion -Wno-suggest-destructor-override -Wno-inconsistent-missing-destructor-override) add_executable(yul_proto_diff_ossfuzz yulProto_diff_ossfuzz.cpp yulFuzzerCommon.cpp protoToYul.cpp yulProto.pb.cc) target_include_directories(yul_proto_diff_ossfuzz PRIVATE /usr/include/libprotobuf-mutator) @@ -66,7 +95,7 @@ if (OSSFUZZ) protobuf.a ) set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - target_compile_options(yul_proto_diff_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion) + target_compile_options(yul_proto_diff_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion -Wno-suggest-destructor-override -Wno-inconsistent-missing-destructor-override) add_executable(yul_proto_diff_custom_mutate_ossfuzz yulProto_diff_ossfuzz.cpp @@ -74,16 +103,16 @@ if (OSSFUZZ) protoToYul.cpp yulProto.pb.cc protomutators/YulProtoMutator.cpp -) + ) target_include_directories(yul_proto_diff_custom_mutate_ossfuzz PRIVATE /usr/include/libprotobuf-mutator) target_link_libraries(yul_proto_diff_custom_mutate_ossfuzz PRIVATE yul yulInterpreter protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a -) + ) set_target_properties(yul_proto_diff_custom_mutate_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - target_compile_options(yul_proto_diff_custom_mutate_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion) + target_compile_options(yul_proto_diff_custom_mutate_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion -Wno-suggest-destructor-override -Wno-inconsistent-missing-destructor-override) add_executable(abiv2_proto_ossfuzz ../../EVMHost.cpp @@ -103,7 +132,29 @@ if (OSSFUZZ) protobuf.a ) set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - target_compile_options(abiv2_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion) + target_compile_options(abiv2_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion -Wno-suggest-destructor-override -Wno-inconsistent-missing-destructor-override) + + add_executable(abiv2_isabelle_ossfuzz + ../../EVMHost.cpp + AbiV2IsabelleFuzzer.cpp + abiV2FuzzerCommon.cpp + protoToAbiV2.cpp + abiV2Proto.pb.cc + ) + target_include_directories(abiv2_isabelle_ossfuzz PRIVATE + /usr/include/libprotobuf-mutator + ) + target_link_libraries(abiv2_isabelle_ossfuzz PRIVATE solidity + evmc + evmone-standalone + protobuf-mutator-libfuzzer.a + protobuf-mutator.a + protobuf.a + abicoder + gmp.a + ) + set_target_properties(abiv2_isabelle_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) + target_compile_options(abiv2_isabelle_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion -Wno-suggest-destructor-override -Wno-inconsistent-missing-destructor-override) add_executable(sol_proto_ossfuzz solProtoFuzzer.cpp @@ -123,7 +174,7 @@ if (OSSFUZZ) protobuf.a ) set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE}) - target_compile_options(sol_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion) + target_compile_options(sol_proto_ossfuzz PUBLIC ${COMPILE_OPTIONS} -Wno-sign-conversion -Wno-suggest-destructor-override -Wno-inconsistent-missing-destructor-override) else() add_library(solc_opt_ossfuzz solc_opt_ossfuzz.cpp @@ -131,12 +182,24 @@ else() ) target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm) + add_library(solc_opt_mutator_ossfuzz + solc_opt_ossfuzz.cpp + ../fuzzer_common.cpp + ) + target_link_libraries(solc_opt_mutator_ossfuzz PRIVATE libsolc evmasm) + add_library(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp ) target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm) + add_library(solc_noopt_mutator_ossfuzz + solc_noopt_ossfuzz.cpp + ../fuzzer_common.cpp + ) + target_link_libraries(solc_noopt_mutator_ossfuzz PRIVATE libsolc evmasm) + add_library(const_opt_ossfuzz const_opt_ossfuzz.cpp ../fuzzer_common.cpp) diff --git a/test/tools/ossfuzz/SolidityCustomMutatorInterface.cpp b/test/tools/ossfuzz/SolidityCustomMutatorInterface.cpp new file mode 100644 index 000000000000..16c71f970870 --- /dev/null +++ b/test/tools/ossfuzz/SolidityCustomMutatorInterface.cpp @@ -0,0 +1,67 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include + +#include + +using namespace std; +using namespace solidity::test::fuzzer; + +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" size_t LLVMFuzzerMutate(uint8_t* _data, size_t _size, size_t _maxSize); +extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* _data, size_t size, size_t _maxSize, unsigned int seed); + +/// Define Solidity's custom mutator by implementing libFuzzer's +/// custom mutator external interface. +extern "C" size_t LLVMFuzzerCustomMutator( + uint8_t* _data, + size_t _size, + size_t _maxSize, + unsigned int _seed +) +{ + if (_maxSize <= _size || _size == 0) + return LLVMFuzzerMutate(_data, _size, _maxSize); + return SolidityCustomMutatorInterface{_data, _size, _maxSize, _seed}.generate(); +} + +SolidityCustomMutatorInterface::SolidityCustomMutatorInterface( + uint8_t* _data, + size_t _size, + size_t _maxSize, + unsigned int _seed + ): + data(_data), + size(_size), + maxMutantSize(_maxSize), + generator(make_shared(_seed)) +{} + +size_t SolidityCustomMutatorInterface::generate() +{ + string testCase = generator->generateTestProgram(); + solAssert( + !testCase.empty() && data, + "Solc custom mutator: Invalid mutant or memory pointer" + ); + size_t mutantSize = min(testCase.size(), maxMutantSize - 1); + mempcpy(data, testCase.data(), mutantSize); + return mutantSize; +} diff --git a/test/tools/ossfuzz/SolidityCustomMutatorInterface.h b/test/tools/ossfuzz/SolidityCustomMutatorInterface.h new file mode 100644 index 000000000000..2b7be6cc5e84 --- /dev/null +++ b/test/tools/ossfuzz/SolidityCustomMutatorInterface.h @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Implements libFuzzer's custom mutator interface. + */ + +#pragma once + +#include + +#include + +namespace solidity::test::fuzzer +{ +struct SolidityCustomMutatorInterface +{ + SolidityCustomMutatorInterface(uint8_t* _data, size_t _size, size_t _maxSize, unsigned _seed); + /// Generates Solidity test program, copies it into buffer + /// provided by libFuzzer and @returns size of the test program. + size_t generate(); + + /// Raw pointer to libFuzzer provided input + uint8_t* data; + /// Size of libFuzzer provided input + size_t size; + /// Maximum length of mutant specified by libFuzzer + size_t maxMutantSize; + /// Solidity generator handle + std::shared_ptr generator; +}; +} diff --git a/test/tools/ossfuzz/SolidityGenerator.cpp b/test/tools/ossfuzz/SolidityGenerator.cpp new file mode 100644 index 000000000000..6e9198b879de --- /dev/null +++ b/test/tools/ossfuzz/SolidityGenerator.cpp @@ -0,0 +1,35 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +using namespace solidity::test::fuzzer; +using namespace solidity::util; +using namespace std; + +string SolidityGenerator::generateTestProgram() +{ + // TODO: Add generators for grammar elements of + // Solidity antlr4 grammar. Currently, the generated + // test program consists of a version pragma only. + return Whiskers(R"(pragma ;)") + ("directive", "solidity >= 0.0.0") + .render(); +} diff --git a/test/tools/ossfuzz/SolidityGenerator.h b/test/tools/ossfuzz/SolidityGenerator.h new file mode 100644 index 000000000000..55c3c1f3ace5 --- /dev/null +++ b/test/tools/ossfuzz/SolidityGenerator.h @@ -0,0 +1,42 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Implements generators for synthesizing mostly syntactically valid + * Solidity test programs. + */ + +#pragma once + +#include + +namespace solidity::test::fuzzer +{ +using RandomEngine = std::mt19937_64; + +class SolidityGenerator +{ +public: + SolidityGenerator(uint64_t _seed): m_rand(_seed) + {} + /// @returns a pseudo randomly generated test program + std::string generateTestProgram(); +private: + /// Random number generator + RandomEngine const m_rand; +}; +} diff --git a/test/tools/ossfuzz/abiV2FuzzerCommon.cpp b/test/tools/ossfuzz/abiV2FuzzerCommon.cpp index c9109ecdbbb7..aea86191639a 100644 --- a/test/tools/ossfuzz/abiV2FuzzerCommon.cpp +++ b/test/tools/ossfuzz/abiV2FuzzerCommon.cpp @@ -1,5 +1,8 @@ #include +#include +#include + using namespace solidity::test::abiv2fuzzer; SolidityCompilationFramework::SolidityCompilationFramework(langutil::EVMVersion _evmVersion) @@ -21,7 +24,7 @@ solidity::bytes SolidityCompilationFramework::compileContract( m_compiler.setOptimiserSettings(_optimization); if (!m_compiler.compile()) { - langutil::SourceReferenceFormatter formatter(std::cerr); + langutil::SourceReferenceFormatter formatter(std::cerr, false, false); for (auto const& error: m_compiler.errors()) formatter.printExceptionInformation( @@ -37,3 +40,46 @@ solidity::bytes SolidityCompilationFramework::compileContract( ); return obj.bytecode; } + +bool AbiV2Utility::isOutputExpected( + uint8_t const* _result, + size_t _length, + std::vector const& _expectedOutput +) +{ + if (_length != _expectedOutput.size()) + return false; + + return (memcmp(_result, _expectedOutput.data(), _length) == 0); +} + +evmc_message AbiV2Utility::initializeMessage(bytes const& _input) +{ + // Zero initialize all message fields + evmc_message msg = {}; + // Gas available (value of type int64_t) is set to its maximum + // value. + msg.gas = std::numeric_limits::max(); + msg.input_data = _input.data(); + msg.input_size = _input.size(); + return msg; +} + +evmc::result AbiV2Utility::executeContract( + EVMHost& _hostContext, + bytes const& _functionHash, + evmc_address _deployedAddress +) +{ + evmc_message message = initializeMessage(_functionHash); + message.destination = _deployedAddress; + message.kind = EVMC_CALL; + return _hostContext.call(message); +} + +evmc::result AbiV2Utility::deployContract(EVMHost& _hostContext, bytes const& _code) +{ + evmc_message message = initializeMessage(_code); + message.kind = EVMC_CREATE; + return _hostContext.call(message); +} diff --git a/test/tools/ossfuzz/abiV2FuzzerCommon.h b/test/tools/ossfuzz/abiV2FuzzerCommon.h index e18458b77662..979ced617a1a 100644 --- a/test/tools/ossfuzz/abiV2FuzzerCommon.h +++ b/test/tools/ossfuzz/abiV2FuzzerCommon.h @@ -1,17 +1,17 @@ #pragma once +#include + #include #include -#include -#include - #include +#include + namespace solidity::test::abiv2fuzzer { - class SolidityCompilationFramework { public: @@ -32,4 +32,36 @@ class SolidityCompilationFramework langutil::EVMVersion m_evmVersion; }; +struct AbiV2Utility +{ + /// Compares the contents of the memory address pointed to + /// by `_result` of `_length` bytes to the expected output. + /// Returns true if `_result` matches expected output, false + /// otherwise. + static bool isOutputExpected( + uint8_t const* _result, + size_t _length, + std::vector const& _expectedOutput + ); + /// Accepts a reference to a user-specified input and returns an + /// evmc_message with all of its fields zero initialized except + /// gas and input fields. + /// The gas field is set to the maximum permissible value so that we + /// don't run into out of gas errors. The input field is copied from + /// user input. + static evmc_message initializeMessage(bytes const& _input); + /// Accepts host context implementation, and keccak256 hash of the function + /// to be called at a specified address in the simulated blockchain as + /// input and returns the result of the execution of the called function. + static evmc::result executeContract( + EVMHost& _hostContext, + bytes const& _functionHash, + evmc_address _deployedAddress + ); + /// Accepts a reference to host context implementation and byte code + /// as input and deploys it on the simulated blockchain. Returns the + /// result of deployment. + static evmc::result deployContract(EVMHost& _hostContext, bytes const& _code); +}; + } diff --git a/test/tools/ossfuzz/abiV2Proto.proto b/test/tools/ossfuzz/abiV2Proto.proto index b6640bbe977c..1afc031767f9 100644 --- a/test/tools/ossfuzz/abiV2Proto.proto +++ b/test/tools/ossfuzz/abiV2Proto.proto @@ -32,10 +32,8 @@ message FixedByteType { required uint32 width = 1; } -// address, address payable -message AddressType { - required bool payable = 1; -} +// address +message AddressType {} message ValueType { oneof value_type_oneof { @@ -46,14 +44,8 @@ message ValueType { } } -// bytes/string -message DynamicByteArrayType { - enum DType { - BYTES = 0; - STRING = 1; - } - required DType type = 1; -} +// bytes +message DynamicByteArrayType {} message ArrayType { required Type t = 1; diff --git a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp index 8c529630f8c4..c31ec8ee0fce 100644 --- a/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp +++ b/test/tools/ossfuzz/abiV2ProtoFuzzer.cpp @@ -16,89 +16,26 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include #include #include -#include #include #include -static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; - using namespace solidity::test::abiv2fuzzer; using namespace solidity::test; using namespace solidity::util; using namespace solidity; using namespace std; -namespace -{ -/// Test function returns a uint256 value -static size_t const expectedOutputLength = 32; +static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; /// Expected output value is decimal 0 -static uint8_t const expectedOutput[expectedOutputLength] = { +static vector const expectedOutput = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -/// Compares the contents of the memory address pointed to -/// by `_result` of `_length` bytes to the expected output. -/// Returns true if `_result` matches expected output, false -/// otherwise. -bool isOutputExpected(uint8_t const* _result, size_t _length) -{ - if (_length != expectedOutputLength) - return false; - - return (memcmp(_result, expectedOutput, expectedOutputLength) == 0); -} - -/// Accepts a reference to a user-specified input and returns an -/// evmc_message with all of its fields zero initialized except -/// gas and input fields. -/// The gas field is set to the maximum permissible value so that we -/// don't run into out of gas errors. The input field is copied from -/// user input. -evmc_message initializeMessage(bytes const& _input) -{ - // Zero initialize all message fields - evmc_message msg = {}; - // Gas available (value of type int64_t) is set to its maximum - // value. - msg.gas = std::numeric_limits::max(); - msg.input_data = _input.data(); - msg.input_size = _input.size(); - return msg; -} - -/// Accepts host context implementation, and keccak256 hash of the function -/// to be called at a specified address in the simulated blockchain as -/// input and returns the result of the execution of the called function. -evmc::result executeContract( - EVMHost& _hostContext, - bytes const& _functionHash, - evmc_address _deployedAddress -) -{ - evmc_message message = initializeMessage(_functionHash); - message.destination = _deployedAddress; - message.kind = EVMC_CALL; - return _hostContext.call(message); -} - -/// Accepts a reference to host context implementation and byte code -/// as input and deploys it on the simulated blockchain. Returns the -/// result of deployment. -evmc::result deployContract(EVMHost& _hostContext, bytes const& _code) -{ - evmc_message message = initializeMessage(_code); - message.kind = EVMC_CREATE; - return _hostContext.call(message); -} -} - DEFINE_PROTO_FUZZER(Contract const& _input) { string contract_source = ProtoConverter{}.contractToString(_input); @@ -147,7 +84,7 @@ DEFINE_PROTO_FUZZER(Contract const& _input) EVMHost hostContext(version, evmone); // Deploy contract and signal failure if deploy failed - evmc::result createResult = deployContract(hostContext, byteCode); + evmc::result createResult = AbiV2Utility::deployContract(hostContext, byteCode); solAssert( createResult.status_code == EVMC_SUCCESS, "Proto ABIv2 Fuzzer: Contract creation failed" @@ -155,7 +92,7 @@ DEFINE_PROTO_FUZZER(Contract const& _input) // Execute test function and signal failure if EVM reverted or // did not return expected output on successful execution. - evmc::result callResult = executeContract( + evmc::result callResult = AbiV2Utility::executeContract( hostContext, fromHex(hexEncodedInput), createResult.create_address @@ -165,7 +102,7 @@ DEFINE_PROTO_FUZZER(Contract const& _input) solAssert(callResult.status_code != EVMC_REVERT, "Proto ABIv2 fuzzer: EVM One reverted"); if (callResult.status_code == EVMC_SUCCESS) solAssert( - isOutputExpected(callResult.output_data, callResult.output_size), + AbiV2Utility::isOutputExpected(callResult.output_data, callResult.output_size, expectedOutput), "Proto ABIv2 fuzzer: ABIv2 coding failure found" ); } diff --git a/test/tools/ossfuzz/const_opt_ossfuzz.cpp b/test/tools/ossfuzz/const_opt_ossfuzz.cpp index 60b1febbc52c..0b799c956e6e 100644 --- a/test/tools/ossfuzz/const_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/const_opt_ossfuzz.cpp @@ -20,6 +20,9 @@ using namespace std; +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); + extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { if (_size <= 250) @@ -28,4 +31,4 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) FuzzerUtil::testConstantOptimizer(input, /*quiet=*/true); } return 0; -} \ No newline at end of file +} diff --git a/test/tools/ossfuzz/protoToAbiV2.cpp b/test/tools/ossfuzz/protoToAbiV2.cpp index 1c422ef8cbbf..47d3942bb040 100644 --- a/test/tools/ossfuzz/protoToAbiV2.cpp +++ b/test/tools/ossfuzz/protoToAbiV2.cpp @@ -1,10 +1,99 @@ #include +#include +#include + +/// Convenience macros +/// Returns a valid Solidity integer width w such that 8 <= w <= 256. +#define INTWIDTH(z, n, _ununsed) BOOST_PP_MUL(BOOST_PP_ADD(n, 1), 8) +/// Using declaration that aliases long boost multiprecision types with +/// s(u) where is a valid Solidity integer width and "s" +/// stands for "signed" and "u" for "unsigned". +#define USINGDECL(z, n, sign) \ + using BOOST_PP_CAT(BOOST_PP_IF(sign, s, u), INTWIDTH(z, n,)) = \ + boost::multiprecision::number< \ + boost::multiprecision::cpp_int_backend< \ + INTWIDTH(z, n,), \ + INTWIDTH(z, n,), \ + BOOST_PP_IF( \ + sign, \ + boost::multiprecision::signed_magnitude, \ + boost::multiprecision::unsigned_magnitude \ + ), \ + boost::multiprecision::unchecked, \ + void \ + > \ + >; +/// Instantiate the using declarations for signed and unsigned integer types. +BOOST_PP_REPEAT(32, USINGDECL, 1) +BOOST_PP_REPEAT(32, USINGDECL, 0) +/// Case implementation that returns an integer value of the specified type. +/// For signed integers, we divide by two because the range for boost multiprecision +/// types is double that of Solidity integer types. Example, 8-bit signed boost +/// number range is [-255, 255] but Solidity `int8` range is [-128, 127] +#define CASEIMPL(z, n, sign) \ + case INTWIDTH(z, n,): \ + stream << BOOST_PP_IF( \ + sign, \ + integerValue< \ + BOOST_PP_CAT( \ + BOOST_PP_IF(sign, s, u), \ + INTWIDTH(z, n,) \ + )>(_counter) / 2, \ + integerValue< \ + BOOST_PP_CAT( \ + BOOST_PP_IF(sign, s, u), \ + INTWIDTH(z, n,) \ + )>(_counter) \ + ); \ + break; +/// Switch implementation that instantiates case statements for (un)signed +/// Solidity integer types. +#define SWITCHIMPL(sign) \ + ostringstream stream; \ + switch (_intWidth) \ + { \ + BOOST_PP_REPEAT(32, CASEIMPL, sign) \ + } \ + return stream.str(); + using namespace std; -using namespace solidity; using namespace solidity::util; using namespace solidity::test::abiv2fuzzer; +namespace +{ +template +static V integerValue(unsigned _counter) +{ + V value = V( + u256(solidity::util::keccak256(solidity::util::h256(_counter))) % u256(boost::math::tools::max_value()) + ); + if (boost::multiprecision::is_signed_number::value && value % 2 == 0) + return value * (-1); + else + return value; +} + +static string signedIntegerValue(unsigned _counter, unsigned _intWidth) +{ + SWITCHIMPL(1) +} + +static string unsignedIntegerValue(unsigned _counter, unsigned _intWidth) +{ + SWITCHIMPL(0) +} + +static string integerValue(unsigned _counter, unsigned _intWidth, bool _signed) +{ + if (_signed) + return signedIntegerValue(_counter, _intWidth); + else + return unsignedIntegerValue(_counter, _intWidth); +} +} + string ProtoConverter::getVarDecl( string const& _type, string const& _varName, @@ -195,7 +284,10 @@ pair ProtoConverter::varDecl( typeStr, ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) ); - + appendToIsabelleTypeString( + tVisitor.isabelleTypeString(), + ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) + ); // Update dyn param only if necessary if (tVisitor.isLastDynParamRightPadded()) m_isLastDynParamRightPadded = true; @@ -224,6 +316,10 @@ pair ProtoConverter::assignChecker( m_counter += acVisitor.counted(); m_checks << assignCheckStrPair.second; + appendToIsabelleValueString( + acVisitor.isabelleValueString(), + ((m_varCounter == 1) ? Delimiter::SKIP : Delimiter::ADD) + ); // State variables cannot be assigned in contract-scope // Therefore, we buffer their assignments and @@ -242,12 +338,12 @@ std::string ProtoConverter::equalityChecksAsString() return m_checks.str(); } -std::string ProtoConverter::delimiterToString(Delimiter _delimiter) +std::string ProtoConverter::delimiterToString(Delimiter _delimiter, bool _space) { switch (_delimiter) { case Delimiter::ADD: - return ", "; + return _space ? ", " : ","; case Delimiter::SKIP: return ""; } @@ -352,6 +448,22 @@ void ProtoConverter::appendTypedParamsPublic( .render(); } +void ProtoConverter::appendToIsabelleTypeString( + std::string const& _typeString, + Delimiter _delimiter +) +{ + m_isabelleTypeString << delimiterToString(_delimiter, false) << _typeString; +} + +void ProtoConverter::appendToIsabelleValueString( + std::string const& _valueString, + Delimiter _delimiter +) +{ + m_isabelleValueString << delimiterToString(_delimiter, false) << _valueString; +} + std::string ProtoConverter::typedParametersAsString(CalleeType _calleeType) { switch (_calleeType) @@ -418,7 +530,7 @@ string ProtoConverter::visit(TestFunction const& _x, string const& _storageVarDe ("functionDeclReturndata", functionDeclReturndata) ("storageVarDefs", _storageVarDefs) ("localVarDefs", localVarDefs) - ("calldataTestCode", testCallDataFunction(_x.invalid_encoding_length())) + ("calldataTestCode", testCallDataFunction(static_cast(_x.invalid_encoding_length()))) ("returndataTestCode", testReturnDataFunction()) ("return_types", m_types.str()) ("return_values", m_argsCoder.str()) @@ -628,6 +740,24 @@ pragma experimental ABIEncoderV2;)"; .render(); } +string ProtoConverter::isabelleTypeString() const +{ + string typeString = m_isabelleTypeString.str(); + if (!typeString.empty()) + return "(" + typeString + ")"; + else + return typeString; +} + +string ProtoConverter::isabelleValueString() const +{ + string valueString = m_isabelleValueString.str(); + if (!valueString.empty()) + return "(" + valueString + ")"; + else + return valueString; +} + string ProtoConverter::contractToString(Contract const& _input) { visit(_input); @@ -635,27 +765,44 @@ string ProtoConverter::contractToString(Contract const& _input) } /// Type visitor +void TypeVisitor::StructTupleString::addTypeStringToTuple(string& _typeString) +{ + index++; + if (index > 1) + stream << ","; + stream << _typeString; +} + +void TypeVisitor::StructTupleString::addArrayBracketToType(string& _arrayBracket) +{ + stream << _arrayBracket; +} + string TypeVisitor::visit(BoolType const&) { m_baseType = "bool"; + m_structTupleString.addTypeStringToTuple(m_baseType); return m_baseType; } string TypeVisitor::visit(IntegerType const& _type) { m_baseType = getIntTypeAsString(_type); + m_structTupleString.addTypeStringToTuple(m_baseType); return m_baseType; } string TypeVisitor::visit(FixedByteType const& _type) { m_baseType = getFixedByteTypeAsString(_type); + m_structTupleString.addTypeStringToTuple(m_baseType); return m_baseType; } -string TypeVisitor::visit(AddressType const& _type) +string TypeVisitor::visit(AddressType const&) { - m_baseType = getAddressTypeAsString(_type); + m_baseType = "address"; + m_structTupleString.addTypeStringToTuple(m_baseType); return m_baseType; } @@ -666,12 +813,13 @@ string TypeVisitor::visit(ArrayType const& _type) string baseType = visit(_type.t()); solAssert(!baseType.empty(), ""); - string arrayBraces = _type.is_static() ? + string arrayBracket = _type.is_static() ? string("[") + to_string(getStaticArrayLengthFromFuzz(_type.length())) + string("]") : string("[]"); - m_baseType += arrayBraces; + m_baseType += arrayBracket; + m_structTupleString.addArrayBracketToType(arrayBracket); // If we don't know yet if the array will be dynamically encoded, // check again. If we already know that it will be, there's no @@ -679,13 +827,14 @@ string TypeVisitor::visit(ArrayType const& _type) if (!m_isLastDynParamRightPadded) m_isLastDynParamRightPadded = DynParamVisitor().visit(_type); - return baseType + arrayBraces; + return baseType + arrayBracket; } -string TypeVisitor::visit(DynamicByteArrayType const& _type) +string TypeVisitor::visit(DynamicByteArrayType const&) { m_isLastDynParamRightPadded = true; - m_baseType = bytesArrayTypeAsString(_type); + m_baseType = "bytes"; + m_structTupleString.addTypeStringToTuple(m_baseType); return m_baseType; } @@ -708,7 +857,8 @@ void TypeVisitor::structDefinition(StructType const& _type) to_string(m_structCounter) + " {" ); - + // Start tuple of types with parenthesis + m_structTupleString.start(); // Increase indentation for struct fields m_indentation++; for (auto const& t: _type.t()) @@ -731,9 +881,13 @@ void TypeVisitor::structDefinition(StructType const& _type) ("member", "m" + to_string(m_structFieldCounter++)) .render() ); + string isabelleTypeStr = tVisitor.isabelleTypeString(); + m_structTupleString.addTypeStringToTuple(isabelleTypeStr); } m_indentation--; structDef += lineString("}"); + // End tuple of types with parenthesis + m_structTupleString.end(); m_structCounter++; m_structDef << structDef; m_indentation = wasIndentation; @@ -758,34 +912,62 @@ string TypeVisitor::visit(StructType const& _type) } /// AssignCheckVisitor implementation +void AssignCheckVisitor::ValueStream::appendValue(string& _value) +{ + solAssert(!_value.empty(), "Abiv2 fuzzer: Empty value"); + index++; + if (index > 1) + stream << ","; + stream << _value; +} + pair AssignCheckVisitor::visit(BoolType const& _type) { string value = ValueGetterVisitor(counter()).visit(_type); + if (!m_forcedVisit) + m_valueStream.appendValue(value); return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); } pair AssignCheckVisitor::visit(IntegerType const& _type) { string value = ValueGetterVisitor(counter()).visit(_type); + if (!m_forcedVisit) + m_valueStream.appendValue(value); return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); } pair AssignCheckVisitor::visit(FixedByteType const& _type) { string value = ValueGetterVisitor(counter()).visit(_type); + if (!m_forcedVisit) + { + string isabelleValue = ValueGetterVisitor{}.isabelleBytesValueAsString(value); + m_valueStream.appendValue(isabelleValue); + } return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); } pair AssignCheckVisitor::visit(AddressType const& _type) { string value = ValueGetterVisitor(counter()).visit(_type); + if (!m_forcedVisit) + { + string isabelleValue = ValueGetterVisitor{}.isabelleAddressValueAsString(value); + m_valueStream.appendValue(isabelleValue); + } return assignAndCheckStringPair(m_varName, m_paramName, value, value, DataType::VALUE); } pair AssignCheckVisitor::visit(DynamicByteArrayType const& _type) { string value = ValueGetterVisitor(counter()).visit(_type); - DataType dataType = _type.type() == DynamicByteArrayType::BYTES ? DataType::BYTES : DataType::STRING; + if (!m_forcedVisit) + { + string isabelleValue = ValueGetterVisitor{}.isabelleBytesValueAsString(value); + m_valueStream.appendValue(isabelleValue); + } + DataType dataType = DataType::BYTES; return assignAndCheckStringPair(m_varName, m_paramName, value, value, dataType); } @@ -849,6 +1031,8 @@ pair AssignCheckVisitor::visit(ArrayType const& _type) pair assignCheckBuffer; string wasVarName = m_varName; string wasParamName = m_paramName; + if (!m_forcedVisit) + m_valueStream.startArray(); for (unsigned i = 0; i < length; i++) { m_varName = wasVarName + "[" + to_string(i) + "]"; @@ -859,12 +1043,18 @@ pair AssignCheckVisitor::visit(ArrayType const& _type) if (i < length - 1) m_structCounter = wasStructCounter; } - // Since struct visitor won't be called for zero-length // arrays, struct counter will not get incremented. Therefore, // we need to manually force a recursive struct visit. if (length == 0 && TypeVisitor().arrayOfStruct(_type)) + { + bool previousState = m_forcedVisit; + m_forcedVisit = true; visit(_type.t()); + m_forcedVisit = previousState; + } + if (!m_forcedVisit) + m_valueStream.endArray(); m_varName = wasVarName; m_paramName = wasParamName; @@ -890,6 +1080,8 @@ pair AssignCheckVisitor::visit(StructType const& _type) string wasVarName = m_varName; string wasParamName = m_paramName; + if (!m_forcedVisit) + m_valueStream.startStruct(); for (auto const& t: _type.t()) { m_varName = wasVarName + ".m" + to_string(i); @@ -903,6 +1095,8 @@ pair AssignCheckVisitor::visit(StructType const& _type) assignCheckBuffer.second += assign.second; i++; } + if (!m_forcedVisit) + m_valueStream.endStruct(); m_varName = wasVarName; m_paramName = wasParamName; return assignCheckBuffer; @@ -933,12 +1127,6 @@ string AssignCheckVisitor::checkString(string const& _ref, string const& _value, string checkPred; switch (_type) { - case DataType::STRING: - checkPred = Whiskers(R"(!bytesCompare(bytes(), ))") - ("varName", _ref) - ("value", _value) - .render(); - break; case DataType::BYTES: checkPred = Whiskers(R"(!bytesCompare(, ))") ("varName", _ref) @@ -969,11 +1157,7 @@ string ValueGetterVisitor::visit(BoolType const&) string ValueGetterVisitor::visit(IntegerType const& _type) { - return integerValueAsString( - _type.is_signed(), - getIntWidth(_type), - counter() - ); + return integerValue(counter(), getIntWidth(_type), _type.is_signed()); } string ValueGetterVisitor::visit(FixedByteType const& _type) @@ -989,56 +1173,14 @@ string ValueGetterVisitor::visit(AddressType const&) return addressValueAsString(counter()); } -string ValueGetterVisitor::visit(DynamicByteArrayType const& _type) +string ValueGetterVisitor::visit(DynamicByteArrayType const&) { return bytesArrayValueAsString( counter(), - getDataTypeOfDynBytesType(_type) == DataType::BYTES + true ); } -std::string ValueGetterVisitor::integerValueAsString(bool _sign, unsigned _width, unsigned _counter) -{ - if (_sign) - return intValueAsString(_width, _counter); - else - return uintValueAsString(_width, _counter); -} - -/* Input(s) - * - Unsigned integer to be hashed - * - Width of desired uint value - * Processing - * - Take hash of first parameter and mask it with the max unsigned value for given bit width - * Output - * - string representation of uint value - */ -std::string ValueGetterVisitor::uintValueAsString(unsigned _width, unsigned _counter) -{ - solAssert( - (_width % 8 == 0), - "Proto ABIv2 Fuzzer: Unsigned integer width is not a multiple of 8" - ); - return maskUnsignedIntToHex(_counter, _width/4); -} - -/* Input(s) - * - counter to be hashed to derive a value for Integer type - * - Width of desired int value - * Processing - * - Take hash of first parameter and mask it with the max signed value for given bit width - * Output - * - string representation of int value - */ -std::string ValueGetterVisitor::intValueAsString(unsigned _width, unsigned _counter) -{ - solAssert( - (_width % 8 == 0), - "Proto ABIv2 Fuzzer: Signed integer width is not a multiple of 8" - ); - return maskUnsignedIntToHex(_counter, ((_width/4) - 1)); -} - std::string ValueGetterVisitor::croppedString( unsigned _numBytes, unsigned _counter, @@ -1109,9 +1251,29 @@ std::string ValueGetterVisitor::fixedByteValueAsString(unsigned _width, unsigned std::string ValueGetterVisitor::addressValueAsString(unsigned _counter) { - return Whiskers(R"(address())") - ("value", uintValueAsString(160, _counter)) - .render(); + return "address(" + maskUnsignedIntToHex(_counter, 40) + ")"; +} + +std::string ValueGetterVisitor::isabelleAddressValueAsString(std::string& _solAddressString) +{ + // Isabelle encoder expects address literal to be exactly + // 20 bytes and a hex string. + // Example: 0x0102030405060708090a0102030405060708090a + std::regex const addressPattern("address\\((.*)\\)"); + std::smatch match; + solAssert(std::regex_match(_solAddressString, match, addressPattern), "Abiv2 fuzzer: Invalid address string"); + std::string addressHex = match[1].str(); + addressHex.erase(2, 24); + return addressHex; +} + +std::string ValueGetterVisitor::isabelleBytesValueAsString(std::string& _solBytesString) +{ + std::regex const bytesPattern("hex\"(.*)\""); + std::smatch match; + solAssert(std::regex_match(_solBytesString, match, bytesPattern), "Abiv2 fuzzer: Invalid bytes string"); + std::string bytesHex = match[1].str(); + return "0x" + bytesHex; } std::string ValueGetterVisitor::variableLengthValueAsString( @@ -1120,10 +1282,6 @@ std::string ValueGetterVisitor::variableLengthValueAsString( bool _isHexLiteral ) { - // TODO: Move this to caller -// solAssert(_numBytes >= 0 && _numBytes <= s_maxDynArrayLength, -// "Proto ABIv2 fuzzer: Invalid hex length" -// ); if (_numBytes == 0) return Whiskers(R"(hex"")") ("isHex", _isHexLiteral) diff --git a/test/tools/ossfuzz/protoToAbiV2.h b/test/tools/ossfuzz/protoToAbiV2.h index f75f5d7e4cc9..a2be558e8c6a 100644 --- a/test/tools/ossfuzz/protoToAbiV2.h +++ b/test/tools/ossfuzz/protoToAbiV2.h @@ -152,6 +152,12 @@ class ProtoConverter ProtoConverter(ProtoConverter const&) = delete; ProtoConverter(ProtoConverter&&) = delete; std::string contractToString(Contract const& _input); + std::string isabelleTypeString() const; + std::string isabelleValueString() const; + bool coderFunction() const + { + return m_test == Contract_Test::Contract_Test_CALLDATA_CODER; + } private: enum class Delimiter { @@ -287,6 +293,20 @@ class ProtoConverter Delimiter _delimiter ); + /// Append type name to type string meant to be + /// passed to Isabelle coder API. + void appendToIsabelleTypeString( + std::string const& _typeString, + Delimiter _delimiter + ); + + /// Append @a _valueString to value string meant to be + /// passed to Isabelle coder API. + void appendToIsabelleValueString( + std::string const& _valueString, + Delimiter _delimiter + ); + /// Returns a Solidity variable declaration statement /// @param _type: string containing Solidity type of the /// variable to be declared. @@ -359,7 +379,7 @@ class ProtoConverter } /// Convert delimter to a comma or null string. - static std::string delimiterToString(Delimiter _delimiter); + static std::string delimiterToString(Delimiter _delimiter, bool _space = true); /// Contains the test program std::ostringstream m_output; @@ -369,6 +389,11 @@ class ProtoConverter /// Contains typed parameter list to be passed to callee functions std::ostringstream m_typedParamsExternal; std::ostringstream m_typedParamsPublic; + /// Contains type string to be passed to Isabelle API + std::ostringstream m_isabelleTypeString; + /// Contains values to be encoded in the format accepted + /// by the Isabelle API. + std::ostringstream m_isabelleValueString; /// Contains type stream to be used in returndata coder function /// signature std::ostringstream m_types; @@ -427,7 +452,6 @@ class AbiV2ProtoVisitor enum class DataType { BYTES, - STRING, VALUE, ARRAY }; @@ -466,19 +490,6 @@ class AbiV2ProtoVisitor return "bytes" + std::to_string(getFixedByteWidth(_x)); } - static std::string getAddressTypeAsString(AddressType const& _x) - { - return (_x.payable() ? "address payable" : "address"); - } - - static DataType getDataTypeOfDynBytesType(DynamicByteArrayType const& _x) - { - if (_x.type() == DynamicByteArrayType::STRING) - return DataType::STRING; - else - return DataType::BYTES; - } - // Convert _counter to string and return its keccak256 hash static u256 hashUnsignedInt(unsigned _counter) { @@ -521,18 +532,13 @@ class AbiV2ProtoVisitor // this linear equation to make the number derived from // _counter approach a uniform distribution over // [0, s_maxDynArrayLength] - return (_counter + 879) * 32 % (s_maxDynArrayLength + 1); - } - - static std::string bytesArrayTypeAsString(DynamicByteArrayType const& _x) - { - switch (_x.type()) - { - case DynamicByteArrayType::BYTES: - return "bytes"; - case DynamicByteArrayType::STRING: - return "string"; - } + auto v = (_counter + 879) * 32 % (s_maxDynArrayLength + 1); + /// Always return an even number because Isabelle string + /// values are formatted as hex literals + if (v % 2 == 1) + return v + 1; + else + return v; } protected: T visitValueType(ValueType const& _type) @@ -630,7 +636,27 @@ class TypeVisitor: public AbiV2ProtoVisitor else return false; } + std::string isabelleTypeString() + { + return m_structTupleString.stream.str(); + } private: + struct StructTupleString + { + StructTupleString() = default; + unsigned index = 0; + std::ostringstream stream; + void start() + { + stream << "("; + } + void end() + { + stream << ")"; + } + void addTypeStringToTuple(std::string& _typeString); + void addArrayBracketToType(std::string& _arrayBracket); + }; void structDefinition(StructType const&); std::string indentation() @@ -641,9 +667,11 @@ class TypeVisitor: public AbiV2ProtoVisitor { return indentation() + _line + "\n"; } - std::string m_baseType; std::ostringstream m_structDef; + /// Utility type for conveniently composing a tuple + /// string for struct types. + StructTupleString m_structTupleString; unsigned m_indentation; unsigned m_structCounter; unsigned m_structStartCounter; @@ -699,7 +727,42 @@ class AssignCheckVisitor: public AbiV2ProtoVisitor= 1) + stream << ","; + index = 0; + stream << "("; + } + void endStruct() + { + stream << ")"; + } + void startArray() + { + if (index >= 1) + stream << ","; + index = 0; + stream << "["; + } + void endArray() + { + stream << "]"; + index++; + } + void appendValue(std::string& _value); + }; std::string indentation() { return std::string(m_indentation * 1, '\t'); @@ -728,6 +791,8 @@ class AssignCheckVisitor: public AbiV2ProtoVisitor solAssert(false, "ABIv2 proto fuzzer: Cannot call valuegettervisitor on complex type"); } using AbiV2ProtoVisitor::visit; + static std::string isabelleAddressValueAsString(std::string& _solAddressString); + static std::string isabelleBytesValueAsString(std::string& _solFixedBytesString); private: unsigned counter() { return m_counter++; } - static std::string intValueAsString(unsigned _width, unsigned _counter); - static std::string uintValueAsString(unsigned _width, unsigned _counter); - static std::string integerValueAsString(bool _sign, unsigned _width, unsigned _counter); static std::string addressValueAsString(unsigned _counter); static std::string fixedByteValueAsString(unsigned _width, unsigned _counter); diff --git a/test/tools/ossfuzz/protoToSol.h b/test/tools/ossfuzz/protoToSol.h index 39f830528c8e..b367acb0350d 100644 --- a/test/tools/ossfuzz/protoToSol.h +++ b/test/tools/ossfuzz/protoToSol.h @@ -37,7 +37,7 @@ struct SolRandomNumGenerator /// @returns a pseudo random unsigned integer unsigned operator()() { - return m_random(); + return static_cast(m_random()); } RandomEngine m_random; diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index aeb8fc665c62..beb74fddf2ca 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -182,9 +182,9 @@ bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type) unsigned ProtoConverter::numVarsInScope() { if (m_inFunctionDef) - return m_currentFuncVars.size(); + return static_cast(m_currentFuncVars.size()); else - return m_currentGlobalVars.size(); + return static_cast(m_currentGlobalVars.size()); } void ProtoConverter::visit(VarRef const& _x) @@ -1344,12 +1344,12 @@ void ProtoConverter::visit(UnaryOpData const& _x) { case UnaryOpData::SIZE: m_output << Whiskers(R"(datasize(""))") - ("id", getObjectIdentifier(_x.identifier())) + ("id", getObjectIdentifier(static_cast(_x.identifier()))) .render(); break; case UnaryOpData::OFFSET: m_output << Whiskers(R"(dataoffset(""))") - ("id", getObjectIdentifier(_x.identifier())) + ("id", getObjectIdentifier(static_cast(_x.identifier()))) .render(); break; } @@ -1473,7 +1473,7 @@ void ProtoConverter::openFunctionScope(vector const& _funcParams) void ProtoConverter::updateFunctionMaps(string const& _var) { - unsigned erased = m_functionSigMap.erase(_var); + size_t erased = m_functionSigMap.erase(_var); for (auto const& i: m_functionDefMap) if (i.second == _var) @@ -1491,7 +1491,7 @@ void ProtoConverter::closeBlockScope() // out of scope from the global function map. for (auto const& f: m_scopeFuncs.back()) { - unsigned numFuncsRemoved = m_functions.size(); + size_t numFuncsRemoved = m_functions.size(); m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end()); numFuncsRemoved -= m_functions.size(); yulAssert( @@ -1908,7 +1908,7 @@ void ProtoConverter::buildObjectScopeTree(Object const& _x) void ProtoConverter::visit(Program const& _x) { // Initialize input size - m_inputSize = _x.ByteSizeLong(); + m_inputSize = static_cast(_x.ByteSizeLong()); // Record EVM Version m_evmVersion = evmVersionMapping(_x.ver()); diff --git a/test/tools/ossfuzz/protomutators/YulProtoMutator.h b/test/tools/ossfuzz/protomutators/YulProtoMutator.h index 0627a34687fd..df1f460f2d32 100644 --- a/test/tools/ossfuzz/protomutators/YulProtoMutator.h +++ b/test/tools/ossfuzz/protomutators/YulProtoMutator.h @@ -38,7 +38,7 @@ struct YulRandomNumGenerator unsigned operator()() { - return m_random(); + return static_cast(m_random()); } RandomEngine m_random; diff --git a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp index 8d615c0bde4d..8d9e098ce47d 100644 --- a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp @@ -25,6 +25,9 @@ using namespace solidity::frontend::test; using namespace std; +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); + extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { if (_size <= 600) @@ -40,7 +43,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { return 0; } - FuzzerUtil::testCompiler(sourceCode, /*optimize=*/false, /*_rand=*/_size); + FuzzerUtil::testCompiler(sourceCode, /*optimize=*/false, /*_rand=*/static_cast(_size)); } return 0; } diff --git a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp index 0d9c7f44e754..78a0772bf91d 100644 --- a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp @@ -25,6 +25,9 @@ using namespace solidity::frontend::test; using namespace std; +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); + extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { if (_size <= 600) @@ -40,7 +43,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { return 0; } - FuzzerUtil::testCompiler(sourceCode, /*optimize=*/true, /*rand=*/_size); + FuzzerUtil::testCompiler(sourceCode, /*optimize=*/true, /*rand=*/static_cast(_size)); } return 0; } diff --git a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp index 458bde745996..e25de4f05636 100644 --- a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp @@ -24,6 +24,9 @@ using namespace solidity; using namespace solidity::yul; using namespace std; +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); + extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { if (_size > 600) diff --git a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp index bd6d249b3523..e9a0720af480 100644 --- a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -44,6 +43,9 @@ using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul::test::yul_fuzzer; +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); + extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { if (_size > 600) diff --git a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp index 646e6ea6a5a2..b1aa9a21fef2 100644 --- a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp @@ -24,6 +24,9 @@ using namespace solidity::util; using namespace solidity::yul; using namespace std; +// Prototype as we can't use the FuzzerInterface.h header. +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size); + extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) { if (_size > 600) diff --git a/test/tools/ossfuzz/yulFuzzerCommon.cpp b/test/tools/ossfuzz/yulFuzzerCommon.cpp index 86cea8327f97..d977c7761175 100644 --- a/test/tools/ossfuzz/yulFuzzerCommon.cpp +++ b/test/tools/ossfuzz/yulFuzzerCommon.cpp @@ -27,12 +27,14 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret( shared_ptr _ast, Dialect const& _dialect, size_t _maxSteps, - size_t _maxTraceSize + size_t _maxTraceSize, + size_t _maxExprNesting ) { InterpreterState state; state.maxTraceSize = _maxTraceSize; state.maxSteps = _maxSteps; + state.maxExprNesting = _maxExprNesting; // Add 64 bytes of pseudo-randomly generated calldata so that // calldata opcodes perform non trivial work. state.calldata = { @@ -59,6 +61,10 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret( { reason = TerminationReason::TraceLimitReached; } + catch (ExpressionNestingLimitReached const&) + { + reason = TerminationReason::ExpresionNestingLimitReached; + } catch (ExplicitlyTerminated const&) { reason = TerminationReason::ExplicitlyTerminated; diff --git a/test/tools/ossfuzz/yulFuzzerCommon.h b/test/tools/ossfuzz/yulFuzzerCommon.h index cc20d6163ab6..489cc4532237 100644 --- a/test/tools/ossfuzz/yulFuzzerCommon.h +++ b/test/tools/ossfuzz/yulFuzzerCommon.h @@ -28,6 +28,7 @@ struct yulFuzzerUtil ExplicitlyTerminated, StepLimitReached, TraceLimitReached, + ExpresionNestingLimitReached, None }; @@ -36,10 +37,12 @@ struct yulFuzzerUtil std::shared_ptr _ast, Dialect const& _dialect, size_t _maxSteps = maxSteps, - size_t _maxTraceSize = maxTraceSize + size_t _maxTraceSize = maxTraceSize, + size_t _maxExprNesting = maxExprNesting ); static size_t constexpr maxSteps = 100; static size_t constexpr maxTraceSize = 75; + static size_t constexpr maxExprNesting = 64; }; } diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index c95b4db7ff0f..63e554ea0667 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -44,7 +44,7 @@ namespace { void printErrors(ostream& _stream, ErrorList const& _errors) { - SourceReferenceFormatter formatter(_stream); + SourceReferenceFormatter formatter(_stream, false, false); for (auto const& error: _errors) formatter.printExceptionInformation( @@ -99,16 +99,23 @@ DEFINE_PROTO_FUZZER(Program const& _input) if ( termReason == yulFuzzerUtil::TerminationReason::StepLimitReached || - termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached + termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached || + termReason == yulFuzzerUtil::TerminationReason::ExpresionNestingLimitReached ) return; stack.optimize(); - yulFuzzerUtil::interpret( + termReason = yulFuzzerUtil::interpret( os2, stack.parserResult()->code, EVMDialect::strictAssemblyForEVMObjects(version) ); + if ( + termReason == yulFuzzerUtil::TerminationReason::StepLimitReached || + termReason == yulFuzzerUtil::TerminationReason::TraceLimitReached || + termReason == yulFuzzerUtil::TerminationReason::ExpresionNestingLimitReached + ) + return; bool isTraceEq = (os1.str() == os2.str()); yulAssert(isTraceEq, "Interpreted traces for optimized and unoptimized code differ."); diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 85b253fc4f23..6242cb2678a7 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include @@ -439,28 +439,47 @@ u256 EVMInstructionInterpreter::eval( return 0; } -u256 EVMInstructionInterpreter::evalBuiltin(BuiltinFunctionForEVM const& _fun, const std::vector& _arguments) +u256 EVMInstructionInterpreter::evalBuiltin( + BuiltinFunctionForEVM const& _fun, + vector const& _arguments, + vector const& _evaluatedArguments +) { if (_fun.instruction) - return eval(*_fun.instruction, _arguments); - else if (_fun.name == "datasize"_yulstring) - return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; - else if (_fun.name == "dataoffset"_yulstring) - return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; - else if (_fun.name == "datacopy"_yulstring) + return eval(*_fun.instruction, _evaluatedArguments); + + string fun = _fun.name.str(); + // Evaluate datasize/offset/copy instructions + if (fun == "datasize" || fun == "dataoffset") + { + string arg = std::get(_arguments.at(0)).value.str(); + if (arg.length() < 32) + arg.resize(32, 0); + if (fun == "datasize") + return u256(keccak256(arg)) & 0xfff; + else + { + // Force different value than for datasize + arg[31]++; + arg[31]++; + return u256(keccak256(arg)) & 0xfff; + } + } + else if (fun == "datacopy") { // This is identical to codecopy. - if (accessMemory(_arguments.at(0), _arguments.at(2))) + if (accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2))) copyZeroExtended( m_state.memory, m_state.code, - size_t(_arguments.at(0)), - size_t(_arguments.at(1) & numeric_limits::max()), - size_t(_arguments.at(2)) + size_t(_evaluatedArguments.at(0)), + size_t(_evaluatedArguments.at(1) & numeric_limits::max()), + size_t(_evaluatedArguments.at(2)) ); + return 0; } else - yulAssert(false, "Unknown builtin: " + _fun.name.str()); + yulAssert(false, "Unknown builtin: " + fun); return 0; } diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index 05d27df9616e..25f9f251989b 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include @@ -71,7 +71,11 @@ class EVMInstructionInterpreter /// Evaluate instruction u256 eval(evmasm::Instruction _instruction, std::vector const& _arguments); /// Evaluate builtin function - u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector const& _arguments); + u256 evalBuiltin( + BuiltinFunctionForEVM const& _fun, + std::vector const& _arguments, + std::vector const& _evaluatedArguments + ); private: /// Checks if the memory access is not too large for the interpreter and adjusts diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp index 61c7b4817c04..b2fc827e423b 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include @@ -35,6 +35,7 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::yul::test; +using solidity::util::h160; using solidity::util::h256; namespace @@ -114,34 +115,61 @@ uint64_t popcnt(uint64_t _v) } -using u512 = boost::multiprecision::number>; - -u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _arguments) +u256 EwasmBuiltinInterpreter::evalBuiltin( + YulString _functionName, + vector const& _arguments, + vector const& _evaluatedArguments +) { vector arg; - for (u256 const& a: _arguments) + for (u256 const& a: _evaluatedArguments) arg.emplace_back(uint64_t(a & uint64_t(-1))); - string fun = _fun.str(); - if (fun == "datasize") - return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; - else if (fun == "dataoffset") - return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; + string const fun = _functionName.str(); + if (fun == "datasize" || fun == "dataoffset") + { + string arg = std::get(_arguments.at(0)).value.str(); + if (arg.length() < 32) + arg.resize(32, 0); + if (fun == "datasize") + return u256(util::keccak256(arg)) & 0xfff; + else if (fun == "dataoffset") + { + // Force different value than for datasize + arg[31]++; + arg[31]++; + return u256(util::keccak256(arg)) & 0xfff; + } + } else if (fun == "datacopy") { // This is identical to codecopy. - if (accessMemory(_arguments.at(0), _arguments.at(2))) - copyZeroExtended( - m_state.memory, - m_state.code, - static_cast(_arguments.at(0)), - static_cast(_arguments.at(1) & numeric_limits::max()), - static_cast(_arguments.at(2)) - ); + accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)); + copyZeroExtended( + m_state.memory, + m_state.code, + static_cast(_evaluatedArguments.at(0)), + static_cast(_evaluatedArguments.at(1) & numeric_limits::max()), + static_cast(_evaluatedArguments.at(2)) + ); return 0; } else if (fun == "i32.drop" || fun == "i64.drop" || fun == "nop") return {}; + else if (fun == "i32.select") + { + if ((arg.at(2) & 0xffffffff) == 0) + return arg.at(1); + else + return arg.at(0); + } + else if (fun == "i64.select") + { + if ((arg.at(2) & 0xffffffffffffffff) == 0) + return arg.at(1); + else + return arg.at(0); + } else if (fun == "i32.wrap_i64") return arg.at(0) & uint32_t(-1); else if (fun == "i64.extend_i32_u") @@ -172,7 +200,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a else if (fun == "i32.store") { accessMemory(arg[0], 4); - writeMemoryHalfWord(arg[0], arg[1]); + writeMemoryHalfWord(arg[0], static_cast(arg[1])); return 0; } else if (fun == "i32.load") @@ -180,14 +208,14 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a accessMemory(arg[0], 4); return readMemoryHalfWord(arg[0]); } - else if (_fun == "i32.clz"_yulstring) + else if (fun == "i32.clz") // NOTE: the clz implementation assumes 64-bit inputs, hence the adjustment return clz64(arg[0] & uint32_t(-1)) - 32; - else if (_fun == "i64.clz"_yulstring) + else if (fun == "i64.clz") return clz64(arg[0]); - else if (_fun == "i32.ctz"_yulstring) + else if (fun == "i32.ctz") return ctz32(uint32_t(arg[0] & uint32_t(-1))); - else if (_fun == "i64.ctz"_yulstring) + else if (fun == "i64.ctz") return ctz64(arg[0]); string prefix = fun; @@ -211,7 +239,7 @@ u256 EwasmBuiltinInterpreter::evalBuiltin(YulString _fun, vector const& _a else if (prefix == "eth") return evalEthBuiltin(suffix, arg); - yulAssert(false, "Unknown builtin: " + _fun.str() + " (or implementation did not return)"); + yulAssert(false, "Unknown builtin: " + fun + " (or implementation did not return)"); return 0; } @@ -284,9 +312,7 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector m_state.calldata.size()) throw ExplicitlyTerminated(); - if (accessMemory(arg[0], arg[2])) - copyZeroExtended( - m_state.memory, m_state.calldata, - size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) - ); + accessMemory(arg[0], arg[2]); + copyZeroExtended( + m_state.memory, m_state.calldata, + size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) + ); return {}; } else if (_fun == "getCallDataSize") return m_state.calldata.size(); else if (_fun == "callCode") { - // TODO read args from memory - // TODO use readAddress to read address. + readAddress(arg[1]); + readU128(arg[2]); + accessMemory(arg[3], arg[4]); logTrace(evmasm::Instruction::CALLCODE, {}); return arg[0] & 1; } else if (_fun == "callDelegate") { - // TODO read args from memory - // TODO use readAddress to read address. + readAddress(arg[1]); + accessMemory(arg[2], arg[3]); logTrace(evmasm::Instruction::DELEGATECALL, {}); return arg[0] & 1; } else if (_fun == "callStatic") { - // TODO read args from memory - // TODO use readAddress to read address. + readAddress(arg[1]); + accessMemory(arg[2], arg[3]); logTrace(evmasm::Instruction::STATICCALL, {}); return arg[0] & 1; } else if (_fun == "storageStore") { - m_state.storage[h256(readU256(arg[0]))] = readU256((arg[1])); + m_state.storage[readBytes32(arg[0])] = readBytes32(arg[1]); return 0; } else if (_fun == "storageLoad") { - writeU256(arg[1], m_state.storage[h256(readU256(arg[0]))]); + writeBytes32(arg[1], m_state.storage[readBytes32(arg[0])]); return 0; } else if (_fun == "getCaller") { - // TODO should this only write 20 bytes? writeAddress(arg[0], m_state.caller); return 0; } @@ -364,11 +391,11 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector 4) throw ExplicitlyTerminated(); - logTrace(evmasm::logInstruction(numberOfTopics), {}); + if (numberOfTopics > 0) + readBytes32(arg[3]); + if (numberOfTopics > 1) + readBytes32(arg[4]); + if (numberOfTopics > 2) + readBytes32(arg[5]); + if (numberOfTopics > 3) + readBytes32(arg[6]); + logTrace(evmasm::logInstruction(static_cast(numberOfTopics)), {}); return 0; } else if (_fun == "getBlockNumber") @@ -431,16 +468,16 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector m_state.returndata.size()) throw ExplicitlyTerminated(); - if (accessMemory(arg[0], arg[2])) - copyZeroExtended( - m_state.memory, m_state.calldata, - size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) - ); + accessMemory(arg[0], arg[2]); + copyZeroExtended( + m_state.memory, m_state.calldata, + size_t(arg[0]), size_t(arg[1]), size_t(arg[2]) + ); return {}; } else if (_fun == "selfDestruct") { - // TODO use readAddress to read address. + readAddress(arg[0]); logTrace(evmasm::Instruction::SELFDESTRUCT, {}); throw ExplicitlyTerminated(); } @@ -471,18 +508,15 @@ u256 EwasmBuiltinInterpreter::evalEthBuiltin(string const& _fun, vector= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size))) - { - u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f); - m_state.msize = max(m_state.msize, newSize); - return _size <= 0xffff; - } - else - m_state.msize = u256(-1); + // Single WebAssembly page. + // TODO: Support expansion in this interpreter. + m_state.msize = 65536; - return false; + if (((_offset + _size) < _offset) || ((_offset + _size) > m_state.msize)) + // Ewasm throws out of bounds exception as opposed to the EVM. + throw ExplicitlyTerminated(); } bytes EwasmBuiltinInterpreter::readMemory(uint64_t _offset, uint64_t _size) @@ -506,10 +540,16 @@ uint32_t EwasmBuiltinInterpreter::readMemoryHalfWord(uint64_t _offset) { uint32_t r = 0; for (size_t i = 0; i < 4; i++) - r |= uint64_t(m_state.memory[_offset + i]) << (i * 8); + r |= uint32_t(m_state.memory[_offset + i]) << (i * 8); return r; } +void EwasmBuiltinInterpreter::writeMemory(uint64_t _offset, bytes const& _value) +{ + for (size_t i = 0; i < _value.size(); i++) + m_state.memory[_offset + i] = _value[i]; +} + void EwasmBuiltinInterpreter::writeMemoryWord(uint64_t _offset, uint64_t _value) { for (size_t i = 0; i < 8; i++) @@ -532,7 +572,7 @@ void EwasmBuiltinInterpreter::writeU256(uint64_t _offset, u256 _value, size_t _c accessMemory(_offset, _croppedTo); for (size_t i = 0; i < _croppedTo; i++) { - m_state.memory[_offset + _croppedTo - 1 - i] = uint8_t(_value & 0xff); + m_state.memory[_offset + i] = uint8_t(_value & 0xff); _value >>= 8; } } @@ -540,9 +580,9 @@ void EwasmBuiltinInterpreter::writeU256(uint64_t _offset, u256 _value, size_t _c u256 EwasmBuiltinInterpreter::readU256(uint64_t _offset, size_t _croppedTo) { accessMemory(_offset, _croppedTo); - u256 value; + u256 value{0}; for (size_t i = 0; i < _croppedTo; i++) - value = (value << 8) | m_state.memory[_offset + i]; + value = (value << 8) | m_state.memory[_offset + _croppedTo - 1 - i]; return value; } diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h index 78cf9216e0e7..1837f7509eb1 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.h @@ -21,9 +21,10 @@ #pragma once -#include +#include #include +#include #include @@ -61,6 +62,8 @@ struct InterpreterState; * * The main focus is that the generated execution trace is the same for equivalent executions * and likely to be different for non-equivalent executions. + * + * The type names are following the Ewasm specification (https://github.com/ewasm/design/blob/master/eth_interface.md). */ class EwasmBuiltinInterpreter { @@ -69,7 +72,11 @@ class EwasmBuiltinInterpreter m_state(_state) {} /// Evaluate builtin function - u256 evalBuiltin(YulString _fun, std::vector const& _arguments); + u256 evalBuiltin( + YulString _functionName, + std::vector const& _arguments, + std::vector const& _evaluatedArguments + ); private: template @@ -84,8 +91,7 @@ class EwasmBuiltinInterpreter /// Checks if the memory access is not too large for the interpreter and adjusts /// msize accordingly. - /// @returns false if the amount of bytes read is lager than 0xffff - bool accessMemory(u256 const& _offset, u256 const& _size = 32); + void accessMemory(u256 const& _offset, u256 const& _size = 32); /// @returns the memory contents at the provided address. /// Does not adjust msize, use @a accessMemory for that bytes readMemory(uint64_t _offset, uint64_t _size = 32); @@ -95,6 +101,9 @@ class EwasmBuiltinInterpreter /// @returns the memory contents (4 bytes) at the provided address (little-endian). /// Does not adjust msize, use @a accessMemory for that uint32_t readMemoryHalfWord(uint64_t _offset); + /// Writes bytes to memory. + /// Does not adjust msize, use @a accessMemory for that + void writeMemory(uint64_t _offset, bytes const& _value); /// Writes a word to memory (little-endian) /// Does not adjust msize, use @a accessMemory for that void writeMemoryWord(uint64_t _offset, uint64_t _value); @@ -105,14 +114,18 @@ class EwasmBuiltinInterpreter /// Does not adjust msize, use @a accessMemory for that void writeMemoryByte(uint64_t _offset, uint8_t _value); - /// Helper for eth.* builtins. Writes to memory (big-endian) and always returns zero. + /// Helper for eth.* builtins. Writes to memory (little-endian) and always returns zero. void writeU256(uint64_t _offset, u256 _value, size_t _croppedTo = 32); void writeU128(uint64_t _offset, u256 _value) { writeU256(_offset, std::move(_value), 16); } - void writeAddress(uint64_t _offset, u256 _value) { writeU256(_offset, std::move(_value), 20); } - /// Helper for eth.* builtins. Reads from memory (big-endian) and returns the value; + /// Helper for eth.* builtins. Writes to memory (as a byte string). + void writeBytes32(uint64_t _offset, util::h256 _value) { accessMemory(_offset, 32); writeMemory(_offset, _value.asBytes()); } + void writeAddress(uint64_t _offset, util::h160 _value) { accessMemory(_offset, 20); writeMemory(_offset, _value.asBytes()); } + /// Helper for eth.* builtins. Reads from memory (little-endian) and returns the value. u256 readU256(uint64_t _offset, size_t _croppedTo = 32); u256 readU128(uint64_t _offset) { return readU256(_offset, 16); } - u256 readAddress(uint64_t _offset) { return readU256(_offset, 20); } + /// Helper for eth.* builtins. Reads from memory (as a byte string). + util::h256 readBytes32(uint64_t _offset) { accessMemory(_offset, 32); return util::h256(readMemory(_offset, 32)); } + util::h160 readAddress(uint64_t _offset) { accessMemory(_offset, 20); return util::h160(readMemory(_offset, 20)); } void logTrace(evmasm::Instruction _instruction, std::vector const& _arguments = {}, bytes const& _data = {}); /// Appends a log to the trace representing an instruction or similar operation by string, diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp index a8496793fff1..df1aaaef30c1 100644 --- a/test/tools/yulInterpreter/Interpreter.cpp +++ b/test/tools/yulInterpreter/Interpreter.cpp @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include #include @@ -247,6 +247,7 @@ void Interpreter::incrementStep() void ExpressionEvaluator::operator()(Literal const& _literal) { + incrementStep(); static YulString const trueString("true"); static YulString const falseString("false"); @@ -256,19 +257,24 @@ void ExpressionEvaluator::operator()(Literal const& _literal) void ExpressionEvaluator::operator()(Identifier const& _identifier) { solAssert(m_variables.count(_identifier.name), ""); + incrementStep(); setValue(m_variables.at(_identifier.name)); } void ExpressionEvaluator::operator()(FunctionCall const& _funCall) { - evaluateArgs(_funCall.arguments); + vector> const* literalArguments = nullptr; + if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name)) + if (!builtin->literalArguments.empty()) + literalArguments = &builtin->literalArguments; + evaluateArgs(_funCall.arguments, literalArguments); if (EVMDialect const* dialect = dynamic_cast(&m_dialect)) { if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) { EVMInstructionInterpreter interpreter(m_state); - setValue(interpreter.evalBuiltin(*fun, values())); + setValue(interpreter.evalBuiltin(*fun, _funCall.arguments, values())); return; } } @@ -276,7 +282,7 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall) if (dialect->builtin(_funCall.functionName.name)) { EwasmBuiltinInterpreter interpreter(m_state); - setValue(interpreter.evalBuiltin(_funCall.functionName.name, values())); + setValue(interpreter.evalBuiltin(_funCall.functionName.name, _funCall.arguments, values())); return; } @@ -317,15 +323,34 @@ void ExpressionEvaluator::setValue(u256 _value) m_values.emplace_back(std::move(_value)); } -void ExpressionEvaluator::evaluateArgs(vector const& _expr) +void ExpressionEvaluator::evaluateArgs( + vector const& _expr, + vector> const* _literalArguments +) { + incrementStep(); vector values; + size_t i = 0; /// Function arguments are evaluated in reverse. for (auto const& expr: _expr | boost::adaptors::reversed) { - visit(expr); + if (!_literalArguments || !_literalArguments->at(_expr.size() - i - 1)) + visit(expr); + else + m_values = {0}; values.push_back(value()); + ++i; } m_values = std::move(values); std::reverse(m_values.begin(), m_values.end()); } + +void ExpressionEvaluator::incrementStep() +{ + m_nestingLevel++; + if (m_state.maxExprNesting > 0 && m_nestingLevel > m_state.maxExprNesting) + { + m_state.trace.emplace_back("Maximum expression nesting level reached."); + throw ExpressionNestingLimitReached(); + } +} diff --git a/test/tools/yulInterpreter/Interpreter.h b/test/tools/yulInterpreter/Interpreter.h index 00e02f6e222f..7af799bbd2b0 100644 --- a/test/tools/yulInterpreter/Interpreter.h +++ b/test/tools/yulInterpreter/Interpreter.h @@ -21,7 +21,7 @@ #pragma once -#include +#include #include #include @@ -55,6 +55,10 @@ class TraceLimitReached: public InterpreterTerminatedGeneric { }; +class ExpressionNestingLimitReached: public InterpreterTerminatedGeneric +{ +}; + enum class ControlFlowState { Default, @@ -92,6 +96,7 @@ struct InterpreterState size_t maxTraceSize = 0; size_t maxSteps = 0; size_t numSteps = 0; + size_t maxExprNesting = 0; ControlFlowState controlFlowState = ControlFlowState::Default; void dumpTraceAndState(std::ostream& _out) const; @@ -197,7 +202,15 @@ class ExpressionEvaluator: public ASTWalker /// Evaluates the given expression from right to left and /// stores it in m_value. - void evaluateArgs(std::vector const& _expr); + void evaluateArgs( + std::vector const& _expr, + std::vector> const* _literalArguments + ); + + /// Increment evaluation count, throwing exception if the + /// nesting level is beyond the upper bound configured in + /// the interpreter state. + void incrementStep(); InterpreterState& m_state; Dialect const& m_dialect; @@ -206,6 +219,8 @@ class ExpressionEvaluator: public ASTWalker Scope& m_scope; /// Current value of the expression std::vector m_values; + /// Current expression nesting level + unsigned m_nestingLevel = 0; }; } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index e4605d10f896..4a14e18e4260 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -20,12 +20,13 @@ */ #include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include @@ -62,7 +64,7 @@ class YulOpti public: void printErrors() { - SourceReferenceFormatter formatter(cerr); + SourceReferenceFormatter formatter(cerr, true, false); for (auto const& error: m_errors) formatter.printErrorInformation(*error); @@ -110,7 +112,7 @@ class YulOpti auto printPair = [&](auto const& optionAndDescription) { cout << optionAndDescription.first << ": "; - cout << setw(longestDescriptionLength) << setiosflags(ios::left); + cout << setw(static_cast(longestDescriptionLength)) << setiosflags(ios::left); cout << optionAndDescription.second << " "; ++index; @@ -157,14 +159,15 @@ class YulOpti map const& extraOptions = { {'#', "quit"}, {',', "VarNameCleaner"}, - {';', "StackCompressor"}, + {';', "StackCompressor"} }; printUsageBanner(abbreviationMap, extraOptions, 4); cout << "? "; cout.flush(); - int option = readStandardInputChar(); - cout << ' ' << char(option) << endl; + // TODO: handle EOF properly. + char option = static_cast(readStandardInputChar()); + cout << ' ' << option << endl; OptimiserStepContext context{m_dialect, *m_nameDispenser, reservedIdentifiers}; @@ -242,8 +245,18 @@ Allowed options)", } string input; + try + { + input = readFileAsString(arguments["input-file"].as()); + } + catch (FileNotFound const& _exception) + { + cerr << "File not found:" << _exception.comment() << endl; + return 1; + } + if (arguments.count("input-file")) - YulOpti{}.runInteractive(readFileAsString(arguments["input-file"].as())); + YulOpti{}.runInteractive(input); else cout << options; diff --git a/test/tools/yulrun.cpp b/test/tools/yulrun.cpp index e682e98bac85..3cc16c56fb8f 100644 --- a/test/tools/yulrun.cpp +++ b/test/tools/yulrun.cpp @@ -35,6 +35,7 @@ #include #include +#include #include @@ -57,7 +58,7 @@ namespace void printErrors(ErrorList const& _errors) { for (auto const& error: _errors) - SourceReferenceFormatter(cout).printErrorInformation(*error); + SourceReferenceFormatter(cout, true, false).printErrorInformation(*error); } pair, shared_ptr> parse(string const& _source) @@ -137,10 +138,19 @@ Allowed options)", else { string input; - if (arguments.count("input-file")) for (string path: arguments["input-file"].as>()) - input += readFileAsString(path); + { + try + { + input += readFileAsString(path); + } + catch (FileNotFound const&) + { + cerr << "File not found: " << path << endl; + return 1; + } + } else input = readStandardInput(); diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index ecf754e8e7d4..ac4c49055545 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -47,27 +47,22 @@ using namespace solidity::util; namespace solidity::phaser::test { +vector const ChrOmOsoMeSteps{ + ConditionalSimplifier::name, + FunctionHoister::name, + RedundantAssignEliminator::name, + ForLoopConditionOutOfBody::name, + Rematerialiser::name, + ForLoopConditionOutOfBody::name, + ExpressionSimplifier::name, + ForLoopInitRewriter::name, + LoopInvariantCodeMotion::name, + ExpressionInliner::name +}; + BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions")) BOOST_AUTO_TEST_SUITE(ChromosomeTest) -BOOST_AUTO_TEST_CASE(constructor_should_convert_from_string_to_optimisation_steps) -{ - vector expectedSteps{ - ConditionalSimplifier::name, - FunctionHoister::name, - RedundantAssignEliminator::name, - ForLoopConditionOutOfBody::name, - Rematerialiser::name, - ForLoopConditionOutOfBody::name, - ExpressionSimplifier::name, - ForLoopInitRewriter::name, - LoopInvariantCodeMotion::name, - ExpressionInliner::name - }; - - BOOST_TEST(Chromosome("ChrOmOsoMe").optimisationSteps() == expectedSteps); -} - BOOST_AUTO_TEST_CASE(makeRandom_should_return_different_chromosome_each_time) { SimulationRNG::reset(1); @@ -78,8 +73,8 @@ BOOST_AUTO_TEST_CASE(makeRandom_should_return_different_chromosome_each_time) BOOST_AUTO_TEST_CASE(makeRandom_should_use_every_possible_step_with_the_same_probability) { SimulationRNG::reset(1); - constexpr int samplesPerStep = 100; - constexpr double relativeTolerance = 0.01; + constexpr int samplesPerStep = 500; + constexpr double relativeTolerance = 0.02; map stepIndices = enumerateOptmisationSteps(); auto chromosome = Chromosome::makeRandom(stepIndices.size() * samplesPerStep); @@ -88,13 +83,18 @@ BOOST_AUTO_TEST_CASE(makeRandom_should_use_every_possible_step_with_the_same_pro for (auto& step: chromosome.optimisationSteps()) samples.push_back(stepIndices.at(step)); - const double expectedValue = (stepIndices.size() - 1) / 2.0; - const double variance = (stepIndices.size() * stepIndices.size() - 1) / 12.0; + const double expectedValue = double(stepIndices.size() - 1) / 2.0; + const double variance = double(stepIndices.size() * stepIndices.size() - 1) / 12.0; BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } +BOOST_AUTO_TEST_CASE(constructor_should_store_genes) +{ + BOOST_TEST(Chromosome("ChrOmOsoMe").genes() == "ChrOmOsoMe"); +} + BOOST_AUTO_TEST_CASE(constructor_should_store_optimisation_steps) { vector steps = { @@ -102,9 +102,8 @@ BOOST_AUTO_TEST_CASE(constructor_should_store_optimisation_steps) BlockFlattener::name, UnusedPruner::name, }; - Chromosome chromosome(steps); - BOOST_TEST(steps == chromosome.optimisationSteps()); + BOOST_TEST(Chromosome(steps).genes() == "tfu"); } BOOST_AUTO_TEST_CASE(constructor_should_allow_duplicate_steps) @@ -116,9 +115,18 @@ BOOST_AUTO_TEST_CASE(constructor_should_allow_duplicate_steps) UnusedPruner::name, BlockFlattener::name, }; - Chromosome chromosome(steps); - BOOST_TEST(steps == chromosome.optimisationSteps()); + BOOST_TEST(Chromosome(steps).genes() == "ttfuf"); + BOOST_TEST(Chromosome("ttfuf").genes() == "ttfuf"); +} + +BOOST_AUTO_TEST_CASE(constructor_should_allow_genes_that_do_not_correspond_to_any_step) +{ + assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0); + assert(OptimiserSuite::stepAbbreviationToNameMap().count('b') == 0); + + BOOST_TEST(Chromosome(".").genes() == "."); + BOOST_TEST(Chromosome("a..abatbb").genes() == "a..abatbb"); } BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_string_representation) @@ -130,27 +138,44 @@ BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_strin BOOST_TEST(chromosome.length() == allSteps.size()); BOOST_TEST(chromosome.optimisationSteps() == allSteps); - BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoighTLMrmVatud"); + BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoighTLMRrmVatpud"); +} + +BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names) +{ + BOOST_TEST(Chromosome("ChrOmOsoMe").optimisationSteps() == ChrOmOsoMeSteps); } BOOST_AUTO_TEST_CASE(randomOptimisationStep_should_return_each_step_with_same_probability) { SimulationRNG::reset(1); - constexpr int samplesPerStep = 100; - constexpr double relativeTolerance = 0.01; + constexpr int samplesPerStep = 500; + constexpr double relativeTolerance = 0.02; map stepIndices = enumerateOptmisationSteps(); vector samples; for (size_t i = 0; i <= stepIndices.size() * samplesPerStep; ++i) samples.push_back(stepIndices.at(Chromosome::randomOptimisationStep())); - const double expectedValue = (stepIndices.size() - 1) / 2.0; - const double variance = (stepIndices.size() * stepIndices.size() - 1) / 12.0; + const double expectedValue = double(stepIndices.size() - 1) / 2.0; + const double variance = double(stepIndices.size() * stepIndices.size() - 1) / 12.0; BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } +BOOST_AUTO_TEST_CASE(stepsToGenes_should_translate_optimisation_step_names_to_abbreviations) +{ + BOOST_TEST(Chromosome::stepsToGenes({}) == ""); + BOOST_TEST(Chromosome::stepsToGenes(ChrOmOsoMeSteps) == "ChrOmOsoMe"); +} + +BOOST_AUTO_TEST_CASE(genesToSteps_should_translate_optimisation_step_abbreviations_to_names) +{ + BOOST_TEST(Chromosome::genesToSteps("") == vector{}); + BOOST_TEST(Chromosome::genesToSteps("ChrOmOsoMe") == ChrOmOsoMeSteps); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index 7c1ae2d96b33..bd063bf1f447 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -183,7 +183,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised { BOOST_TEST( RelativeProgramSize(m_program, nullptr, 3, m_weights).evaluate(m_chromosome) == - round(1000.0 * m_optimisedProgram.codeSize(m_weights) / m_program.codeSize(m_weights)) + round(1000.0 * double(m_optimisedProgram.codeSize(m_weights)) / double(m_program.codeSize(m_weights))) ); } @@ -191,7 +191,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_availabl { BOOST_TEST( RelativeProgramSize(nullopt, m_programCache, 3, m_weights).evaluate(m_chromosome) == - round(1000.0 * m_optimisedProgram.codeSize(m_weights) / m_program.codeSize(m_weights)) + round(1000.0 * double(m_optimisedProgram.codeSize(m_weights)) / double(m_program.codeSize(m_weights))) ); BOOST_TEST(m_programCache->size() == m_chromosome.length()); } @@ -206,7 +206,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number BOOST_TEST(fitness != 1000); BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, nullptr, 3, m_weights, 1).evaluate(m_chromosome)); - BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize(m_weights) / m_program.codeSize(m_weights))); + BOOST_TEST(fitness == round(1000.0 * double(programOptimisedTwice.codeSize(m_weights)) / double(m_program.codeSize(m_weights)))); } BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) @@ -230,7 +230,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_ BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture) { - double sizeRatio = static_cast(m_optimisedProgram.codeSize(m_weights)) / m_program.codeSize(m_weights); + double sizeRatio = double(m_optimisedProgram.codeSize(m_weights)) / double(m_program.codeSize(m_weights)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 0, m_weights).evaluate(m_chromosome) == round(1.0 * sizeRatio)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1, m_weights).evaluate(m_chromosome) == round(10.0 * sizeRatio)); BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2, m_weights).evaluate(m_chromosome) == round(100.0 * sizeRatio)); diff --git a/test/yulPhaser/GeneticAlgorithms.cpp b/test/yulPhaser/GeneticAlgorithms.cpp index cfac86db04f7..16d5cc654b22 100644 --- a/test/yulPhaser/GeneticAlgorithms.cpp +++ b/test/yulPhaser/GeneticAlgorithms.cpp @@ -213,8 +213,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ClassicGeneticAlgorithmTest) -// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable. -BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_proportional_to_fitness, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled()) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_proportional_to_fitness, ClassicGeneticAlgorithmFixture) { constexpr double relativeTolerance = 0.1; constexpr size_t populationSize = 1000; @@ -255,8 +254,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_ BOOST_TEST(abs(meanSquaredError(newFitness, expectedValue) - variance) < variance * relativeTolerance); } -// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable. -BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_the_original_population, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled()) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_the_original_population, ClassicGeneticAlgorithmFixture) { constexpr size_t populationSize = 1000; auto population = Population::makeRandom(m_fitnessMetric, populationSize, 1, 10); @@ -300,8 +298,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_crossover, ClassicGeneticAlgorith BOOST_TEST(totalCrossed >= 2); } -// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable. -BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled()) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithmFixture) { m_options.mutationChance = 0.6; ClassicGeneticAlgorithm algorithm(m_options); @@ -330,8 +327,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithm BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } -// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable. -BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled()) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithmFixture) { m_options.deletionChance = 0.6; ClassicGeneticAlgorithm algorithm(m_options); @@ -360,8 +356,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithm BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } -// FIXME: This test runs *very* slowly (tens of seconds). Investigate, fix and re-enable. -BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithmFixture, *boost::unit_test::disabled()) +BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithmFixture) { m_options.additionChance = 0.6; ClassicGeneticAlgorithm algorithm(m_options); diff --git a/test/yulPhaser/Mutations.cpp b/test/yulPhaser/Mutations.cpp index dfdd600a3f3c..c42ff4e35ca7 100644 --- a/test/yulPhaser/Mutations.cpp +++ b/test/yulPhaser/Mutations.cpp @@ -22,14 +22,17 @@ #include #include +#include #include +#include #include #include using namespace std; using namespace solidity::util; +using namespace solidity::yul; namespace solidity::phaser::test { @@ -40,19 +43,32 @@ BOOST_AUTO_TEST_SUITE(GeneRandomisationTest) BOOST_AUTO_TEST_CASE(geneRandomisation_should_iterate_over_genes_and_replace_them_with_random_ones_with_given_probability) { - Chromosome chromosome("fcCUnDvejs"); - function mutation01 = geneRandomisation(0.1); - function mutation05 = geneRandomisation(0.5); - function mutation10 = geneRandomisation(1.0); + size_t constexpr inputLength = 1000; + double constexpr tolerance = 0.05; + + // Use genes that do not represent valid step abbreviations to be able to easily spot added steps. + assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0); + Chromosome input = Chromosome(string(inputLength, '.')); SimulationRNG::reset(1); - BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 2); - BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 5); - BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 7); - SimulationRNG::reset(2); - BOOST_TEST(countDifferences(mutation01(chromosome), chromosome), 1); - BOOST_TEST(countDifferences(mutation05(chromosome), chromosome), 3); - BOOST_TEST(countDifferences(mutation10(chromosome), chromosome), 9); + for (size_t randomisationChancePercent = 20; randomisationChancePercent <= 100; randomisationChancePercent += 20) + { + double const randomisationChance = double(randomisationChancePercent) / 100.0; + + Chromosome output = geneRandomisation(randomisationChance)(input); + string outputGenes = output.genes(); + BOOST_REQUIRE(output.length() == input.length()); + + double const expectedValue = randomisationChance; + double const variance = randomisationChance * (1 - randomisationChance); + double const randomisedGeneCount = double(input.length() - static_cast(count(outputGenes.begin(), outputGenes.end(), '.'))); + double const squaredError = + (inputLength - randomisedGeneCount) * expectedValue * expectedValue + + randomisedGeneCount * (1 - expectedValue) * (1 - expectedValue); + + BOOST_TEST(abs(randomisedGeneCount / inputLength - expectedValue) < tolerance); + BOOST_TEST(abs(squaredError / inputLength - variance) < tolerance); + } } BOOST_AUTO_TEST_CASE(geneRandomisation_should_return_identical_chromosome_if_probability_is_zero) @@ -65,17 +81,33 @@ BOOST_AUTO_TEST_CASE(geneRandomisation_should_return_identical_chromosome_if_pro BOOST_AUTO_TEST_CASE(geneDeletion_should_iterate_over_genes_and_delete_them_with_given_probability) { - Chromosome chromosome("fcCUnDvejs"); - function mutation01 = geneDeletion(0.1); - function mutation05 = geneDeletion(0.5); + size_t constexpr inputLength = 1000; + double constexpr tolerance = 0.05; + + // Use genes that do not represent valid step abbreviations to be able to easily spot added steps. + assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0); + Chromosome input = Chromosome(string(inputLength, '.')); SimulationRNG::reset(1); - // fcCUnDvejs - BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcCU Dvejs"))); - BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" D ejs"))); - SimulationRNG::reset(2); - BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace("fcUnDvejs"))); - BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace(" Un s"))); + for (size_t deletionChancePercent = 20; deletionChancePercent < 100; deletionChancePercent += 20) + { + double const deletionChance = double(deletionChancePercent) / 100.0; + + Chromosome output = geneDeletion(deletionChance)(input); + string outputGenes = output.genes(); + BOOST_REQUIRE(output.length() <= input.length()); + BOOST_REQUIRE(static_cast(count(outputGenes.begin(), outputGenes.end(), '.')) == output.length()); + + double const expectedValue = deletionChance; + double const variance = deletionChance * (1.0 - deletionChance); + double const deletedGeneCount = double(input.length() - output.length()); + double const squaredError = + (double(inputLength) - deletedGeneCount) * expectedValue * expectedValue + + deletedGeneCount * (1.0 - expectedValue) * (1.0 - expectedValue); + + BOOST_TEST(abs(deletedGeneCount / double(inputLength) - expectedValue) < tolerance); + BOOST_TEST(abs(squaredError / double(inputLength) - variance) < tolerance); + } } BOOST_AUTO_TEST_CASE(geneDeletion_should_return_identical_chromosome_if_probability_is_zero) @@ -96,17 +128,37 @@ BOOST_AUTO_TEST_CASE(geneDeletion_should_delete_all_genes_if_probability_is_one) BOOST_AUTO_TEST_CASE(geneAddition_should_iterate_over_gene_positions_and_insert_new_genes_with_given_probability) { - Chromosome chromosome("fcCUnDvejs"); - function mutation01 = geneAddition(0.1); - function mutation05 = geneAddition(0.5); + size_t constexpr inputLength = 1000; + double constexpr tolerance = 0.05; + size_t constexpr maxAdditions = inputLength + 1; + + // Use genes that do not represent valid step abbreviations to be able to easily spot added steps. + assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0); + Chromosome input = Chromosome(string(inputLength, '.')); SimulationRNG::reset(1); - // f c C U n D v e j s - BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f c C UC n D v e jx s"))); // 20% more - BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("j f cu C U ne D v eI j sf"))); // 50% more - SimulationRNG::reset(2); - BOOST_TEST(mutation01(chromosome) == Chromosome(stripWhitespace(" f cu C U n D v e j s"))); // 10% more - BOOST_TEST(mutation05(chromosome) == Chromosome(stripWhitespace("L f ce Cv U n D v e jO s"))); // 40% more + for (size_t additionChancePercent = 20; additionChancePercent < 100; additionChancePercent += 20) + { + double const additionChance = double(additionChancePercent) / 100.0; + + Chromosome output = geneAddition(additionChance)(input); + BOOST_REQUIRE(output.length() >= input.length()); + BOOST_REQUIRE(output.length() <= inputLength + maxAdditions); + + string_view outputGenes = output.genes(); + size_t preservedGeneCount = static_cast(count(outputGenes.begin(), outputGenes.end(), '.')); + BOOST_REQUIRE(preservedGeneCount == input.length()); + + double const expectedValue = additionChance; + double const variance = additionChance * (1.0 - additionChance); + double const addedGeneCount = double(output.length() - preservedGeneCount); + double const squaredError = + (double(maxAdditions) - addedGeneCount) * expectedValue * expectedValue + + addedGeneCount * (1.0 - expectedValue) * (1.0 - expectedValue); + + BOOST_TEST(abs(addedGeneCount / double(maxAdditions) - expectedValue) < tolerance); + BOOST_TEST(abs(squaredError / double(maxAdditions) - variance) < tolerance); + } } BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position) @@ -117,12 +169,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position Chromosome mutatedChromosome = mutation(chromosome); BOOST_TEST(mutatedChromosome.length() > chromosome.length()); - - vector suffix( - mutatedChromosome.optimisationSteps().end() - static_cast(chromosome.length()), - mutatedChromosome.optimisationSteps().end() - ); - BOOST_TEST(suffix == chromosome.optimisationSteps()); + BOOST_TEST(boost::ends_with(mutatedChromosome.genes(), chromosome.genes())); } BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position) @@ -133,12 +180,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position) Chromosome mutatedChromosome = mutation(chromosome); BOOST_TEST(mutatedChromosome.length() > chromosome.length()); - - vector prefix( - mutatedChromosome.optimisationSteps().begin(), - mutatedChromosome.optimisationSteps().begin() + static_cast(chromosome.length()) - ); - BOOST_TEST(prefix == chromosome.optimisationSteps()); + BOOST_TEST(boost::starts_with(mutatedChromosome.genes(), chromosome.genes())); } BOOST_AUTO_TEST_CASE(geneAddition_should_return_identical_chromosome_if_probability_is_zero) @@ -218,10 +260,11 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_i BOOST_AUTO_TEST_CASE(mutationSequence_should_apply_all_mutations) { Chromosome chromosome("aaaaa"); + vector steps = Chromosome::genesToSteps("gfc"); function mutation = mutationSequence({ - geneSubstitution(3, Chromosome("g").optimisationSteps()[0]), - geneSubstitution(2, Chromosome("f").optimisationSteps()[0]), - geneSubstitution(1, Chromosome("c").optimisationSteps()[0]), + geneSubstitution(3, steps[0]), + geneSubstitution(2, steps[1]), + geneSubstitution(1, steps[2]), }); BOOST_TEST(mutation(chromosome) == Chromosome("acfga")); @@ -230,11 +273,12 @@ BOOST_AUTO_TEST_CASE(mutationSequence_should_apply_all_mutations) BOOST_AUTO_TEST_CASE(mutationSequence_apply_mutations_in_the_order_they_are_given) { Chromosome chromosome("aa"); + vector steps = Chromosome::genesToSteps("gcfo"); function mutation = mutationSequence({ - geneSubstitution(0, Chromosome("g").optimisationSteps()[0]), - geneSubstitution(1, Chromosome("c").optimisationSteps()[0]), - geneSubstitution(0, Chromosome("f").optimisationSteps()[0]), - geneSubstitution(1, Chromosome("o").optimisationSteps()[0]), + geneSubstitution(0, steps[0]), + geneSubstitution(1, steps[1]), + geneSubstitution(0, steps[2]), + geneSubstitution(1, steps[3]), }); BOOST_TEST(mutation(chromosome) == Chromosome("fo")); diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index 673c43824a75..013f3a9e0a4e 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -451,7 +451,7 @@ BOOST_AUTO_TEST_CASE(build_should_apply_prefix) CharStream nestedSource("{{{let x:= 1}}}", ""); Program nestedProgram = get(Program::load(nestedSource)); Program flatProgram = get(Program::load(nestedSource)); - flatProgram.optimise(Chromosome("f").optimisationSteps()); + flatProgram.optimise(Chromosome::genesToSteps("f")); assert(toString(nestedProgram) != toString(flatProgram)); { diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 955ccc49bd65..13d06b521030 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -194,8 +194,8 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_return_population_with_random_chromoso for (auto& step: individual.chromosome.optimisationSteps()) samples.push_back(stepIndices.at(step)); - const double expectedValue = (stepIndices.size() - 1) / 2.0; - const double variance = (stepIndices.size() * stepIndices.size() - 1) / 12.0; + const double expectedValue = double(stepIndices.size() - 1) / 2.0; + const double variance = double(stepIndices.size() * stepIndices.size() - 1) / 12.0; BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp index b7dd1f8edce9..8aa2479a21d2 100644 --- a/test/yulPhaser/Program.cpp +++ b/test/yulPhaser/Program.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/test/yulPhaser/ProgramCache.cpp b/test/yulPhaser/ProgramCache.cpp index 7fb74fca9ef6..3a84aad67953 100644 --- a/test/yulPhaser/ProgramCache.cpp +++ b/test/yulPhaser/ProgramCache.cpp @@ -53,7 +53,7 @@ class ProgramCacheFixture Program optimisedProgram(Program _program, string _abbreviatedOptimisationSteps) const { Program result = move(_program); - result.optimise(Chromosome(_abbreviatedOptimisationSteps).optimisationSteps()); + result.optimise(Chromosome::genesToSteps(_abbreviatedOptimisationSteps)); return result; } diff --git a/test/yulPhaser/Selections.cpp b/test/yulPhaser/Selections.cpp index e5ef00327c82..28c8b8a879dc 100644 --- a/test/yulPhaser/Selections.cpp +++ b/test/yulPhaser/Selections.cpp @@ -218,7 +218,7 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabil vector bernoulliTrials(collectionSize); for (size_t i = 0; i < collectionSize; ++i) - bernoulliTrials[i] = indices.count(i); + bernoulliTrials[i] = double(indices.count(i)); BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index 36a04612e310..d6e8c3a81150 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(uniformInt_returns_different_values_when_called_multiple_ti constexpr double expectedValue = (minValue + maxValue) / 2.0; constexpr double variance = ((maxValue - minValue + 1) * (maxValue - minValue + 1) - 1) / 12.0; - vector samples; + vector samples; for (uint32_t i = 0; i < numSamples; ++i) samples.push_back(SimulationRNG::uniformInt(minValue, maxValue)); @@ -110,21 +110,21 @@ BOOST_AUTO_TEST_CASE(uniformInt_can_be_reset) constexpr uint32_t maxValue = 80; SimulationRNG::reset(1); - vector samples1; + vector samples1; for (uint32_t i = 0; i < numSamples; ++i) samples1.push_back(SimulationRNG::uniformInt(minValue, maxValue)); - vector samples2; + vector samples2; for (uint32_t i = 0; i < numSamples; ++i) samples2.push_back(SimulationRNG::uniformInt(minValue, maxValue)); SimulationRNG::reset(1); - vector samples3; + vector samples3; for (uint32_t i = 0; i < numSamples; ++i) samples3.push_back(SimulationRNG::uniformInt(minValue, maxValue)); SimulationRNG::reset(2); - vector samples4; + vector samples4; for (uint32_t i = 0; i < numSamples; ++i) samples4.push_back(SimulationRNG::uniformInt(minValue, maxValue)); @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(binomialInt_should_produce_samples_with_right_expected_valu constexpr double expectedValue = numTrials * successProbability; constexpr double variance = numTrials * successProbability * (1 - successProbability); - vector samples; + vector samples; for (uint32_t i = 0; i < numSamples; ++i) samples.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); @@ -163,21 +163,21 @@ BOOST_AUTO_TEST_CASE(binomialInt_can_be_reset) constexpr double successProbability = 0.6; SimulationRNG::reset(1); - vector samples1; + vector samples1; for (uint32_t i = 0; i < numSamples; ++i) samples1.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); - vector samples2; + vector samples2; for (uint32_t i = 0; i < numSamples; ++i) samples2.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); SimulationRNG::reset(1); - vector samples3; + vector samples3; for (uint32_t i = 0; i < numSamples; ++i) samples3.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); SimulationRNG::reset(2); - vector samples4; + vector samples4; for (uint32_t i = 0; i < numSamples; ++i) samples4.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); diff --git a/test/yulPhaser/TestHelpers.h b/test/yulPhaser/TestHelpers.h index 732e4ec7a71d..12748a6a1061 100644 --- a/test/yulPhaser/TestHelpers.h +++ b/test/yulPhaser/TestHelpers.h @@ -158,9 +158,9 @@ double mean(std::vector const& _samples) double sum = 0; for (T const& sample: _samples) - sum += static_cast(sample); + sum += double(sample); - return sum / _samples.size(); + return sum / double(_samples.size()); } /// Calculates the sum of squared differences between @a _expectedValue and the values of a series @@ -179,9 +179,9 @@ double meanSquaredError(std::vector const& _samples, double _expectedValue) double sumOfSquaredDifferences = 0; for (T const& sample: _samples) - sumOfSquaredDifferences += (sample - _expectedValue) * (sample - _expectedValue); + sumOfSquaredDifferences += (double(sample) - _expectedValue) * (double(sample) - _expectedValue); - return sumOfSquaredDifferences / _samples.size(); + return sumOfSquaredDifferences / double(_samples.size()); } } diff --git a/tools/solidityUpgrade/SourceUpgrade.cpp b/tools/solidityUpgrade/SourceUpgrade.cpp index dbd974f1f303..540a042a757a 100644 --- a/tools/solidityUpgrade/SourceUpgrade.cpp +++ b/tools/solidityUpgrade/SourceUpgrade.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include @@ -397,7 +397,7 @@ void SourceUpgrade::applyChange( void SourceUpgrade::printErrors() const { - auto formatter = make_unique(cout, true, false); + auto formatter = make_unique(cout, true, false); for (auto const& error: m_compiler->errors()) if (error->type() != langutil::Error::Type::Warning) diff --git a/tools/solidityUpgrade/Upgrade050.cpp b/tools/solidityUpgrade/Upgrade050.cpp index 8e1a2f0273ff..9c0ecd65672e 100644 --- a/tools/solidityUpgrade/Upgrade050.cpp +++ b/tools/solidityUpgrade/Upgrade050.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include diff --git a/tools/solidityUpgrade/Upgrade060.cpp b/tools/solidityUpgrade/Upgrade060.cpp index 58aa94d15fbd..c614f89d0e42 100644 --- a/tools/solidityUpgrade/Upgrade060.cpp +++ b/tools/solidityUpgrade/Upgrade060.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include @@ -100,7 +100,7 @@ inline string appendVirtual(FunctionDefinition const& _function) void AbstractContract::endVisit(ContractDefinition const& _contract) { - bool isFullyImplemented = _contract.annotation().unimplementedDeclarations.empty(); + bool isFullyImplemented = _contract.annotation().unimplementedDeclarations->empty(); if ( !isFullyImplemented && diff --git a/tools/solidityUpgrade/UpgradeChange.cpp b/tools/solidityUpgrade/UpgradeChange.cpp index a4036f77535e..51475ffd333c 100644 --- a/tools/solidityUpgrade/UpgradeChange.cpp +++ b/tools/solidityUpgrade/UpgradeChange.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include using namespace std; using namespace solidity; @@ -37,7 +37,7 @@ void UpgradeChange::apply() void UpgradeChange::log(bool const _shorten) const { stringstream os; - SourceReferenceFormatterHuman formatter{os, true, false}; + SourceReferenceFormatter formatter{os, true, false}; string start = to_string(m_location.start); string end = to_string(m_location.end); diff --git a/tools/yulPhaser/Chromosome.cpp b/tools/yulPhaser/Chromosome.cpp index 453c62d39a30..687c669da51a 100644 --- a/tools/yulPhaser/Chromosome.cpp +++ b/tools/yulPhaser/Chromosome.cpp @@ -37,12 +37,6 @@ ostream& operator<<(ostream& _stream, Chromosome const& _chromosome); } -Chromosome::Chromosome(string const& _optimisationSteps) -{ - for (char abbreviation: _optimisationSteps) - m_optimisationSteps.push_back(OptimiserSuite::stepAbbreviationToNameMap().at(abbreviation)); -} - Chromosome Chromosome::makeRandom(size_t _length) { vector steps; @@ -54,10 +48,7 @@ Chromosome Chromosome::makeRandom(size_t _length) ostream& phaser::operator<<(ostream& _stream, Chromosome const& _chromosome) { - for (auto const& stepName: _chromosome.m_optimisationSteps) - _stream << OptimiserSuite::stepNameToAbbreviationMap().at(stepName); - - return _stream; + return _stream << _chromosome.m_genes; } vector Chromosome::allStepNames() @@ -75,3 +66,26 @@ string const& Chromosome::randomOptimisationStep() return stepNames[SimulationRNG::uniformInt(0, stepNames.size() - 1)]; } + +char Chromosome::randomGene() +{ + return OptimiserSuite::stepNameToAbbreviationMap().at(randomOptimisationStep()); +} + +string Chromosome::stepsToGenes(vector const& _optimisationSteps) +{ + string genes; + for (string const& stepName: _optimisationSteps) + genes.push_back(OptimiserSuite::stepNameToAbbreviationMap().at(stepName)); + + return genes; +} + +vector Chromosome::genesToSteps(string const& _genes) +{ + vector steps; + for (char abbreviation: _genes) + steps.push_back(OptimiserSuite::stepAbbreviationToNameMap().at(abbreviation)); + + return steps; +} diff --git a/tools/yulPhaser/Chromosome.h b/tools/yulPhaser/Chromosome.h index 95f3ae58e656..96477086f0db 100644 --- a/tools/yulPhaser/Chromosome.h +++ b/tools/yulPhaser/Chromosome.h @@ -42,24 +42,32 @@ class Chromosome public: Chromosome() = default; explicit Chromosome(std::vector _optimisationSteps): - m_optimisationSteps(std::move(_optimisationSteps)) {} - explicit Chromosome(std::string const& _optimisationSteps); + m_genes(stepsToGenes(_optimisationSteps)) {} + explicit Chromosome(std::string _genes): + // NOTE: We don't validate the genes - they're only checked at the point of conversion to + // actual optimisation steps names. This is very convenient in mutation tests. + m_genes(std::move(_genes)) {} static Chromosome makeRandom(size_t _length); - size_t length() const { return m_optimisationSteps.size(); } - std::vector const& optimisationSteps() const { return m_optimisationSteps; } + size_t length() const { return m_genes.size(); } + std::string const& genes() const { return m_genes; } + + std::vector optimisationSteps() const { return genesToSteps(m_genes); } friend std::ostream& operator<<(std::ostream& _stream, Chromosome const& _chromosome); - bool operator==(Chromosome const& _other) const { return m_optimisationSteps == _other.m_optimisationSteps; } + bool operator==(Chromosome const& _other) const { return m_genes == _other.m_genes; } bool operator!=(Chromosome const& _other) const { return !(*this == _other); } static std::string const& randomOptimisationStep(); + static char randomGene(); + static std::string stepsToGenes(std::vector const& _optimisationSteps); + static std::vector genesToSteps(std::string const& _genes); private: static std::vector allStepNames(); - std::vector m_optimisationSteps; + std::string m_genes; }; } diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index 66c79fda2223..e0b719e25555 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -62,16 +62,16 @@ size_t ProgramSize::evaluate(Chromosome const& _chromosome) size_t RelativeProgramSize::evaluate(Chromosome const& _chromosome) { - size_t const scalingFactor = pow(10, m_fixedPointPrecision); + double const scalingFactor = pow(10, m_fixedPointPrecision); size_t unoptimisedSize = optimisedProgram(Chromosome("")).codeSize(codeWeights()); if (unoptimisedSize == 0) - return scalingFactor; + return static_cast(scalingFactor); size_t optimisedSize = optimisedProgram(_chromosome).codeSize(codeWeights()); return static_cast(round( - static_cast(optimisedSize) / unoptimisedSize * scalingFactor + double(optimisedSize) / double(unoptimisedSize) * scalingFactor )); } diff --git a/tools/yulPhaser/Mutations.cpp b/tools/yulPhaser/Mutations.cpp index f9d51744eac4..69ec7fc49927 100644 --- a/tools/yulPhaser/Mutations.cpp +++ b/tools/yulPhaser/Mutations.cpp @@ -36,15 +36,15 @@ function phaser::geneRandomisation(double _chance) { return [=](Chromosome const& _chromosome) { - vector optimisationSteps; - for (auto const& step: _chromosome.optimisationSteps()) - optimisationSteps.push_back( + string genes; + for (char gene: _chromosome.genes()) + genes.push_back( SimulationRNG::bernoulliTrial(_chance) ? - Chromosome::randomOptimisationStep() : - step + Chromosome::randomGene() : + gene ); - return Chromosome(move(optimisationSteps)); + return Chromosome(move(genes)); }; } @@ -52,12 +52,12 @@ function phaser::geneDeletion(double _chance) { return [=](Chromosome const& _chromosome) { - vector optimisationSteps; - for (auto const& step: _chromosome.optimisationSteps()) + string genes; + for (char gene: _chromosome.genes()) if (!SimulationRNG::bernoulliTrial(_chance)) - optimisationSteps.push_back(step); + genes.push_back(gene); - return Chromosome(move(optimisationSteps)); + return Chromosome(move(genes)); }; } @@ -65,19 +65,19 @@ function phaser::geneAddition(double _chance) { return [=](Chromosome const& _chromosome) { - vector optimisationSteps; + string genes; if (SimulationRNG::bernoulliTrial(_chance)) - optimisationSteps.push_back(Chromosome::randomOptimisationStep()); + genes.push_back(Chromosome::randomGene()); - for (auto const& step: _chromosome.optimisationSteps()) + for (char gene: _chromosome.genes()) { - optimisationSteps.push_back(step); + genes.push_back(gene); if (SimulationRNG::bernoulliTrial(_chance)) - optimisationSteps.push_back(Chromosome::randomOptimisationStep()); + genes.push_back(Chromosome::randomGene()); } - return Chromosome(move(optimisationSteps)); + return Chromosome(move(genes)); }; } @@ -120,19 +120,14 @@ ChromosomePair fixedPointSwap( assert(_crossoverPoint <= _chromosome1.length()); assert(_crossoverPoint <= _chromosome2.length()); - auto begin1 = _chromosome1.optimisationSteps().begin(); - auto begin2 = _chromosome2.optimisationSteps().begin(); - auto end1 = _chromosome1.optimisationSteps().end(); - auto end2 = _chromosome2.optimisationSteps().end(); - return { Chromosome( - vector(begin1, begin1 + static_cast(_crossoverPoint)) + - vector(begin2 + static_cast(_crossoverPoint), end2) + _chromosome1.genes().substr(0, _crossoverPoint) + + _chromosome2.genes().substr(_crossoverPoint, _chromosome2.length() - _crossoverPoint) ), Chromosome( - vector(begin2, begin2 + static_cast(_crossoverPoint)) + - vector(begin1 + static_cast(_crossoverPoint), end1) + _chromosome2.genes().substr(0, _crossoverPoint) + + _chromosome1.genes().substr(_crossoverPoint, _chromosome1.length() - _crossoverPoint) ), }; } @@ -176,7 +171,7 @@ function phaser::fixedPointCrossover(double _crossoverPoint) return [=](Chromosome const& _chromosome1, Chromosome const& _chromosome2) { size_t minLength = min(_chromosome1.length(), _chromosome2.length()); - size_t concretePoint = static_cast(round(minLength * _crossoverPoint)); + size_t concretePoint = static_cast(round(double(minLength) * _crossoverPoint)); return get<0>(fixedPointSwap(_chromosome1, _chromosome2, concretePoint)); }; @@ -197,24 +192,19 @@ ChromosomePair fixedTwoPointSwap( assert(_crossoverPoint2 <= _chromosome1.length()); assert(_crossoverPoint2 <= _chromosome2.length()); - auto lowPoint = static_cast(min(_crossoverPoint1, _crossoverPoint2)); - auto highPoint = static_cast(max(_crossoverPoint1, _crossoverPoint2)); - - auto begin1 = _chromosome1.optimisationSteps().begin(); - auto begin2 = _chromosome2.optimisationSteps().begin(); - auto end1 = _chromosome1.optimisationSteps().end(); - auto end2 = _chromosome2.optimisationSteps().end(); + size_t lowPoint = min(_crossoverPoint1, _crossoverPoint2); + size_t highPoint = max(_crossoverPoint1, _crossoverPoint2); return { Chromosome( - vector(begin1, begin1 + lowPoint) + - vector(begin2 + lowPoint, begin2 + highPoint) + - vector(begin1 + highPoint, end1) + _chromosome1.genes().substr(0, lowPoint) + + _chromosome2.genes().substr(lowPoint, highPoint - lowPoint) + + _chromosome1.genes().substr(highPoint, _chromosome1.length() - highPoint) ), Chromosome( - vector(begin2, begin2 + lowPoint) + - vector(begin1 + lowPoint, begin1 + highPoint) + - vector(begin2 + highPoint, end2) + _chromosome2.genes().substr(0, lowPoint) + + _chromosome1.genes().substr(lowPoint, highPoint - lowPoint) + + _chromosome2.genes().substr(highPoint, _chromosome2.length() - highPoint) ), }; } @@ -258,42 +248,37 @@ namespace ChromosomePair uniformSwap(Chromosome const& _chromosome1, Chromosome const& _chromosome2, double _swapChance) { - vector steps1; - vector steps2; + string steps1; + string steps2; size_t minLength = min(_chromosome1.length(), _chromosome2.length()); for (size_t i = 0; i < minLength; ++i) if (SimulationRNG::bernoulliTrial(_swapChance)) { - steps1.push_back(_chromosome2.optimisationSteps()[i]); - steps2.push_back(_chromosome1.optimisationSteps()[i]); + steps1.push_back(_chromosome2.genes()[i]); + steps2.push_back(_chromosome1.genes()[i]); } else { - steps1.push_back(_chromosome1.optimisationSteps()[i]); - steps2.push_back(_chromosome2.optimisationSteps()[i]); + steps1.push_back(_chromosome1.genes()[i]); + steps2.push_back(_chromosome2.genes()[i]); } - auto begin1 = _chromosome1.optimisationSteps().begin(); - auto begin2 = _chromosome2.optimisationSteps().begin(); - auto end1 = _chromosome1.optimisationSteps().end(); - auto end2 = _chromosome2.optimisationSteps().end(); - bool swapTail = SimulationRNG::bernoulliTrial(_swapChance); if (_chromosome1.length() > minLength) { if (swapTail) - steps2.insert(steps2.end(), begin1 + static_cast(minLength), end1); + steps2 += _chromosome1.genes().substr(minLength, _chromosome1.length() - minLength); else - steps1.insert(steps1.end(), begin1 + static_cast(minLength), end1); + steps1 += _chromosome1.genes().substr(minLength, _chromosome1.length() - minLength); } if (_chromosome2.length() > minLength) { if (swapTail) - steps1.insert(steps1.end(), begin2 + static_cast(minLength), end2); + steps1 += _chromosome2.genes().substr(minLength, _chromosome2.length() - minLength); else - steps2.insert(steps2.end(), begin2 + static_cast(minLength), end2); + steps2 += _chromosome2.genes().substr(minLength, _chromosome2.length() - minLength); } return {Chromosome(steps1), Chromosome(steps2)}; diff --git a/tools/yulPhaser/PairSelections.cpp b/tools/yulPhaser/PairSelections.cpp index cce99cef5c6d..4c9d4ee5f961 100644 --- a/tools/yulPhaser/PairSelections.cpp +++ b/tools/yulPhaser/PairSelections.cpp @@ -31,7 +31,7 @@ vector> RandomPairSelection::materialise(size_t _poolSize) if (_poolSize < 2) return {}; - auto count = static_cast(round(_poolSize * m_selectionSize)); + auto count = static_cast(round(double(_poolSize) * m_selectionSize)); vector> selection; for (size_t i = 0; i < count; ++i) @@ -94,7 +94,7 @@ vector> PairMosaicSelection::materialise(size_t _poolSize) if (_poolSize < 2) return {}; - size_t count = static_cast(round(_poolSize * m_selectionSize)); + size_t count = static_cast(round(double(_poolSize) * m_selectionSize)); vector> selection; for (size_t i = 0; i < count; ++i) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 589ff93a80f4..ca15f3ccdc40 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -141,7 +141,7 @@ unique_ptr GeneticAlgorithmFactory::build( { case Algorithm::Random: { - double elitePoolSize = 1.0 / _populationSize; + double elitePoolSize = 1.0 / double(_populationSize); if (_options.randomElitePoolSize.has_value()) elitePoolSize = _options.randomElitePoolSize.value(); @@ -154,7 +154,7 @@ unique_ptr GeneticAlgorithmFactory::build( } case Algorithm::GEWEP: { - double percentGenesToRandomise = 1.0 / _options.maxChromosomeLength; + double percentGenesToRandomise = 1.0 / double(_options.maxChromosomeLength); double percentGenesToAddOrDelete = percentGenesToRandomise; if (_options.gewepGenesToRandomise.has_value()) @@ -421,8 +421,8 @@ void Phaser::main(int _argc, char** _argv) Phaser::CommandLineDescription Phaser::buildCommandLineDescription() { - size_t const lineLength = po::options_description::m_default_line_length; - size_t const minDescriptionLength = lineLength - 23; + unsigned const lineLength = po::options_description::m_default_line_length; + unsigned const minDescriptionLength = lineLength - 23; po::options_description keywordDescription( "yul-phaser, a tool for finding the best sequence of Yul optimisation phases.\n" diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index c4b50636991a..1dba2e2fd3c9 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -54,7 +54,7 @@ bool phaser::isFitter(Individual const& a, Individual const& b) return ( (a.fitness < b.fitness) || (a.fitness == b.fitness && a.chromosome.length() < b.chromosome.length()) || - (a.fitness == b.fitness && a.chromosome.length() == b.chromosome.length() && toString(a.chromosome) < toString(b.chromosome)) + (a.fitness == b.fitness && a.chromosome.length() == b.chromosome.length() && a.chromosome.genes() < b.chromosome.genes()) ); } diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index e254ca2412b4..c7b96bd21efa 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -59,7 +60,7 @@ ostream& operator<<(ostream& _stream, Program const& _program); ostream& std::operator<<(ostream& _outputStream, ErrorList const& _errors) { - SourceReferenceFormatter formatter(_outputStream); + SourceReferenceFormatter formatter(_outputStream, true, false); for (auto const& error: _errors) formatter.printErrorInformation(*error); diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 12026805b217..8eec71be2301 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -19,7 +19,7 @@ #pragma once #include -#include +#include #include diff --git a/tools/yulPhaser/ProgramCache.h b/tools/yulPhaser/ProgramCache.h index d0e51a37ef4f..4c145d537c8d 100644 --- a/tools/yulPhaser/ProgramCache.h +++ b/tools/yulPhaser/ProgramCache.h @@ -121,7 +121,7 @@ class ProgramCache CacheStats gatherStats() const; - std::map const& entries() const { return m_entries; }; + std::map const& entries() const { return m_entries; } Program const& program() const { return m_program; } size_t currentRound() const { return m_currentRound; } diff --git a/tools/yulPhaser/Selections.cpp b/tools/yulPhaser/Selections.cpp index e34c80eaf2a8..5c55706454a9 100644 --- a/tools/yulPhaser/Selections.cpp +++ b/tools/yulPhaser/Selections.cpp @@ -28,8 +28,8 @@ using namespace solidity::phaser; vector RangeSelection::materialise(size_t _poolSize) const { - size_t beginIndex = static_cast(round(_poolSize * m_startPercent)); - size_t endIndex = static_cast(round(_poolSize * m_endPercent)); + size_t beginIndex = static_cast(round(double(_poolSize) * m_startPercent)); + size_t endIndex = static_cast(round(double(_poolSize) * m_endPercent)); vector selection; for (size_t i = beginIndex; i < endIndex; ++i) @@ -40,7 +40,7 @@ vector RangeSelection::materialise(size_t _poolSize) const vector MosaicSelection::materialise(size_t _poolSize) const { - size_t count = static_cast(round(_poolSize * m_selectionSize)); + size_t count = static_cast(round(double(_poolSize) * m_selectionSize)); vector selection; for (size_t i = 0; i < count; ++i) @@ -51,7 +51,7 @@ vector MosaicSelection::materialise(size_t _poolSize) const vector RandomSelection::materialise(size_t _poolSize) const { - size_t count = static_cast(round(_poolSize * m_selectionSize)); + size_t count = static_cast(round(double(_poolSize) * m_selectionSize)); vector selection; for (size_t i = 0; i < count; ++i) diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 7a73f81ad962..5fca4d99e5eb 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -62,5 +62,5 @@ uint32_t SimulationRNG::generateSeed() // This is not a secure way to seed the generator but it's good enough for simulation purposes. // The only thing that matters for us is that the sequence is different on each run and that // it fits the expected distribution. It does not have to be 100% unpredictable. - return time(nullptr); + return static_cast(time(nullptr)); }