diff --git a/.circleci/build_win.ps1 b/.circleci/build_win.ps1 index 2681cf67d..9be967c97 100644 --- a/.circleci/build_win.ps1 +++ b/.circleci/build_win.ps1 @@ -18,7 +18,7 @@ else { 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" .. +..\deps\cmake\bin\cmake -G "Visual Studio 16 2019" -DBoost_DIR="$boost_dir\" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_INSTALL_PREFIX="$PSScriptRoot\..\upload" -DUSE_Z3=OFF .. if ( -not $? ) { throw "CMake configure failed." } msbuild solidity.sln /p:Configuration=Release /m:5 /v:minimal if ( -not $? ) { throw "Build failed." } diff --git a/.circleci/config.yml b/.circleci/config.yml index bf047b5a6..4706fe6c0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,12 +17,15 @@ parameters: default: "solbuildpackpusher/solidity-buildpack-deps@sha256:61232feea23c8c57e82cf5fae890f8b86bbec353cdc04f2fcba383ca589e1d8b" ubuntu-1604-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-11 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:4acb2674eab3e7939d6dc6caa0b8320f4dd79484325242b58473ca2875792d90" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-13 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c26a7ffc9fc243a4ec3105b9dc1edcdd964ad0e9665c83172b7ebda74bbf3021" emscripten-docker-image: type: string # solbuildpackpusher/solidity-buildpack-deps:emscripten-6 default: "solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154" + evm-version: + type: string + default: london orbs: win: circleci/windows@2.2.0 @@ -50,6 +53,25 @@ defaults: name: Correctness proofs for optimization rules command: scripts/run_proofs.sh + - 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 + name: docs pragma version check + command: ./scripts/docs_version_pragma_check.sh + # -------------------------------------------------------------------------- # Artifacts Templates @@ -111,17 +133,7 @@ defaults: - store_test_results: &store_test_results path: test_results/ - - 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_soltest_steps: &run_soltest_steps + - steps_soltest: &steps_soltest steps: - checkout - attach_workspace: @@ -130,7 +142,7 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results - - run_soltest_all_steps: &run_soltest_all_steps + - steps_soltest_all: &steps_soltest_all steps: - checkout - attach_workspace: @@ -139,12 +151,7 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results - - run_cmdline_tests: &run_cmdline_tests - name: command line tests - no_output_timeout: 30m - command: ./test/cmdlineTests.sh - - - run_cmdline_tests_steps: &run_cmdline_tests_steps + - steps_cmdline_tests: &steps_cmdline_tests steps: - checkout - attach_workspace: @@ -153,34 +160,30 @@ defaults: - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results - - run_docs_pragma_min_version: &run_docs_pragma_min_version - name: docs pragma version check - command: ./scripts/docs_version_pragma_check.sh - - test_ubuntu1604_clang: &test_ubuntu1604_clang docker: - image: << pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image >> - <<: *run_soltest_steps + <<: *steps_soltest - test_ubuntu2004_clang: &test_ubuntu2004_clang docker: - image: << pipeline.parameters.ubuntu-2004-clang-docker-image >> - <<: *run_soltest_steps + <<: *steps_soltest - test_ubuntu2004: &test_ubuntu2004 docker: - image: << pipeline.parameters.ubuntu-2004-docker-image >> parallelism: 6 - <<: *run_soltest_all_steps + <<: *steps_soltest_all - test_asan: &test_asan <<: *test_ubuntu2004 - <<: *run_soltest_steps + <<: *steps_soltest - test_ubuntu2004_clang_cli: &test_ubuntu2004_clang_cli docker: - image: << pipeline.parameters.ubuntu-2004-clang-docker-image >> - <<: *run_cmdline_tests_steps + <<: *steps_cmdline_tests # -------------------------------------------------------------------------- # Workflow Templates @@ -434,6 +437,32 @@ jobs: name: Python unit tests command: python.exe test/pyscriptTests.py + b_ubu: &b_ubu + resource_class: xlarge + docker: + - image: << pipeline.parameters.ubuntu-2004-docker-image >> + environment: + MAKEFLAGS: -j 10 + steps: + - checkout + - run: *run_build + - store_artifacts: *artifacts_solc + - store_artifacts: *artifacts_tools + - persist_to_workspace: *artifacts_executables + + # x64 ASAN build, for testing for memory related bugs + b_ubu_asan: &b_ubu_asan + <<: *b_ubu + environment: + CMAKE_OPTIONS: -DSANITIZE=address + MAKEFLAGS: -j 10 + CMAKE_BUILD_TYPE: Release + steps: + - checkout + - run: *run_build + - store_artifacts: *artifacts_solc + - persist_to_workspace: *artifacts_executables + b_ubu_clang: &b_ubu_clang resource_class: xlarge docker: @@ -477,19 +506,6 @@ jobs: - store_artifacts: *artifacts_solc - persist_to_workspace: *artifacts_executables - b_ubu: &b_ubu - resource_class: xlarge - docker: - - image: << pipeline.parameters.ubuntu-2004-docker-image >> - environment: - MAKEFLAGS: -j 10 - steps: - - checkout - - run: *run_build - - store_artifacts: *artifacts_solc - - store_artifacts: *artifacts_tools - - persist_to_workspace: *artifacts_executables - b_ubu_release: &b_ubu_release <<: *b_ubu environment: @@ -523,7 +539,7 @@ jobs: t_ubu_codecov: <<: *test_ubuntu2004 environment: - EVM: constantinople + EVM: << pipeline.parameters.evm-version >> OPTIMIZE: 1 steps: - checkout @@ -586,9 +602,7 @@ jobs: b_archlinux: docker: - # FIXME: Newer releases won't work until CircleCI updates its host machines. - # See https://github.com/ethereum/solidity/pull/11332 - - image: archlinux:base-20210131.0.14634 + - image: archlinux:base environment: TERM: xterm MAKEFLAGS: -j 3 @@ -642,7 +656,7 @@ jobs: macos: xcode: "11.0.0" environment: - EVM: constantinople + EVM: << pipeline.parameters.evm-version >> OPTIMIZE: 0 TERM: xterm steps: @@ -696,19 +710,6 @@ jobs: - soljson.js - version.txt - # x64 ASAN build, for testing for memory related bugs - b_ubu_asan: &b_ubu_asan - <<: *b_ubu - environment: - CMAKE_OPTIONS: -DSANITIZE=address - MAKEFLAGS: -j 10 - CMAKE_BUILD_TYPE: Release - steps: - - checkout - - run: *run_build - - store_artifacts: *artifacts_solc - - persist_to_workspace: *artifacts_executables - b_docs: docker: - image: << pipeline.parameters.ubuntu-2004-docker-image >> @@ -727,52 +728,38 @@ jobs: t_archlinux_soltest: &t_archlinux_soltest docker: - # FIXME: Newer releases won't work until CircleCI updates its host machines. - # See https://github.com/ethereum/solidity/pull/11332 - - image: archlinux:base-20210131.0.14634 + - image: archlinux:base environment: - EVM: constantinople + EVM: << pipeline.parameters.evm-version >> OPTIMIZE: 0 TERM: xterm # For Archlinux we do not have prebuilt docker images and we would need to build evmone from source, # thus we forgo semantics tests to speed things up. - # FIXME: Z3 4.8.11 prerelease is now in main Arch Linux repos but it makes some of our SMT - # tests hang. Disabling SMT tests until we get a proper release. - # See https://github.com/Z3Prover/z3/issues/5330 for more details. - SOLTEST_FLAGS: --no-semantic-tests --no-smt + SOLTEST_FLAGS: --no-semantic-tests 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 + - when: + condition: true + <<: *steps_soltest t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul docker: - image: << pipeline.parameters.ubuntu-2004-docker-image >> environment: - EVM: constantinople + EVM: << pipeline.parameters.evm-version >> SOLTEST_FLAGS: --enforce-via-yul OPTIMIZE: 0 TERM: xterm - steps: - - checkout - - attach_workspace: - at: build - - run: *run_soltest - - store_test_results: *store_test_results - - store_artifacts: *artifacts_test_results + <<: *steps_soltest t_ubu_clang_soltest: &t_ubu_clang_soltest <<: *test_ubuntu2004_clang environment: - EVM: constantinople + EVM: << pipeline.parameters.evm-version >> OPTIMIZE: 0 t_ubu_release_soltest: &t_ubu_release_soltest @@ -783,7 +770,7 @@ jobs: - image: << pipeline.parameters.ubuntu-2004-docker-image >> environment: TERM: xterm - <<: *run_cmdline_tests_steps + <<: *steps_cmdline_tests t_ubu_release_cli: &t_ubu_release_cli <<: *t_ubu_cli @@ -793,47 +780,43 @@ jobs: environment: TERM: xterm ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 - <<: *run_cmdline_tests_steps + <<: *steps_cmdline_tests - t_ubu_asan_constantinople: + t_ubu_asan: <<: *test_asan environment: - EVM: constantinople + EVM: << pipeline.parameters.evm-version >> OPTIMIZE: 0 SOLTEST_FLAGS: --no-smt ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 - t_ubu_asan_constantinople_clang: + t_ubu_asan_clang: <<: *test_ubuntu2004_clang environment: - EVM: constantinople + EVM: << pipeline.parameters.evm-version >> OPTIMIZE: 0 SOLTEST_FLAGS: --no-smt ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 t_ubu_ubsan_clang: + environment: + EVM: << pipeline.parameters.evm-version >> docker: - image: << pipeline.parameters.ubuntu-2004-clang-docker-image >> steps: - - checkout - - attach_workspace: - at: build - - run: *run_soltest + - when: + condition: true + <<: *steps_soltest - run: *gitter_notify_failure - - store_test_results: *store_test_results - - store_artifacts: *artifacts_test_results t_ubu_ubsan_clang_cli: docker: - image: << pipeline.parameters.ubuntu-2004-clang-docker-image >> steps: - - checkout - - attach_workspace: - at: build - - run: *run_cmdline_tests + - when: + condition: true + <<: *steps_cmdline_tests - run: *gitter_notify_failure - - store_test_results: *store_test_results - - store_artifacts: *artifacts_test_results t_ems_solcjs: docker: @@ -1024,7 +1007,7 @@ jobs: b_bytecode_ems: docker: - - image: circleci/node:14 + - image: circleci/node:16 environment: SOLC_EMSCRIPTEN: "On" steps: @@ -1216,7 +1199,6 @@ workflows: branches: only: - develop - - develop_060 jobs: # OSSFUZZ builds and (regression) tests @@ -1230,8 +1212,8 @@ workflows: # 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: *workflow_ubuntu2004_asan + - t_ubu_asan_clang: *workflow_ubuntu2004_asan_clang - t_ubu_asan_cli: *workflow_ubuntu2004_asan # UBSan build and tests diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 0505aa6db..b2f4b4e10 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -35,6 +35,21 @@ set -eu +function validate_checksum { + local package="$1" + local expected_checksum="$2" + + local actual_checksum + actual_checksum=$(sha256sum "$package") + if [[ $actual_checksum != "${expected_checksum} ${package}" ]] + then + >&2 echo "ERROR: Wrong checksum for package $package." + >&2 echo "Actual: $actual_checksum" + >&2 echo "Expected: $expected_checksum" + exit 1 + fi +} + if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies then git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow @@ -48,23 +63,31 @@ then ./scripts/install_obsolete_jsoncpp_1_7_4.sh # z3 - z3_version="z3-4.8.12" - osx_version="osx-10.15.7" - wget "https://github.com/Z3Prover/z3/releases/download/$z3_version/$z3_version-x64-$osx_version.zip" - unzip "$z3_version-x64-$osx_version.zip" - rm -f "$z3_version-x64-$osx_version.zip" - cp "$z3_version-x64-$osx_version/bin/libz3.a" /usr/local/lib - cp "$z3_version-x64-$osx_version/bin/z3" /usr/local/bin - cp "$z3_version-x64-$osx_version"/include/* /usr/local/include - rm -rf "$z3_version-x64-$osx_version" + z3_version="4.8.12" + z3_dir="z3-${z3_version}-x64-osx-10.15.7" + z3_package="${z3_dir}.zip" + wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_package}" + validate_checksum "$z3_package" a1f6ef3c99456147c4d3f2652dc6bc90951c4ab3fe7741a255eb794f0ab8938c + unzip "$z3_package" + rm "$z3_package" + cp "${z3_dir}/bin/libz3.a" /usr/local/lib + cp "${z3_dir}/bin/z3" /usr/local/bin + cp "${z3_dir}/include/"* /usr/local/include + rm -r "$z3_dir" # evmone - wget https://github.com/ethereum/evmone/releases/download/v0.8.0/evmone-0.8.0-darwin-x86_64.tar.gz - tar xzpf evmone-0.8.0-darwin-x86_64.tar.gz -C /usr/local - rm -f evmone-0.8.0-darwin-x86_64.tar.gz + evmone_version="0.8.0" + evmone_package="evmone-${evmone_version}-darwin-x86_64.tar.gz" + wget "https://github.com/ethereum/evmone/releases/download/v${evmone_version}/${evmone_package}" + validate_checksum "$evmone_package" e8efef478822f0ed6d0493e89004181e895893f93963152a2a81589acc3a0828 + tar xzpf "$evmone_package" -C /usr/local + rm "$evmone_package" # hera - wget https://github.com/ewasm/hera/releases/download/v0.5.0/hera-0.5.0-darwin-x86_64.tar.gz - tar xzpf hera-0.5.0-darwin-x86_64.tar.gz -C /usr/local - rm -f hera-0.5.0-darwin-x86_64.tar.gz + hera_version="0.5.0" + hera_package="hera-${hera_version}-darwin-x86_64.tar.gz" + wget "https://github.com/ewasm/hera/releases/download/v${hera_version}/${hera_package}" + validate_checksum "$hera_package" 190050d7ace384ecd79ec1b1f607a9ff40e196b4eec75932958d4814d221d059 + tar xzpf "$hera_package" -C /usr/local + rm "$hera_package" fi diff --git a/CMakeLists.txt b/CMakeLists.txt index e835a66ff..6014cc6fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.8") +set(PROJECT_VERSION "0.8.10") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) @@ -34,6 +34,7 @@ endif() option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF) +option(STRICT_Z3_VERSION "Use the latest version of Z3" ON) # Setup cccache. include(EthCcache) @@ -63,8 +64,29 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens include(EthOptions) configure_project(TESTS) +set(LATEST_Z3_VERSION "4.8.12") +set(MINIMUM_Z3_VERSION "4.8.0") +find_package(Z3) +if (${Z3_FOUND}) + if (${STRICT_Z3_VERSION}) + if (NOT ("${Z3_VERSION_STRING}" VERSION_EQUAL ${LATEST_Z3_VERSION})) + message( + FATAL_ERROR + "SMTChecker tests require Z3 ${LATEST_Z3_VERSION} for all tests to pass.\n\ +Build with -DSTRICT_Z3_VERSION=OFF if you want to use a different version. \ +You can also use -DUSE_Z3=OFF to build without Z3. In both cases use --no-smt when running tests." + ) + endif() + else() + if ("${Z3_VERSION_STRING}" VERSION_LESS ${MINIMUM_Z3_VERSION}) + message( + FATAL_ERROR + "Solidity requires Z3 ${MINIMUM_Z3_VERSION} or newer. You can also use -DUSE_Z3=OFF to build without Z3." + ) + endif() + endif() +endif() -find_package(Z3 4.8.0) if(${USE_Z3_DLOPEN}) add_definitions(-DHAVE_Z3) add_definitions(-DHAVE_Z3_DLOPEN) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 1d897a53f..674c2addd 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -81,7 +81,7 @@ tuple myNamespace::meanAndSigma(vector const& _v) - Copyright - License (e.g. see COPYING) 2. Never use `#ifdef`/`#define`/`#endif` file guards. Prefer `#pragma` once as first line below file comment. -3. Prefer static const variable to value macros. +3. Prefer static constexpr variables to value macros. 4. Prefer inline constexpr functions to function macros. 5. Split complex macro on multiple lines with `\`. diff --git a/Changelog.md b/Changelog.md index f81b0d2a7..cfa19ed15 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,23 +6,72 @@ Breaking changes: * Inline Assembly: Consider functions, function parameters and return variables for shadowing checks. -### 0.8.8 (unreleased) +### 0.8.10 (unreleased) Language Features: + * Inline Assembly: Support ``.address`` and ``.selector`` on external function pointers to access their address and function selector. Compiler Features: - * Commandline Interface: Normalize paths specified on the command line and make them relative for files located inside base path. - * Immutable variables can be read at construction time once they are initialized. - * SMTChecker: Support low level ``call`` as external calls to unknown code. - * SMTChecker: Add constraints to better correlate ``address(this).balance`` and ``msg.value``. - * Commandline Interface: Disallowed the ``--experimental-via-ir`` option to be used with Standard Json, Assembler and Linker modes. + * SMTChecker: Output values for ``block.*``, ``msg.*`` and ``tx.*`` variables that are present in the called functions. Bugfixes: + * Commandline Interface: Fix extra newline character being appended to sources passed through standard input, affecting their hashes. + * SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``). + + + + +### 0.8.9 (2021-09-29) + +Important Bugfixes: + * Immutables: Properly perform sign extension on signed immutables. + * User Defined Value Type: Fix storage layout of user defined value types for underlying types shorter than 32 bytes. + + +Bugfixes: + * AST: Export ``canonicalName`` for ``UserDefinedValueTypeDefinition`` and ``ContractDefinition``. + + + +### 0.8.8 (2021-09-27) + +Language Features: + * Inheritance: A function that overrides only a single interface function does not require the ``override`` specifier. + * Type System: Support ``type(E).min`` and ``type(E).max`` for enums. + * User Defined Value Type: allows creating a zero cost abstraction over a value type with stricter type requirements. + + +Compiler Features: + * Commandline Interface: Add ``--include-path`` option for specifying extra directories that may contain importable code (e.g. packaged third-party libraries). + * Commandline Interface: Do not implicitly run evm bytecode generation unless needed for the requested output. + * Commandline Interface: Normalize paths specified on the command line and make them relative for files located inside base path and/or include paths. + * Immutable variables can be read at construction time once they are initialized. + * SMTChecker: Add constraints to better correlate ``address(this).balance`` and ``msg.value``. + * SMTChecker: Support constants via modules. + * SMTChecker: Support low level ``call`` as external calls to unknown code. + * SMTChecker: Support the ``value`` option for external function calls. + * SMTChecker: Support user defined value types. + + +Bugfixes: + * Code Generator: Fix ICE on assigning to calldata structs and statically-sized calldata arrays in inline assembly. + * Code Generator: Use stable source order for ABI functions. + * Commandline Interface: Disallow the ``--experimental-via-ir`` option in Standard JSON, Assembler and Linker modes. + * Commandline Interface: Fix resolution of paths whitelisted with ``--allowed-paths`` or implicitly due to base path, remappings and files being compiled. Correctly handle paths that do not match imports exactly due to being relative, non-normalized or empty. + * Commandline Interface: Report optimizer options as invalid in Standard JSON and linker modes instead of ignoring them. + * Name Resolver: Fix that when importing an aliased symbol using ``import {AliasedName} from "a.sol"`` it would use the original name of the symbol and not the aliased one. + * Opcode Optimizer: Prevent the optimizer from running multiple times to avoid potential bytecode differences for referenced code. + * Parser: Properly check for multiple SPDX license identifiers next to each other and validate them. + * SMTChecker: Fix BMC's constraints regarding internal functions. + * SMTChecker: Fix false negative caused by ``push`` on storage array references returned by internal functions. * SMTChecker: Fix false positive in external calls from constructors. * SMTChecker: Fix internal error on some multi-source uses of ``abi.*``, cryptographic functions and constants. - * SMTChecker: Fix false negative caused by ``push`` on storage array references returned by internal functions. + * Standard JSON: Fix non-fatal errors in Yul mode being discarded if followed by a fatal error. + * Type Checker: Correct wrong error message in inline assembly complaining about ``.slot`` or ``.offset` not valid when actually ``.length`` was used. + * Type Checker: Disallow modifier declarations and definitions in interfaces. + * Yul Optimizer: Fix a crash in LoadResolver, when ``keccak256`` has particular non-identifier arguments. diff --git a/README.md b/README.md index abc8616e0..ccd8dc634 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Please follow the if you want to help. You can find our current feature and bug priorities for forthcoming -releases [in the projects section](https://github.com/ethereum/solidity/projects). +releases in the [projects section](https://github.com/ethereum/solidity/projects). ## Maintainers * [@axic](https://github.com/axic) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index d26060383..0695c56a3 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -102,14 +102,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Some Linux-specific Clang settings. We don't want these for OS X. if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") - - # TODO - Is this even necessary? Why? - # See http://stackoverflow.com/questions/19774778/when-is-it-necessary-to-use-use-the-flag-stdlib-libstdc. - add_compile_options(-stdlib=libstdc++) - - # Tell Boost that we're using Clang's libc++. Not sure exactly why we need to do. - add_definitions(-DBOOST_ASIO_HAS_CLANG_LIBCXX) - # Use fancy colors in the compiler diagnostics add_compile_options(-fcolor-diagnostics) @@ -200,6 +192,10 @@ if (SANITIZE) elseif (sanitizer STREQUAL "undefined") # The following flags not used by fuzzer but used by us may create problems, so consider # disabling them: alignment, pointer-overflow. + # The following flag is not used by us to reduce terminal noise + # i.e., warnings printed on stderr: unsigned-integer-overflow + # Note: The C++ standard does not officially consider unsigned integer overflows + # to be undefined behavior since they are implementation independent. # Flags are alphabetically sorted and are for clang v10.0 list(APPEND undefinedSanitizerChecks alignment @@ -217,18 +213,12 @@ if (SANITIZE) returns-nonnull-attribute shift signed-integer-overflow - unsigned-integer-overflow unreachable vla-bound vptr ) list(JOIN undefinedSanitizerChecks "," sanitizerChecks) - list(REMOVE_ITEM undefinedSanitizerChecks unsigned-integer-overflow) - # The fuzzer excludes reports of unsigned-integer-overflow. Hence, we remove it - # from the -fno-sanitize-recover checks. Consider reducing this list if we do not - # want to be notified about other failed checks. - list(JOIN undefinedSanitizerChecks "," dontRecoverFromChecks) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${sanitizerChecks} -fno-sanitize-recover=${dontRecoverFromChecks}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${sanitizerChecks} -fno-sanitize-recover=${sanitizerChecks}") endif() endif() diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 04cb876be..7a84a01d9 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -25,13 +25,20 @@ 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) +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") -find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +if (WIN32) + # Boost 1.77 fixes a bug that causes crashes on Windows for some relative paths in --allow-paths. + # See https://github.com/boostorg/filesystem/issues/201 + find_package(Boost 1.77.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +else() + # Boost 1.65 is the first to also provide boost::get for rvalue-references (#5787). + find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) +endif() # If cmake is older than boost and boost is older than 1.70, # find_package does not define imported targets, so we have to @@ -51,7 +58,7 @@ foreach (BOOST_COMPONENT IN LISTS BOOST_COMPONENTS) set_property(TARGET Boost::${BOOST_COMPONENT} PROPERTY IMPORTED_LOCATION ${Boost_${BOOST_COMPONENT_UPPER}_LIBRARY}) set_property(TARGET Boost::${BOOST_COMPONENT} PROPERTY INTERFACE_LINK_LIBRARIES ${Boost_${BOOST_COMPONENT_UPPER}_LIBRARIES}) set_property(TARGET Boost::${BOOST_COMPONENT} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) - endif() + endif() get_property(LOCATION TARGET Boost::${BOOST_COMPONENT} PROPERTY IMPORTED_LOCATION) message(STATUS "Found Boost::${BOOST_COMPONENT} at ${LOCATION}") endforeach() diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 1b61351fa..6f50c70d9 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -113,6 +113,9 @@ them. +-------------------------------+-----------------------------------------------------------------------------+ |:ref:`enum` |``uint8`` | +-------------------------------+-----------------------------------------------------------------------------+ +|:ref:`user defined value types |its underlying value type | +|` | | ++-------------------------------+-----------------------------------------------------------------------------+ |:ref:`struct` |``tuple`` | +-------------------------------+-----------------------------------------------------------------------------+ diff --git a/docs/assembly.rst b/docs/assembly.rst index 9da8ebb8b..c26a53e49 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -35,9 +35,9 @@ Example ------- The following example provides library code to access the code of another contract and -load it into a ``bytes`` variable. This is not possible with "plain Solidity" and the -idea is that reusable assembly libraries can enhance the Solidity language -without a compiler change. +load it into a ``bytes`` variable. This is possible with "plain Solidity" too, by using +``
.code``. But the point here is that reusable assembly libraries can enhance the +Solidity language without a compiler change. .. code-block:: solidity @@ -124,9 +124,43 @@ Access to External Variables, Functions and Libraries You can access Solidity variables and other identifiers by using their name. Local variables of value type are directly usable in inline assembly. +They can both be read and assigned to. -Local variables that refer to memory or calldata evaluate to the -address of the variable in memory, resp. calldata, not the value itself. +Local variables that refer to memory evaluate to the address of the variable in memory not the value itself. +Such variables can also be assigned to, but note that an assignment will only change the pointer and not the data +and that it is your responsibility to respect Solidity's memory management. +See :ref:`Conventions in Solidity `. + +Similarly, local variables that refer to statically-sized calldata arrays or calldata structs +evaluate to the address of the variable in calldata, not the value itself. +The variable can also be assigned a new offset, but note that no validation to ensure that +the variable will not point beyond ``calldatasize()`` is performed. + +For external function pointers the address and the function selector can be +accessed using ``x.address`` and ``x.selector``. +The selector consists of four right-aligned bytes. +Both values are can be assigned to. For example: + +.. code-block:: solidity + :force: + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity >=0.8.10 <0.9.0; + + contract C { + // Assigns a new selector and address to the return variable @fun + function combineToFunctionPointer(address newAddress, uint newSelector) public pure returns (function() external fun) { + assembly { + fun.selector := newSelector + fun.address := newAddress + } + } + } + +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, but as for the static case, no validation will be performed +to ensure that the resulting data area is within the bounds of ``calldatasize()``. For local storage variables or state variables, a single Yul identifier is not sufficient, since they do not necessarily occupy a single full storage slot. @@ -135,9 +169,10 @@ 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. +You can also assign to the ``.slot`` part of a local storage variable pointer. +For these (structs, arrays or mappings), the ``.offset`` part is always zero. +It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable, +though. Local Solidity variables are available for assignments, for example: @@ -178,17 +213,6 @@ Since Solidity 0.7.0, variables and functions declared inside the inline assembly block may not contain ``.``, but using ``.`` is valid to access Solidity variables from outside the inline assembly block. -Assignments are possible to assembly-local variables and to function-local -variables. Take care that when you assign to variables that point to -memory or storage, you will only change the pointer and not the data. - -You can assign to the ``.slot`` part of a local storage variable pointer. -For these (structs, arrays or mappings), the ``.offset`` part is always zero. -It is not possible to assign to the ``.slot`` or ``.offset`` part of a state variable, -though. - - - Things to Avoid --------------- @@ -199,6 +223,8 @@ functional-style opcodes, counting stack height for variable access and removing stack slots for assembly-local variables when the end of their block is reached. +.. _conventions-in-solidity: + Conventions in Solidity ----------------------- diff --git a/docs/bugs.json b/docs/bugs.json index 5f394aecc..0b72c05c7 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,25 @@ [ + { + "uid": "SOL-2021-4", + "name": "UserDefinedValueTypesBug", + "summary": "User defined value types with underlying type shorter than 32 bytes used incorrect storage layout and wasted storage", + "description": "The compiler did not correctly compute the storage layout of user defined value types based on types that are shorter than 32 bytes. It would always use a full storage slot for these types, even if the underlying type was shorter. This was wasteful and might have problems with tooling or contract upgrades.", + "link": "https://blog.soliditylang.org/2021/09/29/user-defined-value-types-bug/", + "introduced": "0.8.8", + "fixed": "0.8.9", + "severity": "very low" + + }, + { + "uid": "SOL-2021-3", + "name": "SignedImmutables", + "summary": "Immutable variables of signed integer type shorter than 256 bits can lead to values with invalid higher order bits if inline assembly is used.", + "description": "When immutable variables of signed integer type shorter than 256 bits are read, their higher order bits were unconditionally set to zero. The correct operation would be to sign-extend the value, i.e. set the higher order bits to one if the sign bit is one. This sign-extension is performed by Solidity just prior to when it matters, i.e. when a value is stored in memory, when it is compared or when a division is performed. Because of that, to our knowledge, the only way to access the value in its unclean state is by reading it through inline assembly.", + "link": "https://blog.soliditylang.org/2021/09/29/signed-immutables-bug/", + "introduced": "0.6.5", + "fixed": "0.8.9", + "severity": "very low" + }, { "uid": "SOL-2021-2", "name": "ABIDecodeTwoDimensionalArrayMemory", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 0c1dbbc18..4324b8339 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1333,6 +1333,7 @@ }, "0.6.10": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1342,6 +1343,7 @@ }, "0.6.11": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1351,6 +1353,7 @@ }, "0.6.12": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1402,6 +1405,7 @@ }, "0.6.5": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1415,6 +1419,7 @@ }, "0.6.6": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1427,6 +1432,7 @@ }, "0.6.7": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1439,6 +1445,7 @@ }, "0.6.8": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1448,6 +1455,7 @@ }, "0.6.9": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1458,6 +1466,7 @@ }, "0.7.0": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1467,6 +1476,7 @@ }, "0.7.1": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1477,6 +1487,7 @@ }, "0.7.2": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy", @@ -1486,6 +1497,7 @@ }, "0.7.3": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", "EmptyByteArrayCopy" @@ -1494,6 +1506,7 @@ }, "0.7.4": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching" ], @@ -1501,6 +1514,7 @@ }, "0.7.5": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching" ], @@ -1508,6 +1522,7 @@ }, "0.7.6": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching" ], @@ -1515,6 +1530,7 @@ }, "0.8.0": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching" ], @@ -1522,6 +1538,7 @@ }, "0.8.1": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching" ], @@ -1529,6 +1546,7 @@ }, "0.8.2": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching" ], @@ -1536,24 +1554,44 @@ }, "0.8.3": { "bugs": [ + "SignedImmutables", "ABIDecodeTwoDimensionalArrayMemory" ], "released": "2021-03-23" }, "0.8.4": { - "bugs": [], + "bugs": [ + "SignedImmutables" + ], "released": "2021-04-21" }, "0.8.5": { - "bugs": [], + "bugs": [ + "SignedImmutables" + ], "released": "2021-06-10" }, "0.8.6": { - "bugs": [], + "bugs": [ + "SignedImmutables" + ], "released": "2021-06-22" }, "0.8.7": { - "bugs": [], + "bugs": [ + "SignedImmutables" + ], "released": "2021-08-11" + }, + "0.8.8": { + "bugs": [ + "UserDefinedValueTypesBug", + "SignedImmutables" + ], + "released": "2021-09-27" + }, + "0.8.9": { + "bugs": [], + "released": "2021-09-29" } } \ No newline at end of file diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index 93283e62c..7d1ce7256 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -155,12 +155,17 @@ When a function has multiple return types, the statement ``return (v0, v1, ..., The number of components must be the same as the number of return variables and their types have to match, potentially after an :ref:`implicit conversion `. +.. _state-mutability: + +State Mutability +================ + .. index:: ! view function, function;view .. _view-functions: View Functions -============== +-------------- Functions can be declared ``view`` in which case they promise not to modify the state. @@ -214,7 +219,7 @@ The following statements are considered modifying the state: .. _pure-functions: Pure Functions -============== +-------------- Functions can be declared ``pure`` in which case they promise not to read from or modify the state. @@ -270,12 +275,17 @@ This behaviour is also in line with the ``STATICCALL`` opcode. not do state-changing operations, but it cannot check that the contract that will be called at runtime is actually of that type. +.. _special-functions: + +Special Functions +================= + .. index:: ! receive ether function, function;receive ! receive .. _receive-ether-function: Receive Ether Function -====================== +---------------------- A contract can have at most one ``receive`` function, declared using ``receive() external payable { ... }`` @@ -346,7 +356,7 @@ Below you can see an example of a Sink contract that uses function ``receive``. .. _fallback-function: Fallback Function -================= +----------------- 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)`` diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 24d2fd2ff..b77feda0a 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -303,6 +303,13 @@ contracts can no longer change the behaviour of that function. outside of interfaces. In interfaces, all functions are automatically considered ``virtual``. +.. note:: + + Starting from Solidity 0.8.8, the ``override`` keyword is not + required when overriding an interface function, except for the + case where the function is defined in multiple bases. + + Public state variables can override external functions if the parameter and return types of the function matches the getter function of the variable: diff --git a/docs/contracts/interfaces.rst b/docs/contracts/interfaces.rst index bfaf1bf2e..a4581a15f 100644 --- a/docs/contracts/interfaces.rst +++ b/docs/contracts/interfaces.rst @@ -6,12 +6,14 @@ Interfaces ********** -Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions: +Interfaces are similar to abstract contracts, but they cannot have any functions implemented. +There are further restrictions: - They cannot inherit from other contracts, but they can inherit from other interfaces. - All declared functions must be external. - They cannot declare a constructor. - They cannot declare state variables. +- They cannot declare modifiers. Some of these restrictions might be lifted in the future. @@ -33,10 +35,10 @@ Interfaces are denoted by their own keyword: Contracts can inherit interfaces as they would inherit other contracts. -All functions declared in interfaces are implicitly ``virtual``, which means that -they can be overridden. This does not automatically mean that an overriding function -can be overridden again - this is only possible if the overriding -function is marked ``virtual``. +All functions declared in interfaces are implicitly ``virtual`` and any +functions that override them do not need the ``override`` keyword. +This does not automatically mean that an overriding function can be overridden again - +this is only possible if the overriding function is marked ``virtual``. Interfaces can inherit from other interfaces. This has the same rules as normal inheritance. diff --git a/docs/contributing.rst b/docs/contributing.rst index 1d621d4a9..6b2db9546 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -100,12 +100,13 @@ Running the Tests ----------------- Solidity includes different types of tests, most of them bundled into the -`Boost C++ Test Framework `_ application ``soltest``. +`Boost C++ Test Framework `_ application ``soltest``. Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. 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. +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 to discover the location of the ``evmone`` library starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, @@ -137,9 +138,9 @@ Or, for example, to run all the tests for the yul disambiguator: See especially: -- `show_progress (-p) `_ to show test completion, -- `run_test (-t) `_ to run specific tests cases, and -- `report-level (-r) `_ give a more detailed report. +- `show_progress (-p) `_ to show test completion, +- `run_test (-t) `_ to run specific tests cases, and +- `report-level (-r) `_ give a more detailed report. .. note :: diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 95a55f6c6..1de35cf6b 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -65,9 +65,10 @@ uses up at least one stack slot and there are only 1024 slots available. External Function Calls ----------------------- -The expressions ``this.g(8);`` and ``c.g(2);`` (where ``c`` is a contract -instance) are also valid function calls, but this time, the function -will be called "externally", via a message call and not directly via jumps. +Functions can also be called using the ``this.g(8);`` and ``c.g(2);`` notation, where +``c`` is a contract instance and ``g`` is a function belonging to ``c``. +Calling the function ``g`` via either way results in it being called "externally", using a +message call and not directly via jumps. Please note that function calls on ``this`` cannot be used in the constructor, as the actual contract has not been created yet. @@ -105,8 +106,10 @@ otherwise, the ``value`` option would not be available. .. warning:: Be careful that ``feed.info{value: 10, gas: 800}`` only locally sets the ``value`` and amount of ``gas`` sent with the function call, and the - 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. + parentheses at the end perform the actual call. So + ``feed.info{value: 10, gas: 800}`` does not call the function and + the ``value`` and ``gas`` settings are lost, only + ``feed.info{value: 10, gas: 800}()`` performs the function call. 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 diff --git a/docs/grammar/SolidityParser.g4 b/docs/grammar/SolidityParser.g4 index 0dbc7339d..f7630c59e 100644 --- a/docs/grammar/SolidityParser.g4 +++ b/docs/grammar/SolidityParser.g4 @@ -19,6 +19,7 @@ sourceUnit: ( | constantVariableDeclaration | structDefinition | enumDefinition + | userDefinedValueTypeDefinition | errorDefinition )* EOF; @@ -89,6 +90,7 @@ contractBodyElement: | receiveFunctionDefinition | structDefinition | enumDefinition + | userDefinedValueTypeDefinition | stateVariableDeclaration | eventDefinition | errorDefinition @@ -247,6 +249,11 @@ 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; +/** + * Definition of a user defined value type. Can occur at top-level within a source unit or within a contract, library or interface. + */ +userDefinedValueTypeDefinition: + Type name=identifier Is elementaryTypeName[true] Semicolon; /** * The declaration of a state variable. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index a0857e038..ea415f021 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -313,7 +313,8 @@ The following are dependencies for all builds of Solidity: +===================================+=======================================================+ | `CMake`_ (version 3.13+) | Cross-platform build file generator. | +-----------------------------------+-------------------------------------------------------+ -| `Boost`_ (version 1.65+) | C++ libraries. | +| `Boost`_ (version 1.77+ on | C++ libraries. | +| Windows, 1.65+ otherwise) | | +-----------------------------------+-------------------------------------------------------+ | `Git`_ | Command-line tool for retrieving source code. | +-----------------------------------+-------------------------------------------------------+ @@ -335,6 +336,16 @@ The following are dependencies for all builds of Solidity: Starting from 0.5.10 linking against Boost 1.70+ should work without manual intervention. +.. note:: + The default build configuration requires a specific Z3 version (the latest one at the time the + code was last updated). Changes introduced between Z3 releases often result in slightly different + (but still valid) results being returned. Our SMT tests do not account for these differences and + will likely fail with a different version than the one they were written for. This does not mean + that a build using a different version is faulty. If you pass ``-DSTRICT_Z3_VERSION=OFF`` option + to CMake, you can build with any version that satisfies the requirement given in the table above. + If you do this, however, please remember to pass the ``--no-smt`` option to ``scripts/tests.sh`` + to skip the SMT tests. + Minimum Compiler Versions ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -378,6 +389,8 @@ You need to install the following dependencies for Windows builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | `Visual Studio 2019`_ (Optional) | C++ compiler and dev environment. | +-----------------------------------+-------------------------------------------------------+ +| `Boost`_ (version 1.77+) | C++ libraries. | ++-----------------------------------+-------------------------------------------------------+ If you already have one IDE and only need the compiler and libraries, you could install Visual Studio 2019 Build Tools. diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index 470bfbf31..40a727068 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -72,8 +72,9 @@ The initial content of the VFS depends on how you invoke the compiler: solc contract.sol /usr/local/dapp-bin/token.sol The source unit name of a file loaded this way is constructed by converting its path to a - canonical form and making it relative to the base path if it is located inside. - See :ref:`Base Path Normalization and Stripping ` for + canonical form and, if possible, making it relative to either the base path or one of the + include paths. + See :ref:`CLI Path Normalization and Stripping ` for a detailed description of this process. .. index:: standard JSON @@ -295,16 +296,36 @@ Here are some examples of what you can expect if they are not: The use of relative imports containing leading ``..`` segments is not recommended. The same effect can be achieved in a more reliable way by using direct imports with - :ref:`base path ` and :ref:`import remapping `. + :ref:`base path and include paths `. -.. index:: ! base path, --base-path -.. _base-path: +.. index:: ! base path, ! --base-path, ! include paths, ! --include-path +.. _base-and-include-paths: -Base Path -========= +Base Path and Include Paths +=========================== -The base path specifies the directory that the Host Filesystem Loader will load files from. -It is simply prepended to a source unit name before the filesystem lookup is performed. +The base path and include paths represent directories that the Host Filesystem Loader will load files from. +When a source unit name is passed to the loader, it prepends the base path to it and performs a +filesystem lookup. +If the lookup does not succeed, the same is done with all directories on the include path list. + +It is recommended to set the base path to the root directory of your project and use include paths to +specify additional locations that may contain libraries your project depends on. +This lets you import from these libraries in a uniform way, no matter where they are located in the +filesystem relative to your project. +For example, if you use npm to install packages and your contract imports +``@openzeppelin/contracts/utils/Strings.sol``, you can use these options to tell the compiler that +the library can be found in one of the npm package directories: + +.. code-block:: bash + + solc contract.sol \ + --base-path . \ + --include-path node_modules/ \ + --include-path /usr/local/lib/node_modules/ + +Your contract will compile (with the same exact metadata) no matter whether you install the library +in the local or global package directory or even directly under your project root. By default the base path is empty, which leaves the source unit name unchanged. When the source unit name is a relative path, this results in the file being looked up in the @@ -314,10 +335,23 @@ interpreted as absolute paths on disk. If the base path itself is relative, it is interpreted as relative to the current working directory of the compiler. -.. _base-path-normalization-and-stripping: +.. note:: -Base Path Normalization and Stripping -------------------------------------- + Include paths cannot have empty values and must be used together with a non-empty base path. + +.. note:: + + Include paths and base path can overlap as long as it does not make import resolution ambiguous. + For example, you can specify a directory inside base path as an include directory or have an + include directory that is a subdirectory of another include directory. + The compiler will only issue an error if the source unit name passed to the Host Filesystem + Loader represents an existing path when combined with multiple include paths or an include path + and base path. + +.. _cli-path-normalization-and-stripping: + +CLI Path Normalization and Stripping +------------------------------------ On the command line the compiler behaves just as you would expect from any other program: it accepts paths in a format native to the platform and relative paths are relative to the current @@ -326,7 +360,7 @@ The source unit names assigned to files whose paths are specified on the command should not change just because the project is being compiled on a different platform or because the compiler happens to have been invoked from a different directory. To achieve this, paths to source files coming from the command line must be converted to a canonical -form, and, if possible, made relative to the base path. +form, and, if possible, made relative to the base path or one of the include paths. The normalization rules are as follows: @@ -357,7 +391,8 @@ The normalization rules are as follows: You can avoid such situations by ensuring that all the files are available within a single directory tree on the same drive. -Once canonicalized, the base path is stripped from all source file paths that start with it. +After normalization the compiler attempts to make the source file path relative. +It tries the base path first and then the include paths in the order they were given. If the base path is empty or not specified, it is treated as if it was equal to the path to the current working directory (with all symbolic links resolved). The result is accepted only if the normalized directory path is the exact prefix of the normalized @@ -366,6 +401,16 @@ Otherwise the file path remains absolute. This makes the conversion unambiguous and ensures that the relative path does not start with ``../``. The resulting file path becomes the source unit name. +.. note:: + + The relative path produced by stripping must remain unique within the base path and include paths. + For example the compiler will issue an error for the following command if both + ``/project/contract.sol`` and ``/lib/contract.sol`` exist: + + .. code-block:: bash + + solc /project/contract.sol --base-path /project --include-path /lib + .. note:: Prior to version 0.8.8, CLI path stripping was not performed and the only normalization applied @@ -373,6 +418,74 @@ The resulting file path becomes the source unit name. When working with older versions of the compiler it is recommended to invoke the compiler from the base path and to only use relative paths on the command line. +.. index:: ! allowed paths, ! --allow-paths, remapping; target +.. _allowed-paths: + +Allowed Paths +============= + +As a security measure, the Host Filesystem Loader will refuse to load files from outside of a few +locations that are considered safe by default: + +- Outside of Standard JSON mode: + + - The directories containing input files listed on the command line. + - The directories used as :ref:`remapping ` targets. + If the target is not a directory (i.e does not end with ``/``, ``/.`` or ``/..``) the directory + containing the target is used instead. + - Base path and include paths. + +- In Standard JSON mode: + + - Base path and include paths. + +Additional directories can be whitelisted using the ``--allow-paths`` option. +The option accepts a comma-separated list of paths: + +.. code-block:: bash + + cd /home/user/project/ + solc token/contract.sol \ + lib/util.sol=libs/util.sol \ + --base-path=token/ \ + --include-path=/lib/ \ + --allow-paths=../utils/,/tmp/libraries + +When the compiler is invoked with the command shown above, the Host Filesystem Loader will allow +importing files from the following directories: + +- ``/home/user/project/token/`` (because ``token/`` contains the input file and also because it is + the base path), +- ``/lib/`` (because ``/lib/`` is one of the include paths), +- ``/home/user/project/libs/`` (because ``libs/`` is a directory containing a remapping target), +- ``/home/user/utils/`` (because of ``../utils/`` passed to ``--allow-paths``), +- ``/tmp/libraries/`` (because of ``/tmp/libraries`` passed to ``--allow-paths``), + +.. note:: + + The working directory of the compiler is one of the paths allowed by default only if it + happens to be the base path (or the base path is not specified or has an empty value). + +.. note:: + + The compiler does not check if allowed paths actually exist and whether they are directories. + Non-existent or empty paths are simply ignored. + If an allowed path matches a file rather than a directory, the file is considered whitelisted, too. + +.. note:: + + Allowed paths are case-sensitive even if the filesystem is not. + The case must exactly match the one used in your imports. + For example ``--allow-paths tokens`` will not match ``import "Tokens/IERC20.sol"``. + +.. warning:: + + Files and directories only reachable through symbolic links from allowed directories are not + automatically whitelisted. + For example if ``token/contract.sol`` in the example above was actually a symlink pointing at + ``/etc/passwd`` the compiler would refuse to load it unless ``/etc/`` was one of the allowed + paths too. + .. index:: ! remapping; import, ! import; remapping, ! remapping; context, ! remapping; prefix, ! remapping; target .. _import-remapping: @@ -426,6 +539,13 @@ Loader, which will then look in ``/project/dapp-bin/library/iterable_mapping.sol would need to recreate parts of your local directory structure in the VFS and (if you rely on Host Filesystem Loader) also in the host filesystem. + To avoid having your local directory structure embedded in the metadata, it is recommended to + designate the directories containing libraries as *include paths* instead. + For example, in the example above ``--include-path /home/user/packages/`` would let you use + imports starting with ``mymath/``. + Unlike remapping, the option on its own will not make ``mymath`` appear as ``@math`` but this + can be achieved by creating a symbolic link or renaming the package subdirectory. + As a more complex example, suppose you rely on a module that uses an old version of dapp-bin that you checked out to ``/project/dapp-bin_old``, then you can run: diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index eeff23b58..c7e531c43 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -353,6 +353,10 @@ code. Always use the latest version of the compiler to be notified about all recently introduced warnings. +Messages of type ``info`` issued by the compiler are not dangerous, and simply +represent extra suggestions and optional information that the compiler thinks +might be useful to the user. + Restrict the Amount of Ether ============================ diff --git a/docs/types/operators.rst b/docs/types/operators.rst index 733dc806d..29962588f 100644 --- a/docs/types/operators.rst +++ b/docs/types/operators.rst @@ -7,7 +7,7 @@ If ``a`` is an LValue (i.e. a variable or something that can be assigned to), th following operators are available as shorthands: ``a += e`` is equivalent to ``a = a + e``. The operators ``-=``, ``*=``, ``/=``, ``%=``, -``|=``, ``&=`` and ``^=`` are defined accordingly. ``a++`` and ``a--`` are equivalent +``|=``, ``&=``, ``^=``, ``<<=`` and ``>>=`` are defined accordingly. ``a++`` and ``a--`` are equivalent to ``a += 1`` / ``a -= 1`` but the expression itself still has the previous value of ``a``. In contrast, ``--a`` and ``++a`` have the same effect on ``a`` but return the value after the change. diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 776cddf19..c02eea8a9 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -66,7 +66,7 @@ Shifts ^^^^^^ The result of a shift operation has the type of the left operand, truncating the result to match the type. -The right operand must be of unsigned type, trying to shift by an signed type will produce a compilation error. +The right operand must be of unsigned type, trying to shift by a signed type will produce a compilation error. Shifts can be "simulated" using multiplication by powers of two in the following way. Note that the truncation to the type of the left operand is always performed at the end, but not mentioned explicitly. @@ -587,11 +587,14 @@ 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``. +Using ``type(NameOfEnum).min`` and ``type(NameOfEnum).max`` you can get the +smallest and respectively largest value of the given enum. + .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.4.16 <0.9.0; + pragma solidity ^0.8.8; contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } @@ -612,11 +615,83 @@ subsequent unsigned integer values starting from ``0``. function getDefaultChoice() public pure returns (uint) { return uint(defaultChoice); } + + function getLargestValue() public pure returns (ActionChoices) { + return type(ActionChoices).max; + } + + function getSmallestValue() public pure returns (ActionChoices) { + return type(ActionChoices).min; + } } .. note:: Enums can also be declared on the file level, outside of contract or library definitions. +.. index:: ! user defined value type, custom type + +.. _user-defined-value-types: + +User Defined Value Types +------------------------ + +A user defined value type allows creating a zero cost abstraction over an elementary value type. +This is similar to an alias, but with stricter type requirements. + +A user defined value type is defined using ``type C is V``, where ``C`` is the name of the newly +introduced type and ``V`` has to be a built-in value type (the "underlying type"). The function +``C.wrap`` is used to convert from the underlying type to the custom type. Similarly, the +function ``C.unwrap`` is used to convert from the custom type to the underlying type. + +The type ``C`` does not have any operators or bound member functions. In particular, even the +operator ``==`` is not defined. Explicit and implicit conversions to and from other types are +disallowed. + +The data-representation of values of such types are inherited from the underlying type +and the underlying type is also used in the ABI. + +The following example illustrates a custom type ``UFixed256x18`` representing a decimal fixed point +type with 18 decimals and a minimal library to do arithmetic operations on the type. + + +.. code-block:: solidity + + // SPDX-License-Identifier: GPL-3.0 + pragma solidity ^0.8.8; + + // Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type. + type UFixed256x18 is uint256; + + /// A minimal library to do fixed point operations on UFixed256x18. + library FixedMath { + uint constant multiplier = 10**18; + + /// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked + /// arithmetic on uint256. + function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b)); + } + /// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked + /// arithmetic on uint256. + function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b); + } + /// Take the floor of a UFixed256x18 number. + /// @return the largest integer that does not exceed `a`. + function floor(UFixed256x18 a) internal pure returns (uint256) { + return UFixed256x18.unwrap(a) / multiplier; + } + /// Turns a uint256 into a UFixed256x18 of the same value. + /// Reverts if the integer is too large. + function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(a * multiplier); + } + } + +Notice how ``UFixed256x18.wrap`` and ``FixedMath.toUFixed256x18`` have the same signature but +perform two very different operations: The ``UFixed256x18.wrap`` function returns a ``UFixed256x18`` +that has the same data representation as the input, whereas ``toUFixed256x18`` returns a +``UFixed256x18`` that has the same numerical value. .. index:: ! function type, ! type; function diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 2ca338041..1b14187ca 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -33,7 +33,7 @@ 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 -.. index:: allowed paths, --allow-paths, base path, --base-path +.. index:: allowed paths, --allow-paths, base path, --base-path, include paths, --include-path Base Path and Import Remapping ------------------------------ @@ -47,16 +47,13 @@ it is also possible to provide :ref:`path redirects ` using `` This essentially instructs the compiler to search for anything starting with ``github.com/ethereum/dapp-bin/`` under ``/usr/local/lib/dapp-bin``. -``solc`` will not read files from the filesystem that lie outside of -the remapping targets and outside of the directories where explicitly specified source -files reside, so things like ``import "/etc/passwd";`` only work if you add ``/=/`` as a remapping. When accessing the filesystem to search for imports, :ref:`paths that do not start with ./ -or ../ ` are treated as relative to the directory specified using -``--base-path`` option (or the current working directory if base path is not specified). -Furthermore, the part added via ``--base-path`` will not appear in the contract metadata. +or ../ ` are treated as relative to the directories specified using +``--base-path`` and ``--include-path`` options (or the current working directory if base path is not specified). +Furthermore, the part of the path added via these options will not appear in the contract metadata. -For security reasons the compiler has restrictions on what directories it can access. +For security reasons the compiler has :ref:`restrictions on what directories it can access `. Directories of source files specified on the command line and target paths of remappings are automatically allowed to be accessed by the file reader, but everything else is rejected by default. @@ -437,7 +434,7 @@ Output Description .. code-block:: javascript { - // Optional: not present if no errors/warnings were encountered + // Optional: not present if no errors/warnings/infos were encountered "errors": [ { // Optional: Location within the source file. @@ -460,7 +457,7 @@ Output Description "type": "TypeError", // Mandatory: Component where the error originated, such as "general", "ewasm", etc. "component": "general", - // Mandatory ("error" or "warning") + // Mandatory ("error", "warning" or "info", but please note that this may be extended in the future) "severity": "error", // Optional: unique code for the cause of the error "errorCode": "3141", @@ -604,6 +601,7 @@ Error Types 11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue. 12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue. 13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. +14. ``Info``: Information that the compiler thinks the user might find useful, but is not dangerous and does not necessarily need to be addressed. .. _compiler-tools: diff --git a/docs/yul.rst b/docs/yul.rst index d0126e65b..e4819ff22 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -174,9 +174,32 @@ whitespace, i.e. there is no terminating ``;`` or newline required. Literals -------- -As literals, you can use integer constants in decimal or hexadecimal notation -or strings as ASCII (`"abc"`) or HEX strings (`hex"616263"`) of up to -32 bytes length. +As literals, you can use: + +- Integer constants in decimal or hexadecimal notation. + +- ASCII strings (e.g. ``"abc"``), which may contain hex escapes ``\xNN`` and Unicode escapes ``\uNNNN`` where ``N`` are hexadecimal digits. + +- Hex strings (e.g. ``hex"616263"``). + +In the EVM dialect of Yul, literals represent 256-bit words as follows: + +- Decimal or hexadecimal constants must be less than ``2**256``. + They represent the 256-bit word with that value as an unsigned integer in big endian encoding. + +- An ASCII string is first viewed as a byte sequence, by viewing + a non-escape ASCII character as a single byte whose value is the ASCII code, + an escape ``\xNN`` as single byte with that value, and + an escape ``\uNNNN`` as the UTF-8 sequence of bytes for that code point. + The byte sequence must not exceed 32 bytes. + The byte sequence is padded with zeros on the right to reach 32 bytes in length; + in other words, the string is stored left-aligned. + The padded byte sequence represents a 256-bit word whose most significant 8 bits are the ones from the first byte, + i.e. the bytes are interpreted in big endian form. + +- A hex string is first viewed as a byte sequence, by viewing + each pair of contiguous hex digits as a byte. + The byte sequence must not exceed 32 bytes (i.e. 64 hex digits), and is treated as above. When compiling for the EVM, this will be translated into an appropriate ``PUSHi`` instruction. In the following example, @@ -184,8 +207,7 @@ appropriate ``PUSHi`` instruction. In the following example, bitwise ``and`` with the string "abc" is computed. The final value is assigned to a local variable called ``x``. -Strings are stored left-aligned and cannot be longer than 32 bytes. -The limit does not apply to string literals passed to builtin functions that require +The 32-byte limit above does not apply to string literals passed to builtin functions that require literal arguments (e.g. ``setimmutable`` or ``loadimmutable``). Those strings never end up in the generated bytecode. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index dad9635d7..7884dd191 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -31,12 +31,16 @@ #include #include +#include #include #include -#include #include +#include + +#include +#include using namespace std; using namespace solidity; @@ -55,7 +59,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i) return m_items.back(); } -unsigned Assembly::bytesRequired(unsigned subTagSize) const +unsigned Assembly::codeSize(unsigned subTagSize) const { for (unsigned tagSize = subTagSize; true; ++tagSize) { @@ -65,7 +69,7 @@ unsigned Assembly::bytesRequired(unsigned subTagSize) const for (AssemblyItem const& i: m_items) ret += i.bytesRequired(tagSize); - if (util::bytesRequired(ret) <= tagSize) + if (numberEncodingSize(ret) <= tagSize) return static_cast(ret); } } @@ -82,16 +86,7 @@ string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& if (it == _sourceCodes.end()) return {}; - string const& source = it->second; - if (static_cast(_location.start) >= source.size()) - return {}; - - string cut = source.substr(static_cast(_location.start), static_cast(_location.end - _location.start)); - auto newLinePos = cut.find_first_of("\n"); - if (newLinePos != string::npos) - cut = cut.substr(0, newLinePos) + "..."; - - return cut; + return CharStream::singleLineSnippet(it->second, _location); } class Functionalizer @@ -185,7 +180,7 @@ void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap co _out << _prefix << "stop" << endl; for (auto const& i: m_data) if (u256(i.first) >= m_subs.size()) - _out << _prefix << "data_" << toHex(u256(i.first)) << " " << toHex(i.second) << endl; + _out << _prefix << "data_" << toHex(u256(i.first)) << " " << util::toHex(i.second) << endl; for (size_t i = 0; i < m_subs.size(); ++i) { @@ -196,7 +191,7 @@ void Assembly::assemblyStream(ostream& _out, string const& _prefix, StringMap co } if (m_auxiliaryData.size() > 0) - _out << endl << _prefix << "auxdata: 0x" << toHex(m_auxiliaryData) << endl; + _out << endl << _prefix << "auxdata: 0x" << util::toHex(m_auxiliaryData) << endl; } string Assembly::assemblyString(StringMap const& _sourceCodes) const @@ -208,7 +203,7 @@ string Assembly::assemblyString(StringMap const& _sourceCodes) const Json::Value Assembly::createJsonValue(string _name, int _source, int _begin, int _end, string _value, string _jumpType) { - Json::Value value; + Json::Value value{Json::objectValue}; value["name"] = _name; value["source"] = _source; value["begin"] = _begin; @@ -230,8 +225,9 @@ string Assembly::toStringInHex(u256 _value) Json::Value Assembly::assemblyJSON(map const& _sourceIndices) const { Json::Value root; + root[".code"] = Json::arrayValue; - Json::Value& collection = root[".code"] = Json::arrayValue; + Json::Value& collection = root[".code"]; for (AssemblyItem const& i: m_items) { int sourceIndex = -1; @@ -258,10 +254,6 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) collection.append( createJsonValue("PUSH", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()), i.getJumpTypeAsString())); break; - case PushString: - collection.append( - createJsonValue("PUSH tag", sourceIndex, i.location().start, i.location().end, m_strings.at(h256(i.data())))); - break; case PushTag: if (i.data() == 0) collection.append( @@ -320,7 +312,7 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) collection.append(createJsonValue("PUSH data", sourceIndex, i.location().start, i.location().end, toStringInHex(i.data()))); break; case VerbatimBytecode: - collection.append(createJsonValue("VERBATIM", sourceIndex, i.location().start, i.location().end, toHex(i.verbatimData()))); + collection.append(createJsonValue("VERBATIM", sourceIndex, i.location().start, i.location().end, util::toHex(i.verbatimData()))); break; default: assertThrow(false, InvalidOpcode, ""); @@ -329,10 +321,11 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) if (!m_data.empty() || !m_subs.empty()) { - Json::Value& data = root[".data"] = Json::objectValue; + root[".data"] = Json::objectValue; + Json::Value& data = root[".data"]; for (auto const& i: m_data) if (u256(i.first) >= m_subs.size()) - data[toStringInHex((u256)i.first)] = toHex(i.second); + data[toStringInHex((u256)i.first)] = util::toHex(i.second); for (size_t i = 0; i < m_subs.size(); ++i) { @@ -343,7 +336,7 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) } if (m_auxiliaryData.size() > 0) - root[".auxdata"] = toHex(m_auxiliaryData); + root[".auxdata"] = util::toHex(m_auxiliaryData); return root; } @@ -409,18 +402,21 @@ Assembly& Assembly::optimise(OptimiserSettings const& _settings) return *this; } -map Assembly::optimiseInternal( +map const& Assembly::optimiseInternal( OptimiserSettings const& _settings, std::set _tagsReferencedFromOutside ) { + if (m_tagReplacements) + return *m_tagReplacements; + // Run optimisation for sub-assemblies. for (size_t subId = 0; subId < m_subs.size(); ++subId) { OptimiserSettings settings = _settings; // Disable creation mode for sub-assemblies. settings.isCreation = false; - map subTagReplacements = m_subs[subId]->optimiseInternal( + map const& subTagReplacements = m_subs[subId]->optimiseInternal( settings, JumpdestRemover::referencedTags(m_items, subId) ); @@ -546,7 +542,8 @@ map Assembly::optimiseInternal( *this ); - return tagReplacements; + m_tagReplacements = move(tagReplacements); + return *m_tagReplacements; } LinkerObject const& Assembly::assemble() const @@ -597,20 +594,20 @@ LinkerObject const& Assembly::assemble() const "Cannot push and assign immutables in the same assembly subroutine." ); - unsigned bytesRequiredForCode = bytesRequired(static_cast(subTagSize)); + unsigned bytesRequiredForCode = codeSize(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); + unsigned bytesPerTag = numberEncodingSize(bytesRequiredForCode); uint8_t tagPush = static_cast(pushInstruction(bytesPerTag)); unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + static_cast(m_auxiliaryData.size()); for (auto const& sub: m_subs) bytesRequiredIncludingData += static_cast(sub->assemble().bytecode.size()); - unsigned bytesPerDataRef = util::bytesRequired(bytesRequiredIncludingData); + unsigned bytesPerDataRef = numberEncodingSize(bytesRequiredIncludingData); uint8_t dataRefPush = static_cast(pushInstruction(bytesPerDataRef)); ret.bytecode.reserve(bytesRequiredIncludingData); @@ -625,22 +622,9 @@ LinkerObject const& Assembly::assemble() const case Operation: ret.bytecode.push_back(static_cast(i.instruction())); break; - case PushString: - { - ret.bytecode.push_back(static_cast(Instruction::PUSH32)); - unsigned ii = 0; - for (auto j: m_strings.at(h256(i.data()))) - if (++ii > 32) - break; - else - ret.bytecode.push_back(uint8_t(j)); - while (ii++ < 32) - ret.bytecode.push_back(0); - break; - } case Push: { - unsigned b = max(1, util::bytesRequired(i.data())); + unsigned b = max(1, numberEncodingSize(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); @@ -670,7 +654,7 @@ 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)); - unsigned b = max(1, util::bytesRequired(s)); + unsigned b = max(1, numberEncodingSize(s)); ret.bytecode.push_back(static_cast(pushInstruction(b))); ret.bytecode.resize(ret.bytecode.size() + b); bytesRef byr(&ret.bytecode.back() + 1 - b, b); @@ -775,15 +759,23 @@ LinkerObject const& Assembly::assemble() const assertThrow(tagId < tagPositions.size(), AssemblyException, "Reference to non-existing tag."); size_t pos = tagPositions[tagId]; assertThrow(pos != numeric_limits::max(), AssemblyException, "Reference to tag without position."); - assertThrow(util::bytesRequired(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space."); + assertThrow(numberEncodingSize(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space."); bytesRef r(ret.bytecode.data() + i.first, bytesPerTag); toBigEndian(pos, r); } for (auto const& [name, tagInfo]: m_namedTags) { size_t position = m_tagPositionsInBytecode.at(tagInfo.id); + optional tagIndex; + for (auto&& [index, item]: m_items | ranges::views::enumerate) + if (item.type() == Tag && static_cast(item.data()) == tagInfo.id) + { + tagIndex = index; + break; + } ret.functionDebugData[name] = { position == numeric_limits::max() ? nullopt : optional{position}, + tagIndex, tagInfo.sourceID, tagInfo.params, tagInfo.returns diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 4a0779ef7..1d91c2f90 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -165,9 +165,9 @@ protected: /// Does the same operations as @a optimise, but should only be applied to a sub and /// returns the replaced tags. Also takes an argument containing the tags of this assembly /// that are referenced in a super-assembly. - std::map optimiseInternal(OptimiserSettings const& _settings, std::set _tagsReferencedFromOutside); + std::map const& optimiseInternal(OptimiserSettings const& _settings, std::set _tagsReferencedFromOutside); - unsigned bytesRequired(unsigned subTagSize) const; + unsigned codeSize(unsigned subTagSize) const; private: static Json::Value createJsonValue( @@ -210,6 +210,10 @@ protected: /// This map is used only for sub-assemblies which are not direct sub-assemblies (where path is having more than one value). std::map, size_t> m_subPaths; + /// Contains the tag replacements relevant for super-assemblies. + /// If set, it means the optimizer has run and we will not run it again. + std::optional> m_tagReplacements; + mutable LinkerObject m_assembledObject; mutable std::vector m_tagPositionsInBytecode; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index 75c0e4dca..a34870eda 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -21,11 +21,13 @@ #include #include +#include #include #include #include #include +#include using namespace std; using namespace solidity; @@ -70,10 +72,8 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength) const case Operation: case Tag: // 1 byte for the JUMPDEST return 1; - case PushString: - return 1 + 32; case Push: - return 1 + max(1, util::bytesRequired(data())); + return 1 + max(1, numberEncodingSize(data())); case PushSubSize: case PushProgramSize: return 1 + 4; // worst case: a 16MB program @@ -118,7 +118,6 @@ size_t AssemblyItem::returnValues() const case Operation: return static_cast(instructionInfo(instruction()).ret); case Push: - case PushString: case PushTag: case PushData: case PushSub: @@ -147,7 +146,6 @@ bool AssemblyItem::canBeFunctional() const case Operation: return !isDupInstruction(instruction()) && !isSwapInstruction(instruction()); case Push: - case PushString: case PushTag: case PushData: case PushSub: @@ -193,10 +191,7 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const break; } case Push: - text = toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add); - break; - case PushString: - text = string("data_") + util::toHex(data()); + text = toHex(toCompactBigEndian(data(), 1), util::HexPrefix::Add); break; case PushTag: { @@ -214,7 +209,7 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const text = string("tag_") + to_string(static_cast(data())) + ":"; break; case PushData: - text = string("data_") + util::toHex(data()); + text = string("data_") + toHex(data()); break; case PushSub: case PushSubSize: @@ -233,16 +228,16 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const text = string("bytecodeSize"); break; case PushLibraryAddress: - text = string("linkerSymbol(\"") + util::toHex(data()) + string("\")"); + text = string("linkerSymbol(\"") + toHex(data()) + string("\")"); break; case PushDeployTimeAddress: text = string("deployTimeAddress()"); break; case PushImmutable: - text = string("immutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + text = string("immutable(\"") + "0x" + util::toHex(toCompactBigEndian(data(), 1)) + "\")"; break; case AssignImmutable: - text = string("assignImmutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + text = string("assignImmutable(\"") + "0x" + util::toHex(toCompactBigEndian(data(), 1)) + "\")"; break; case UndefinedItem: assertThrow(false, AssemblyException, "Invalid assembly item."); @@ -276,9 +271,6 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item) case Push: _out << " PUSH " << hex << _item.data() << dec; break; - case PushString: - _out << " PushString" << hex << (unsigned)_item.data() << dec; - break; case PushTag: { size_t subId = _item.splitForeignPushTag().first; diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 5d810c02c..f1b2b5043 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -38,7 +38,6 @@ enum AssemblyItemType UndefinedItem, Operation, Push, - PushString, PushTag, PushSub, PushSubSize, diff --git a/libevmasm/BlockDeduplicator.h b/libevmasm/BlockDeduplicator.h index ef131ae52..fb83b317e 100644 --- a/libevmasm/BlockDeduplicator.h +++ b/libevmasm/BlockDeduplicator.h @@ -25,6 +25,8 @@ #pragma once #include +#include + #include #include @@ -64,9 +66,14 @@ private: /// Iterator that skips tags and skips to the end if (all branches of) the control /// flow does not continue to the next instruction. /// If the arguments are supplied to the constructor, replaces items on the fly. - struct BlockIterator: std::iterator + struct BlockIterator { public: + using iterator_category = std::forward_iterator_tag; + using value_type = AssemblyItem const; + using difference_type = std::ptrdiff_t; + using pointer = AssemblyItem const*; + using reference = AssemblyItem const&; BlockIterator( AssemblyItems::const_iterator _it, AssemblyItems::const_iterator _end, diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index a6bb342bc..6800baef3 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -133,7 +133,7 @@ bigint LiteralMethod::gasNeeded() const return combineGas( simpleRunGas({Instruction::PUSH1}), // PUSHX plus data - (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(util::toCompactBigEndian(m_value, 1)), + (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)), 0 ); } @@ -146,13 +146,13 @@ bigint CodeCopyMethod::gasNeeded() const // Data gas for copy routines: Some bytes are zero, but we ignore them. bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas), // Data gas for data itself - dataGas(util::toBigEndian(m_value)) + dataGas(toBigEndian(m_value)) ); } AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const { - bytes data = util::toBigEndian(m_value); + bytes data = toBigEndian(m_value); assertThrow(data.size() == 32, OptimizerException, "Invalid number encoding."); AssemblyItems actualCopyRoutine = copyRoutine(); actualCopyRoutine[4] = _assembly.newData(data); @@ -192,7 +192,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value) if (_value < 0x10000) // Very small value, not worth computing return AssemblyItems{_value}; - else if (util::bytesRequired(~_value) < util::bytesRequired(_value)) + else if (numberEncodingSize(~_value) < numberEncodingSize(_value)) // Negated is shorter to represent return findRepresentation(~_value) + AssemblyItems{Instruction::NOT}; else diff --git a/libevmasm/ControlFlowGraph.h b/libevmasm/ControlFlowGraph.h index d27702c2a..ef6dd2f88 100644 --- a/libevmasm/ControlFlowGraph.h +++ b/libevmasm/ControlFlowGraph.h @@ -24,12 +24,14 @@ #pragma once -#include -#include #include #include #include +#include +#include +#include + namespace solidity::evmasm { diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index 8df523842..c17ff9c29 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -29,6 +29,7 @@ #include #include +#include using namespace std; using namespace solidity; diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 57a898f65..3ad9ba084 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -47,7 +47,6 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Push: case PushTag: case PushData: - case PushString: case PushSub: case PushSubSize: case PushProgramSize: diff --git a/libevmasm/Inliner.cpp b/libevmasm/Inliner.cpp index d0f1a06f5..8d95bdc3b 100644 --- a/libevmasm/Inliner.cpp +++ b/libevmasm/Inliner.cpp @@ -36,7 +36,7 @@ #include #include - +#include using namespace std; using namespace solidity; diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 10035b854..ad6bc9796 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace solidity::evmasm @@ -301,7 +302,7 @@ bool isValidInstruction(Instruction _inst); extern const std::map c_instructions; /// Iterate through EVM code and call a function on each instruction. -void eachInstruction(bytes const& _mem, std::function const& _onInstruction); +void eachInstruction(bytes const& _mem, std::function const& _onInstruction); /// Convert from EVM code to simple EVM assembly language. std::string disassemble(bytes const& _mem, std::string const& _delimiter = " "); diff --git a/libevmasm/JumpdestRemover.cpp b/libevmasm/JumpdestRemover.cpp index c4901700f..2c0c5875e 100644 --- a/libevmasm/JumpdestRemover.cpp +++ b/libevmasm/JumpdestRemover.cpp @@ -24,6 +24,8 @@ #include +#include + using namespace std; using namespace solidity; using namespace solidity::util; diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 67445cb75..841affab0 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -415,7 +415,7 @@ KnownState::Id KnownState::applyKeccak256( { bytes data; for (Id a: arguments) - data += util::toBigEndian(*m_expressionClasses->knownConstant(a)); + data += toBigEndian(*m_expressionClasses->knownConstant(a)); data.resize(length); v = m_expressionClasses->find(AssemblyItem(u256(util::keccak256(data)), _location)); } diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h index 235b6361b..723170610 100644 --- a/libevmasm/KnownState.h +++ b/libevmasm/KnownState.h @@ -24,14 +24,6 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include - #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wredeclared-class-member" @@ -58,6 +50,15 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include + namespace solidity::langutil { struct SourceLocation; diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index e512dd7ab..c23426882 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -48,6 +48,7 @@ struct LinkerObject struct FunctionDebugData { std::optional bytecodeOffset; + std::optional instructionIndex; std::optional sourceID; size_t params = {}; size_t returns = {}; diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 6df80dac8..0ee0b5efd 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -112,7 +112,7 @@ struct PushPop: SimplePeepholeOptimizerMethod auto t = _push.type(); return _pop == Instruction::POP && ( SemanticInformation::isDupInstruction(_push) || - t == Push || t == PushString || t == PushTag || t == PushSub || + t == Push || t == PushTag || t == PushSub || t == PushSubSize || t == PushProgramSize || t == PushData || t == PushLibraryAddress ); } diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index fda7f0c7f..ec0970e65 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -51,7 +51,7 @@ template S modWorkaround(S const& _a, S const& _b) } // This works around a bug fixed with Boost 1.64. -// https://www.boost.org/doc/libs/1_68_0/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64 +// https://www.boost.org/doc/libs/release/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64 template S shlWorkaround(S const& _x, unsigned _amount) { return u256((bigint(_x) << _amount) & u256(-1)); diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index d3359c4f7..41dc07fcf 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -41,7 +41,6 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool case VerbatimBytecode: return true; case Push: - case PushString: case PushTag: case PushSub: case PushSubSize: diff --git a/liblangutil/CharStream.cpp b/liblangutil/CharStream.cpp index 8ab99fca8..3b30bad77 100644 --- a/liblangutil/CharStream.cpp +++ b/liblangutil/CharStream.cpp @@ -128,3 +128,19 @@ string_view CharStream::text(SourceLocation const& _location) const static_cast(_location.end - _location.start) ); } + +string CharStream::singleLineSnippet(string const& _sourceCode, SourceLocation const& _location) +{ + if (!_location.hasText()) + return {}; + + if (static_cast(_location.start) >= _sourceCode.size()) + return {}; + + string cut = _sourceCode.substr(static_cast(_location.start), static_cast(_location.end - _location.start)); + auto newLinePos = cut.find_first_of("\n\r"); + if (newLinePos != string::npos) + cut = cut.substr(0, newLinePos) + "..."; + + return cut; +} diff --git a/liblangutil/CharStream.h b/liblangutil/CharStream.h index 4b729771f..761cece31 100644 --- a/liblangutil/CharStream.h +++ b/liblangutil/CharStream.h @@ -118,6 +118,15 @@ public: /// Returns an empty string view if the source location does not `hasText()`. std::string_view text(SourceLocation const& _location) const; + /// @returns the first line of the referenced source fragment. If the fragment is longer than + /// one line, appends an ellipsis to indicate that. + std::string singleLineSnippet(SourceLocation const& _location) const + { + return singleLineSnippet(m_source, _location); + } + + static std::string singleLineSnippet(std::string const& _sourceCode, SourceLocation const& _location); + private: std::string m_source; std::string m_name; diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 728c9dad9..02487034a 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -94,6 +94,16 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type) if (m_warningCount >= c_maxWarningsAllowed) return true; } + else if (_type == Error::Type::Info) + { + m_infoCount++; + + if (m_infoCount == c_maxInfosAllowed) + m_errorList.push_back(make_shared(2833_error, Error::Type::Info, "There are more than 256 infos. Ignoring the rest.")); + + if (m_infoCount >= c_maxInfosAllowed) + return true; + } else { m_errorCount++; @@ -242,3 +252,12 @@ void ErrorReporter::docstringParsingError(ErrorId _error, SourceLocation const& _description ); } + +void ErrorReporter::info( + ErrorId _error, + SourceLocation const& _location, + string const& _description +) +{ + error(_error, Error::Type::Info, _location, _description); +} diff --git a/liblangutil/ErrorReporter.h b/liblangutil/ErrorReporter.h index 7cdce0caa..6f91f0713 100644 --- a/liblangutil/ErrorReporter.h +++ b/liblangutil/ErrorReporter.h @@ -63,6 +63,8 @@ public: SecondarySourceLocation const& _secondaryLocation ); + void info(ErrorId _error, SourceLocation const& _location, std::string const& _description); + void error( ErrorId _error, Error::Type _type, @@ -118,13 +120,13 @@ public: void clear(); - /// @returns true iff there is any error (ignores warnings). + /// @returns true iff there is any error (ignores warnings and infos). bool hasErrors() const { return m_errorCount > 0; } - /// @returns the number of errors (ignores warnings). + /// @returns the number of errors (ignores warnings and infos). unsigned errorCount() const { return m_errorCount; @@ -183,9 +185,11 @@ private: unsigned m_errorCount = 0; unsigned m_warningCount = 0; + unsigned m_infoCount = 0; unsigned const c_maxWarningsAllowed = 256; unsigned const c_maxErrorsAllowed = 256; + unsigned const c_maxInfosAllowed = 256; }; } diff --git a/liblangutil/Exceptions.cpp b/liblangutil/Exceptions.cpp index 3caa81b1d..34aa69803 100644 --- a/liblangutil/Exceptions.cpp +++ b/liblangutil/Exceptions.cpp @@ -47,6 +47,9 @@ Error::Error( case Type::DocstringParsingError: m_typeName = "DocstringParsingError"; break; + case Type::Info: + m_typeName = "Info"; + break; case Type::ParserError: m_typeName = "ParserError"; break; diff --git a/liblangutil/Exceptions.h b/liblangutil/Exceptions.h index c7385bac8..6946ff463 100644 --- a/liblangutil/Exceptions.h +++ b/liblangutil/Exceptions.h @@ -28,6 +28,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -45,18 +49,68 @@ struct FatalError: virtual util::Exception {}; struct UnimplementedFeatureError: virtual util::Exception {}; struct InvalidAstError: virtual util::Exception {}; + /// Assertion that throws an InternalCompilerError containing the given description if it is not met. -#define solAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::solidity::langutil::InternalCompilerError, DESCRIPTION) +#if !BOOST_PP_VARIADICS_MSVC +#define solAssert(...) BOOST_PP_OVERLOAD(solAssert_,__VA_ARGS__)(__VA_ARGS__) +#else +#define solAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(solAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) +#endif -#define solUnimplementedAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::solidity::langutil::UnimplementedFeatureError, DESCRIPTION) +#define solAssert_1(CONDITION) \ + solAssert_2(CONDITION, "") +#define solAssert_2(CONDITION, DESCRIPTION) \ + assertThrowWithDefaultDescription( \ + CONDITION, \ + ::solidity::langutil::InternalCompilerError, \ + DESCRIPTION, \ + "Solidity assertion failed" \ + ) + + +/// Assertion that throws an UnimplementedFeatureError containing the given description if it is not met. +#if !BOOST_PP_VARIADICS_MSVC +#define solUnimplementedAssert(...) BOOST_PP_OVERLOAD(solUnimplementedAssert_,__VA_ARGS__)(__VA_ARGS__) +#else +#define solUnimplementedAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(solUnimplementedAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) +#endif + +#define solUnimplementedAssert_1(CONDITION) \ + solUnimplementedAssert_2(CONDITION, "") + +#define solUnimplementedAssert_2(CONDITION, DESCRIPTION) \ + assertThrowWithDefaultDescription( \ + CONDITION, \ + ::solidity::langutil::UnimplementedFeatureError, \ + DESCRIPTION, \ + "Unimplemented feature" \ + ) + + +/// Helper that unconditionally reports an unimplemented feature. #define solUnimplemented(DESCRIPTION) \ solUnimplementedAssert(false, DESCRIPTION) -#define astAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::solidity::langutil::InvalidAstError, DESCRIPTION) + +/// Assertion that throws an InvalidAstError containing the given description if it is not met. +#if !BOOST_PP_VARIADICS_MSVC +#define astAssert(...) BOOST_PP_OVERLOAD(astAssert_,__VA_ARGS__)(__VA_ARGS__) +#else +#define astAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(astAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) +#endif + +#define astAssert_1(CONDITION) \ + astAssert_2(CONDITION, "") + +#define astAssert_2(CONDITION, DESCRIPTION) \ + assertThrowWithDefaultDescription( \ + CONDITION, \ + ::solidity::langutil::InvalidAstError, \ + DESCRIPTION, \ + "AST assertion failed" \ + ) + using errorSourceLocationInfo = std::pair; @@ -120,7 +174,15 @@ public: ParserError, TypeError, SyntaxError, - Warning + Warning, + Info + }; + + enum class Severity + { + Error, + Warning, + Info }; Error( @@ -139,21 +201,63 @@ public: static Error const* containsErrorOfType(ErrorList const& _list, Error::Type _type) { for (auto e: _list) - { if (e->type() == _type) return e.get(); - } return nullptr; } - static bool containsOnlyWarnings(ErrorList const& _list) + + static Severity errorSeverity(Type _type) + { + if (_type == Type::Info) + return Severity::Info; + if (_type == Type::Warning) + return Severity::Warning; + return Severity::Error; + } + + static bool isError(Severity _severity) + { + return _severity == Severity::Error; + } + + static bool isError(Type _type) + { + return isError(errorSeverity(_type)); + } + + static bool containsErrors(ErrorList const& _list) { for (auto e: _list) - { - if (e->type() != Type::Warning) - return false; - } - return true; + if (isError(e->type())) + return true; + return false; } + + static std::string formatErrorSeverity(Severity _severity) + { + if (_severity == Severity::Info) + return "Info"; + if (_severity == Severity::Warning) + return "Warning"; + solAssert(isError(_severity), ""); + return "Error"; + } + + static std::string formatErrorSeverityLowercase(Severity _severity) + { + switch (_severity) + { + case Severity::Info: + return "info"; + case Severity::Warning: + return "warning"; + case Severity::Error: + solAssert(isError(_severity), ""); + return "error"; + } + solAssert(false, ""); + } + private: ErrorId m_errorId; Type m_type; diff --git a/liblangutil/ParserBase.h b/liblangutil/ParserBase.h index 6c5ce081b..f72400f6c 100644 --- a/liblangutil/ParserBase.h +++ b/liblangutil/ParserBase.h @@ -104,7 +104,7 @@ protected: void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description); std::shared_ptr m_scanner; - /// The reference to the list of errors and warning to add errors/warnings during parsing + /// The reference to the list of errors, warnings and infos to add errors/warnings/infos during parsing ErrorReporter& m_errorReporter; /// Current recursion depth during parsing. size_t m_recursionDepth = 0; diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 87be458fd..fede6d3f8 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -59,6 +59,7 @@ #include #include #include +#include using namespace std; diff --git a/liblangutil/SemVerHandler.cpp b/liblangutil/SemVerHandler.cpp index ede1cd7be..ceb562aed 100644 --- a/liblangutil/SemVerHandler.cpp +++ b/liblangutil/SemVerHandler.cpp @@ -26,6 +26,7 @@ #include #include +#include using namespace std; using namespace solidity; diff --git a/liblangutil/SourceReferenceExtractor.cpp b/liblangutil/SourceReferenceExtractor.cpp index 717e590df..373ad6b18 100644 --- a/liblangutil/SourceReferenceExtractor.cpp +++ b/liblangutil/SourceReferenceExtractor.cpp @@ -30,7 +30,7 @@ using namespace solidity::langutil; SourceReferenceExtractor::Message SourceReferenceExtractor::extract( CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, - string _category + string _severity ) { SourceLocation const* location = boost::get_error_info(_exception); @@ -44,7 +44,7 @@ SourceReferenceExtractor::Message SourceReferenceExtractor::extract( for (auto const& info: secondaryLocation->infos) secondary.emplace_back(extract(_charStreamProvider, &info.second, info.first)); - return Message{std::move(primary), _category, std::move(secondary), nullopt}; + return Message{std::move(primary), _severity, std::move(secondary), nullopt}; } SourceReferenceExtractor::Message SourceReferenceExtractor::extract( @@ -52,8 +52,8 @@ SourceReferenceExtractor::Message SourceReferenceExtractor::extract( Error const& _error ) { - string category = (_error.type() == Error::Type::Warning) ? "Warning" : "Error"; - Message message = extract(_charStreamProvider, _error, category); + string severity = Error::formatErrorSeverity(Error::errorSeverity(_error.type())); + Message message = extract(_charStreamProvider, _error, severity); message.errorId = _error.errorId(); return message; } diff --git a/liblangutil/SourceReferenceExtractor.h b/liblangutil/SourceReferenceExtractor.h index d26a941f5..6aeb6065e 100644 --- a/liblangutil/SourceReferenceExtractor.h +++ b/liblangutil/SourceReferenceExtractor.h @@ -41,7 +41,7 @@ struct LineColumn struct SourceReference { - std::string message; ///< A message that relates to this source reference (such as a warning or an error message). + std::string message; ///< A message that relates to this source reference (such as a warning, info or an error message). std::string sourceName; ///< Underlying source name (for example the filename). LineColumn position; ///< Actual (error) position this source reference is surrounding. bool multiline = {false}; ///< Indicates whether the actual SourceReference is truncated to one line. @@ -64,12 +64,12 @@ namespace SourceReferenceExtractor struct Message { SourceReference primary; - std::string category; // "Error", "Warning", ... + std::string severity; // "Error", "Warning", "Info", ... std::vector secondary; std::optional errorId; }; - Message extract(CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, std::string _category); + Message extract(CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, std::string _severity); Message extract(CharStreamProvider const& _charStreamProvider, Error const& _error); SourceReference extract(CharStreamProvider const& _charStreamProvider, SourceLocation const* _location, std::string message = ""); } diff --git a/liblangutil/SourceReferenceFormatter.cpp b/liblangutil/SourceReferenceFormatter.cpp index 20d0f3306..623e13dd1 100644 --- a/liblangutil/SourceReferenceFormatter.cpp +++ b/liblangutil/SourceReferenceFormatter.cpp @@ -164,7 +164,7 @@ void SourceReferenceFormatter::printSourceLocation(SourceReference const& _ref) void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtractor::Message const& _msg) { // exception header line - errorColored() << _msg.category; + errorColored() << _msg.severity; if (m_withErrorIds && _msg.errorId.has_value()) errorColored() << " (" << _msg.errorId.value().error << ")"; messageColored() << ": " << _msg.primary.message << '\n'; @@ -181,9 +181,9 @@ void SourceReferenceFormatter::printExceptionInformation(SourceReferenceExtracto m_stream << '\n'; } -void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category) +void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _severity) { - printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _exception, _category)); + printExceptionInformation(SourceReferenceExtractor::extract(m_charStreamProvider, _exception, _severity)); } void SourceReferenceFormatter::printErrorInformation(ErrorList const& _errors) diff --git a/liblangutil/SourceReferenceFormatter.h b/liblangutil/SourceReferenceFormatter.h index fc1f418a2..5bac03a9a 100644 --- a/liblangutil/SourceReferenceFormatter.h +++ b/liblangutil/SourceReferenceFormatter.h @@ -52,7 +52,7 @@ public: /// Prints source location if it is given. void printSourceLocation(SourceReference const& _ref); void printExceptionInformation(SourceReferenceExtractor::Message const& _msg); - void printExceptionInformation(util::Exception const& _exception, std::string const& _category); + void printExceptionInformation(util::Exception const& _exception, std::string const& _severity); void printErrorInformation(langutil::ErrorList const& _errors); void printErrorInformation(Error const& _error); @@ -77,7 +77,7 @@ public: { return formatExceptionInformation( _error, - (_error.type() == Error::Type::Warning) ? "Warning" : "Error", + Error::formatErrorSeverity(Error::errorSeverity(_error.type())), _charStreamProvider ); } diff --git a/liblangutil/UniqueErrorReporter.h b/liblangutil/UniqueErrorReporter.h index d0f6dc4e2..886720568 100644 --- a/liblangutil/UniqueErrorReporter.h +++ b/liblangutil/UniqueErrorReporter.h @@ -58,11 +58,7 @@ public: void warning(ErrorId _error, std::string const& _description) { - if (!seen(_error, {}, _description)) - { - m_errorReporter.warning(_error, _description); - markAsSeen(_error, {}, _description); - } + m_errorReporter.warning(_error, _description); } bool seen(ErrorId _error, SourceLocation const& _location, std::string const& _description) const @@ -77,8 +73,8 @@ public: void markAsSeen(ErrorId _error, SourceLocation const& _location, std::string const& _description) { - solAssert(!seen(_error, _location, _description), ""); - m_seenErrors[{_error, _location}] = _description; + if (_location != SourceLocation{}) + m_seenErrors[{_error, _location}] = _description; } ErrorList const& errors() const { return m_errorReporter.errors(); } diff --git a/libsmtutil/CHCSmtLib2Interface.cpp b/libsmtutil/CHCSmtLib2Interface.cpp index 028c5efc2..48fdbd328 100644 --- a/libsmtutil/CHCSmtLib2Interface.cpp +++ b/libsmtutil/CHCSmtLib2Interface.cpp @@ -60,8 +60,8 @@ void CHCSmtLib2Interface::reset() void CHCSmtLib2Interface::registerRelation(Expression const& _expr) { - smtAssert(_expr.sort, ""); - smtAssert(_expr.sort->kind == Kind::Function, ""); + smtAssert(_expr.sort); + smtAssert(_expr.sort->kind == Kind::Function); if (!m_variables.count(_expr.name)) { auto fSort = dynamic_pointer_cast(_expr.sort); @@ -124,7 +124,7 @@ pair CHCSmtLib2Interface::query(Expre void CHCSmtLib2Interface::declareVariable(string const& _name, SortPointer const& _sort) { - smtAssert(_sort, ""); + smtAssert(_sort); if (_sort->kind == Kind::Function) declareFunction(_name, _sort); else if (!m_variables.count(_name)) @@ -172,13 +172,13 @@ string CHCSmtLib2Interface::forall() void CHCSmtLib2Interface::declareFunction(string const& _name, SortPointer const& _sort) { - smtAssert(_sort, ""); - smtAssert(_sort->kind == Kind::Function, ""); + smtAssert(_sort); + smtAssert(_sort->kind == Kind::Function); // TODO Use domain and codomain as key as well if (!m_variables.count(_name)) { auto fSort = dynamic_pointer_cast(_sort); - smtAssert(fSort->codomain, ""); + smtAssert(fSort->codomain); string domain = toSmtLibSort(fSort->domain); string codomain = toSmtLibSort(*fSort->codomain); m_variables.insert(_name); diff --git a/libsmtutil/Exceptions.h b/libsmtutil/Exceptions.h index 29011dab9..fd144ca72 100644 --- a/libsmtutil/Exceptions.h +++ b/libsmtutil/Exceptions.h @@ -21,12 +21,31 @@ #include #include +#include +#include +#include + namespace solidity::smtutil { struct SMTLogicError: virtual util::Exception {}; -#define smtAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, SMTLogicError, DESCRIPTION) +/// Assertion that throws an SMTLogicError containing the given description if it is not met. +#if !BOOST_PP_VARIADICS_MSVC +#define smtAssert(...) BOOST_PP_OVERLOAD(smtAssert_,__VA_ARGS__)(__VA_ARGS__) +#else +#define smtAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(smtAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) +#endif + +#define smtAssert_1(CONDITION) \ + smtAssert_2(CONDITION, "") + +#define smtAssert_2(CONDITION, DESCRIPTION) \ + assertThrowWithDefaultDescription( \ + CONDITION, \ + ::solidity::smtutil::SMTLogicError, \ + DESCRIPTION, \ + "SMT assertion failed" \ + ) } diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index 21648c245..87ee78013 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -22,6 +22,7 @@ #include #include +#include #include @@ -42,11 +43,11 @@ struct SMTSolverChoice bool smtlib2 = false; bool z3 = false; - static constexpr SMTSolverChoice All() { return {true, true, true}; } - static constexpr SMTSolverChoice CVC4() { return {true, false, false}; } - static constexpr SMTSolverChoice SMTLIB2() { return {false, true, false}; } - static constexpr SMTSolverChoice Z3() { return {false, false, true}; } - static constexpr SMTSolverChoice None() { return {false, false, false}; } + static constexpr SMTSolverChoice All() noexcept { return {true, true, true}; } + static constexpr SMTSolverChoice CVC4() noexcept { return {true, false, false}; } + static constexpr SMTSolverChoice SMTLIB2() noexcept { return {false, true, false}; } + static constexpr SMTSolverChoice Z3() noexcept { return {false, false, true}; } + static constexpr SMTSolverChoice None() noexcept { return {false, false, false}; } static std::optional fromString(std::string const& _solvers) { @@ -65,7 +66,7 @@ struct SMTSolverChoice return solvers; } - SMTSolverChoice& operator&(SMTSolverChoice const& _other) + SMTSolverChoice& operator&=(SMTSolverChoice const& _other) { cvc4 &= _other.cvc4; smtlib2 &= _other.smtlib2; @@ -73,9 +74,10 @@ struct SMTSolverChoice return *this; } - SMTSolverChoice& operator&=(SMTSolverChoice const& _other) + SMTSolverChoice operator&(SMTSolverChoice _other) const noexcept { - return *this & _other; + _other &= *this; + return _other; } bool operator!=(SMTSolverChoice const& _other) const noexcept { return !(*this == _other); } @@ -101,9 +103,9 @@ struct SMTSolverChoice return true; } - bool none() { return !some(); } - bool some() { return cvc4 || smtlib2 || z3; } - bool all() { return cvc4 && smtlib2 && z3; } + bool none() const noexcept { return !some(); } + bool some() const noexcept { return cvc4 || smtlib2 || z3; } + bool all() const noexcept { return cvc4 && smtlib2 && z3; } }; enum class CheckResult diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 965fba5ed..0fc6761f7 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -27,6 +27,8 @@ #include #include +#include + using namespace std; using namespace solidity; using namespace solidity::frontend; diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp index 631545569..c159b8357 100644 --- a/libsolidity/analysis/ContractLevelChecker.cpp +++ b/libsolidity/analysis/ContractLevelChecker.cpp @@ -71,7 +71,7 @@ bool ContractLevelChecker::check(SourceUnit const& _sourceUnit) findDuplicateDefinitions( filterDeclarations(*_sourceUnit.annotation().exportedSymbols) ); - if (!Error::containsOnlyWarnings(m_errorReporter.errors())) + if (Error::containsErrors(m_errorReporter.errors())) noErrors = false; for (ASTPointer const& node: _sourceUnit.nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) @@ -97,7 +97,7 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract) checkPayableFallbackWithoutReceive(_contract); checkStorageSize(_contract); - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _contract) diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 181943032..0c64ac3b5 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -36,7 +36,7 @@ bool ControlFlowAnalyzer::run() for (auto& [pair, flow]: m_cfg.allFunctionFlows()) analyze(*pair.function, pair.contract, *flow); - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } void ControlFlowAnalyzer::analyze(FunctionDefinition const& _function, ContractDefinition const* _contract, FunctionFlow const& _flow) diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index 98dcbf069..2d03aaf9c 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -409,7 +409,8 @@ bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly) void ControlFlowBuilder::visit(yul::Statement const& _statement) { solAssert(m_currentNode && m_inlineAssembly, ""); - m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, locationOf(_statement)); + solAssert(nativeLocationOf(_statement) == originLocationOf(_statement), ""); + m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, nativeLocationOf(_statement)); ASTWalker::visit(_statement); } @@ -496,14 +497,15 @@ void ControlFlowBuilder::operator()(yul::Identifier const& _identifier) solAssert(m_currentNode && m_inlineAssembly, ""); auto const& externalReferences = m_inlineAssembly->annotation().externalReferences; if (externalReferences.count(&_identifier)) - { if (auto const* declaration = dynamic_cast(externalReferences.at(&_identifier).declaration)) + { + solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), ""); m_currentNode->variableOccurrences.emplace_back( *declaration, VariableOccurrence::Kind::Access, - _identifier.debugData->location + nativeLocationOf(_identifier) ); - } + } } void ControlFlowBuilder::operator()(yul::Assignment const& _assignment) @@ -514,11 +516,14 @@ void ControlFlowBuilder::operator()(yul::Assignment const& _assignment) for (auto const& variable: _assignment.variableNames) if (externalReferences.count(&variable)) if (auto const* declaration = dynamic_cast(externalReferences.at(&variable).declaration)) + { + solAssert(nativeLocationOf(variable) == originLocationOf(variable), ""); m_currentNode->variableOccurrences.emplace_back( *declaration, VariableOccurrence::Kind::Assignment, - variable.debugData->location + nativeLocationOf(variable) ); + } } void ControlFlowBuilder::operator()(yul::FunctionCall const& _functionCall) @@ -548,7 +553,7 @@ void ControlFlowBuilder::operator()(yul::FunctionDefinition const&) void ControlFlowBuilder::operator()(yul::Leave const&) { // This has to be implemented, if we ever decide to visit functions. - solUnimplementedAssert(false, ""); + solUnimplemented(""); } bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) diff --git a/libsolidity/analysis/ControlFlowGraph.cpp b/libsolidity/analysis/ControlFlowGraph.cpp index 66ec552ca..f4afe0149 100644 --- a/libsolidity/analysis/ControlFlowGraph.cpp +++ b/libsolidity/analysis/ControlFlowGraph.cpp @@ -27,7 +27,7 @@ using namespace solidity::frontend; bool CFG::constructFlow(ASTNode const& _astRoot) { _astRoot.accept(*this); - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index c184e11df..b015939e2 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -140,6 +140,30 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct) return false; } +void DeclarationTypeChecker::endVisit(UserDefinedValueTypeDefinition const& _userDefined) +{ + TypeName const* typeName = _userDefined.underlyingType(); + solAssert(typeName, ""); + if (!dynamic_cast(typeName)) + m_errorReporter.fatalTypeError( + 8657_error, + typeName->location(), + "The underlying type for a user defined value type has to be an elementary value type." + ); + + Type const* type = typeName->annotation().type; + solAssert(type, ""); + solAssert(!dynamic_cast(type), ""); + if (!type->isValueType()) + m_errorReporter.typeError( + 8129_error, + _userDefined.location(), + "The underlying type of the user defined value type \"" + + _userDefined.name() + + "\" is not a value type." + ); +} + void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName) { if (_typeName.annotation().type) @@ -158,6 +182,8 @@ void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName) _typeName.annotation().type = TypeProvider::enumType(*enumDef); else if (ContractDefinition const* contract = dynamic_cast(declaration)) _typeName.annotation().type = TypeProvider::contract(*contract); + else if (auto userDefinedValueType = dynamic_cast(declaration)) + _typeName.annotation().type = TypeProvider::userDefinedValueType(*userDefinedValueType); else { _typeName.annotation().type = TypeProvider::emptyTuple(); @@ -227,12 +253,13 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping) { case Type::Category::Enum: case Type::Category::Contract: + case Type::Category::UserDefinedValueType: break; default: m_errorReporter.fatalTypeError( 7804_error, typeName->location(), - "Only elementary types, contract types or enums are allowed as mapping keys." + "Only elementary types, user defined value types, contract types or enums are allowed as mapping keys." ); break; } diff --git a/libsolidity/analysis/DeclarationTypeChecker.h b/libsolidity/analysis/DeclarationTypeChecker.h index d327d4296..cb887c845 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.h +++ b/libsolidity/analysis/DeclarationTypeChecker.h @@ -60,6 +60,7 @@ private: void endVisit(VariableDeclaration const& _variable) override; bool visit(EnumDefinition const& _enum) override; bool visit(StructDefinition const& _struct) override; + void endVisit(UserDefinedValueTypeDefinition const& _userDefined) override; bool visit(UsingForDirective const& _usingForDirective) override; bool visit(InheritanceSpecifier const& _inheritanceSpecifier) override; diff --git a/libsolidity/analysis/DocStringTagParser.cpp b/libsolidity/analysis/DocStringTagParser.cpp index 52fb4aebe..137cdb409 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -34,6 +34,7 @@ #include #include +#include using namespace std; using namespace solidity; @@ -233,7 +234,7 @@ void DocStringTagParser::parseDocStrings( for (auto const& [tagName, tagValue]: _annotation.docTags) { - string static const customPrefix("custom:"); + string_view static constexpr customPrefix("custom:"); if (tagName == "custom" || tagName == "custom:") m_errorReporter.docstringParsingError( 6564_error, diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 20c4f2c46..74834a0cb 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -102,7 +102,12 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, mapname(), + &alias.location, + false, + m_errorReporter )) error = true; } @@ -607,13 +612,31 @@ bool DeclarationRegistrationHelper::visitNode(ASTNode& _node) if (auto* declaration = dynamic_cast(&_node)) registerDeclaration(*declaration); + + if (auto* annotation = dynamic_cast(&_node.annotation())) + { + string canonicalName = dynamic_cast(_node).name(); + solAssert(!canonicalName.empty(), ""); + + for ( + ASTNode const* scope = m_currentScope; + scope != nullptr; + scope = m_scopes[scope]->enclosingNode() + ) + if (auto decl = dynamic_cast(scope)) + { + solAssert(!decl->name().empty(), ""); + canonicalName = decl->name() + "." + canonicalName; + } + + annotation->canonicalName = canonicalName; + } + 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(); return true; } @@ -663,23 +686,4 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio solAssert(_declaration.annotation().contract == m_currentContract, ""); } -string DeclarationRegistrationHelper::currentCanonicalName() const -{ - string ret; - for ( - ASTNode const* scope = m_currentScope; - scope != nullptr; - scope = m_scopes[scope]->enclosingNode() - ) - { - if (auto decl = dynamic_cast(scope)) - { - if (!ret.empty()) - ret = "." + ret; - ret = decl->name() + ret; - } - } - return ret; -} - } diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 919e84a66..36a22ed2e 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -176,9 +176,6 @@ private: static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2); - /// @returns the canonical name of the current scope. - std::string currentCanonicalName() const; - std::map>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; diff --git a/libsolidity/analysis/OverrideChecker.cpp b/libsolidity/analysis/OverrideChecker.cpp index 18a40be40..6f44f92ac 100644 --- a/libsolidity/analysis/OverrideChecker.cpp +++ b/libsolidity/analysis/OverrideChecker.cpp @@ -86,7 +86,8 @@ private: int currentNode = static_cast(numNodes++); nodes[_function] = currentNode; nodeInv[currentNode] = _function; - if (_function.overrides()) + + if (!_function.baseFunctions().empty()) for (auto const& baseFunction: _function.baseFunctions()) addEdge(currentNode, visit(baseFunction)); else @@ -518,7 +519,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr "Override changes modifier signature." ); - if (!_overriding.overrides()) + if (!_overriding.overrides() && !(_super.isFunction() && _super.contract().isInterface())) overrideError( _overriding, _super, diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index e5c3ec2f8..cdc8c8b7e 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -36,14 +36,14 @@ using namespace solidity::frontend; bool PostTypeChecker::check(ASTNode const& _astRoot) { _astRoot.accept(*this); - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } bool PostTypeChecker::finalize() { for (auto& checker: m_checkers) checker->finalize(); - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } bool PostTypeChecker::visit(ContractDefinition const& _contractDefinition) diff --git a/libsolidity/analysis/PostTypeContractLevelChecker.cpp b/libsolidity/analysis/PostTypeContractLevelChecker.cpp index cfc6f4f29..32927188f 100644 --- a/libsolidity/analysis/PostTypeContractLevelChecker.cpp +++ b/libsolidity/analysis/PostTypeContractLevelChecker.cpp @@ -68,5 +68,5 @@ bool PostTypeContractLevelChecker::check(ContractDefinition const& _contract) errorHashes[hash][signature] = error->location(); } - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 625336742..50c9cbd4f 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -201,9 +201,13 @@ bool ReferencesResolver::visit(Return const& _return) void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) { - validateYulIdentifierName(_function.name, _function.debugData->location); + solAssert(nativeLocationOf(_function) == originLocationOf(_function), ""); + validateYulIdentifierName(_function.name, nativeLocationOf(_function)); for (yul::TypedName const& varName: _function.parameters + _function.returnVariables) - validateYulIdentifierName(varName.name, varName.debugData->location); + { + solAssert(nativeLocationOf(varName) == originLocationOf(varName), ""); + validateYulIdentifierName(varName.name, nativeLocationOf(varName)); + } bool wasInsideFunction = m_yulInsideFunction; m_yulInsideFunction = true; @@ -213,13 +217,17 @@ void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) void ReferencesResolver::operator()(yul::Identifier const& _identifier) { - static set suffixes{"slot", "offset", "length"}; + solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), ""); + + static set suffixes{"slot", "offset", "length", "address", "selector"}; string suffix; for (string const& s: suffixes) if (boost::algorithm::ends_with(_identifier.name.str(), "." + s)) suffix = s; - // Could also use `pathFromCurrentScope`, split by '.' + // Could also use `pathFromCurrentScope`, split by '.'. + // If we do that, suffix should only be set for when it has a special + // meaning, not for normal identifierPaths. auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); if (!suffix.empty()) { @@ -238,7 +246,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) { m_errorReporter.declarationError( 4718_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Multiple matching identifiers. Resolving overloaded identifiers is not supported." ); return; @@ -251,7 +259,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) ) m_errorReporter.declarationError( 9467_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Identifier not found. Use \".slot\" and \".offset\" to access storage variables." ); return; @@ -261,7 +269,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) { m_errorReporter.declarationError( 6578_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Cannot access local Solidity variables from inside an inline assembly function." ); return; @@ -274,7 +282,10 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) { for (auto const& identifier: _varDecl.variables) - validateYulIdentifierName(identifier.name, identifier.debugData->location); + { + solAssert(nativeLocationOf(identifier) == originLocationOf(identifier), ""); + validateYulIdentifierName(identifier.name, nativeLocationOf(identifier)); + } if (_varDecl.value) visit(*_varDecl.value); diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 4931401cb..01ca601f1 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -86,7 +86,7 @@ StaticAnalyzer::~StaticAnalyzer() bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } bool StaticAnalyzer::visit(ContractDefinition const& _contract) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 412ad1aa9..e47080cb4 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -41,7 +41,7 @@ using namespace solidity::util; bool SyntaxChecker::checkSyntax(ASTNode const& _astRoot) { _astRoot.accept(*this); - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } bool SyntaxChecker::visit(SourceUnit const& _sourceUnit) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f0ef5aae8..72cb8feb0 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -73,7 +73,7 @@ bool TypeChecker::checkTypeRequirements(SourceUnit const& _source) m_currentSourceUnit = &_source; _source.accept(*this); m_currentSourceUnit = nullptr; - return Error::containsOnlyWarnings(m_errorReporter.errors()); + return !Error::containsErrors(m_errorReporter.errors()); } Type const* TypeChecker::type(Expression const& _expression) const @@ -246,7 +246,10 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio Type::Category typeCategory = typeTypePtr->actualType()->category(); if (auto const* contractType = dynamic_cast(typeTypePtr->actualType())) wrongType = contractType->isSuper(); - else if (typeCategory != Type::Category::Integer) + else if ( + typeCategory != Type::Category::Integer && + typeCategory != Type::Category::Enum + ) wrongType = true; } else @@ -257,7 +260,7 @@ TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(Functio 4259_error, arguments.front()->location(), "Invalid type for argument in the function call. " - "A contract type or an integer type is required, but " + + "An enum type, contract type or an integer type is required, but " + type(*arguments.front())->toString(true) + " provided." ); @@ -314,14 +317,22 @@ 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 (auto const* contractDef = dynamic_cast(_modifier.scope())) + { + if (_modifier.virtualSemantics() && contractDef->isLibrary()) + m_errorReporter.typeError( + 3275_error, + _modifier.location(), + "Modifiers in a library cannot be virtual." + ); + + if (contractDef->isInterface()) + m_errorReporter.typeError( + 6408_error, + _modifier.location(), + "Modifiers cannot be defined or declared in interfaces." + ); + } if (!_modifier.isImplemented() && !_modifier.virtualSemantics()) m_errorReporter.typeError(8063_error, _modifier.location(), "Modifiers without implementation must be marked virtual."); @@ -754,7 +765,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(var->type(), "Expected variable type!"); if (var->immutable()) { - m_errorReporter.typeError(3773_error, _identifier.debugData->location, "Assembly access to immutable variables is not supported."); + m_errorReporter.typeError(3773_error, nativeLocationOf(_identifier), "Assembly access to immutable variables is not supported."); return false; } if (var->isConstant()) @@ -763,7 +774,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { m_errorReporter.typeError( 3558_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Constant variable is circular." ); return false; @@ -773,24 +784,24 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (var && !var->value()) { - m_errorReporter.typeError(3224_error, _identifier.debugData->location, "Constant has no value."); + m_errorReporter.typeError(3224_error, nativeLocationOf(_identifier), "Constant has no value."); return false; } else if (_context == yul::IdentifierContext::LValue) { - m_errorReporter.typeError(6252_error, _identifier.debugData->location, "Constant variables cannot be assigned to."); + m_errorReporter.typeError(6252_error, nativeLocationOf(_identifier), "Constant variables cannot be assigned to."); return false; } - else if (!identifierInfo.suffix.empty()) + else if (identifierInfo.suffix == "slot" || identifierInfo.suffix == "offset") { - m_errorReporter.typeError(6617_error, _identifier.debugData->location, "The suffixes .offset and .slot can only be used on non-constant storage variables."); + m_errorReporter.typeError(6617_error, nativeLocationOf(_identifier), "The suffixes .offset and .slot can only be used on non-constant storage variables."); return false; } else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast(var->value().get())) { m_errorReporter.typeError( 2249_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Constant variables with non-literal values cannot be forward referenced from inline assembly." ); return false; @@ -800,7 +811,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) type(*var->value())->category() != Type::Category::RationalNumber )) { - m_errorReporter.typeError(7615_error, _identifier.debugData->location, "Only direct number constants and references to such constants are supported by inline assembly."); + m_errorReporter.typeError(7615_error, nativeLocationOf(_identifier), "Only direct number constants and references to such constants are supported by inline assembly."); return false; } } @@ -810,24 +821,24 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (!identifierInfo.suffix.empty()) { string const& suffix = identifierInfo.suffix; - solAssert((set{"offset", "slot", "length"}).count(suffix), ""); - if (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage)) + solAssert((set{"offset", "slot", "length", "selector", "address"}).count(suffix), ""); + if (!var->isConstant() && (var->isStateVariable() || var->type()->dataStoredIn(DataLocation::Storage))) { if (suffix != "slot" && suffix != "offset") { - m_errorReporter.typeError(4656_error, _identifier.debugData->location, "State variables only support \".slot\" and \".offset\"."); + m_errorReporter.typeError(4656_error, nativeLocationOf(_identifier), "State variables only support \".slot\" and \".offset\"."); return false; } else if (_context == yul::IdentifierContext::LValue) { if (var->isStateVariable()) { - m_errorReporter.typeError(4713_error, _identifier.debugData->location, "State variables cannot be assigned to - you have to use \"sstore()\"."); + m_errorReporter.typeError(4713_error, nativeLocationOf(_identifier), "State variables cannot be assigned to - you have to use \"sstore()\"."); return false; } else if (suffix != "slot") { - m_errorReporter.typeError(9739_error, _identifier.debugData->location, "Only .slot can be assigned to."); + m_errorReporter.typeError(9739_error, nativeLocationOf(_identifier), "Only .slot can be assigned to."); return false; } } @@ -839,13 +850,26 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (suffix != "offset" && suffix != "length") { - m_errorReporter.typeError(1536_error, _identifier.debugData->location, "Calldata variables only support \".offset\" and \".length\"."); + m_errorReporter.typeError(1536_error, nativeLocationOf(_identifier), "Calldata variables only support \".offset\" and \".length\"."); + return false; + } + } + else if (auto const* fpType = dynamic_cast(var->type())) + { + if (suffix != "selector" && suffix != "address") + { + m_errorReporter.typeError(9272_error, nativeLocationOf(_identifier), "Variables of type function pointer only support \".selector\" and \".address\"."); + return false; + } + if (fpType->kind() != FunctionType::Kind::External) + { + m_errorReporter.typeError(8533_error, nativeLocationOf(_identifier), "Only Variables of type external function pointer support \".selector\" and \".address\"."); return false; } } else { - m_errorReporter.typeError(3622_error, _identifier.debugData->location, "The suffix \"." + suffix + "\" is not supported by this variable or type."); + m_errorReporter.typeError(3622_error, nativeLocationOf(_identifier), "The suffix \"." + suffix + "\" is not supported by this variable or type."); return false; } } @@ -853,14 +877,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { m_errorReporter.typeError( 1408_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "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.debugData->location, "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables."); + m_errorReporter.typeError(9068_error, nativeLocationOf(_identifier), "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables."); return false; } else if (var->type()->sizeOnStack() != 1) @@ -869,18 +893,18 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) auto const* arrayType = dynamic_cast(var->type()); arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) ) - m_errorReporter.typeError(1397_error, _identifier.debugData->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\"."); + m_errorReporter.typeError(1397_error, nativeLocationOf(_identifier), "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.debugData->location, "Only types that use one stack slot are supported."); + m_errorReporter.typeError(9857_error, nativeLocationOf(_identifier), "Only types that use one stack slot are supported."); } return false; } } else if (!identifierInfo.suffix.empty()) { - m_errorReporter.typeError(7944_error, _identifier.debugData->location, "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."); + m_errorReporter.typeError(7944_error, nativeLocationOf(_identifier), "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."); return false; } else if (_context == yul::IdentifierContext::LValue) @@ -888,7 +912,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (dynamic_cast(declaration)) return false; - m_errorReporter.typeError(1990_error, _identifier.debugData->location, "Only local variables can be assigned to in inline assembly."); + m_errorReporter.typeError(1990_error, nativeLocationOf(_identifier), "Only local variables can be assigned to in inline assembly."); return false; } @@ -897,7 +921,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast(declaration)) { - m_errorReporter.declarationError(2025_error, _identifier.debugData->location, "Access to functions is not allowed in inline assembly."); + m_errorReporter.declarationError(2025_error, nativeLocationOf(_identifier), "Access to functions is not allowed in inline assembly."); return false; } else if (dynamic_cast(declaration)) @@ -907,7 +931,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (!contract->isLibrary()) { - m_errorReporter.typeError(4977_error, _identifier.debugData->location, "Expected a library."); + m_errorReporter.typeError(4977_error, nativeLocationOf(_identifier), "Expected a library."); return false; } } @@ -2470,6 +2494,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = functionType->returnParameterTypes(); break; } + case FunctionType::Kind::Wrap: + case FunctionType::Kind::Unwrap: + { + typeCheckFunctionGeneralChecks(_functionCall, functionType); + returnTypes = functionType->returnParameterTypes(); + break; + } default: { typeCheckFunctionCall(_functionCall, functionType); @@ -2907,7 +2938,10 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) // 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. if (auto tt = dynamic_cast(exprType)) - if (tt->actualType()->category() == Type::Category::Enum) + if ( + tt->actualType()->category() == Type::Category::Enum || + tt->actualType()->category() == Type::Category::UserDefinedValueType + ) annotation.isPure = true; if ( auto const* functionType = dynamic_cast(exprType); diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 87831972d..81f755cec 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -69,7 +69,7 @@ public: if (yul::EVMDialect const* dialect = dynamic_cast(&m_dialect)) if (yul::BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) if (fun->instruction) - checkInstruction(_funCall.debugData->location, *fun->instruction); + checkInstruction(nativeLocationOf(_funCall), *fun->instruction); for (auto const& arg: _funCall.arguments) std::visit(*this, arg); diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 6d5e89ce2..c36aee5b3 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -274,7 +274,7 @@ uint32_t ContractDefinition::interfaceId() const { uint32_t result{0}; for (auto const& function: interfaceFunctionList(false)) - result ^= util::fromBigEndian(function.first.ref()); + result ^= fromBigEndian(function.first.ref()); return result; } @@ -335,6 +335,17 @@ TypeNameAnnotation& TypeName::annotation() const return initAnnotation(); } +Type const* UserDefinedValueTypeDefinition::type() const +{ + solAssert(m_underlyingType->annotation().type, ""); + return TypeProvider::typeType(TypeProvider::userDefinedValueType(*this)); +} + +TypeDeclarationAnnotation& UserDefinedValueTypeDefinition::annotation() const +{ + return initAnnotation(); +} + Type const* StructDefinition::type() const { solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker."); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index f02e93599..c229252a8 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -726,6 +726,40 @@ public: Type const* type() const override; }; +/** + * User defined value types, i.e., custom types, for example, `type MyInt is int`. Allows creating a + * zero cost abstraction over value type with stricter type requirements. + */ +class UserDefinedValueTypeDefinition: public Declaration +{ +public: + UserDefinedValueTypeDefinition( + int64_t _id, + SourceLocation const& _location, + ASTPointer _name, + SourceLocation _nameLocation, + ASTPointer _underlyingType + ): + Declaration(_id, _location, _name, std::move(_nameLocation), Visibility::Default), + m_underlyingType(std::move(_underlyingType)) + { + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + Type const* type() const override; + + TypeDeclarationAnnotation& annotation() const override; + + TypeName const* underlyingType() const { return m_underlyingType.get(); } + bool isVisibleViaContractTypeAccess() const override { return true; } + +private: + /// The name of the underlying type + ASTPointer m_underlyingType; +}; + /** * Parameter list, used as function parameter list, return list and for try and catch. * None of the parameters is allowed to contain mappings (not even recursively diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 9c08a374f..53931e426 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -211,7 +211,7 @@ struct InlineAssemblyAnnotation: StatementAnnotation struct ExternalIdentifierInfo { Declaration const* declaration = nullptr; - /// Suffix used, one of "slot", "offset", "length" or empty. + /// Suffix used, one of "slot", "offset", "length", "address", "selector" or empty. std::string suffix; size_t valueSize = size_t(-1); }; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 5b73eb3a3..79e6a4ecf 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -51,6 +51,7 @@ class UsingForDirective; class StructDefinition; class EnumDefinition; class EnumValue; +class UserDefinedValueTypeDefinition; class ParameterList; class FunctionDefinition; class VariableDeclaration; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index e009bc8cb..326cca43e 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -26,12 +26,12 @@ #include #include -#include #include #include #include #include +#include #include @@ -172,16 +172,19 @@ void ASTJsonConverter::appendExpressionAttributes( _attributes += exprAttributes; } -Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair _info) const +Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair _info) const { Json::Value tuple(Json::objectValue); - tuple["src"] = sourceLocationToString(_info.first->debugData->location); + tuple["src"] = sourceLocationToString(nativeLocationOf(*_info.first)); tuple["declaration"] = idOrNull(_info.second.declaration); 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; } @@ -276,6 +279,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) make_pair("nodes", toJson(_node.subNodes())), make_pair("scope", idOrNull(_node.scope())) }; + addIfSet(attributes, "canonicalName", _node.annotation().canonicalName); if (_node.annotation().unimplementedDeclarations.has_value()) attributes.emplace_back("fullyImplemented", _node.annotation().unimplementedDeclarations->empty()); @@ -354,6 +358,21 @@ bool ASTJsonConverter::visit(EnumValue const& _node) return false; } +bool ASTJsonConverter::visit(UserDefinedValueTypeDefinition const& _node) +{ + solAssert(_node.underlyingType(), ""); + std::vector> attributes = { + make_pair("name", _node.name()), + make_pair("nameLocation", sourceLocationToString(_node.nameLocation())), + make_pair("underlyingType", toJson(*_node.underlyingType())) + }; + addIfSet(attributes, "canonicalName", _node.annotation().canonicalName); + + setJsonNode(_node, "UserDefinedValueTypeDefinition", std::move(attributes)); + + return false; +} + bool ASTJsonConverter::visit(ParameterList const& _node) { setJsonNode(_node, "ParameterList", { diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 34903c7a1..56fdb1304 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -81,6 +81,7 @@ public: bool visit(StructDefinition const& _node) override; bool visit(EnumDefinition const& _node) override; bool visit(EnumValue const& _node) override; + bool visit(UserDefinedValueTypeDefinition const& _node) override; bool visit(ParameterList const& _node) override; bool visit(OverrideSpecifier const& _node) override; bool visit(FunctionDefinition const& _node) override; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 5d7ba1211..2e69df93f 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -61,7 +61,7 @@ map> ASTJsonImporter::jsonToSourceUnit(map(src.first)); for (auto const& srcPair: _sourceList) { - astAssert(!srcPair.second.isNull(), ""); + astAssert(!srcPair.second.isNull()); astAssert(member(srcPair.second,"nodeType") == "SourceUnit", "The 'nodeType' of the highest node must be 'SourceUnit'."); m_sourceUnits[srcPair.first] = createSourceUnit(srcPair.second, srcPair.first); } @@ -133,6 +133,8 @@ ASTPointer ASTJsonImporter::convertJsonToASTNode(Json::Value const& _js return createEnumDefinition(_json); if (nodeType == "EnumValue") return createEnumValue(_json); + if (nodeType == "UserDefinedValueTypeDefinition") + return createUserDefinedValueTypeDefinition(_json); if (nodeType == "ParameterList") return createParameterList(_json); if (nodeType == "OverrideSpecifier") @@ -387,6 +389,16 @@ ASTPointer ASTJsonImporter::createEnumValue(Json::Value const& _node) ); } +ASTPointer ASTJsonImporter::createUserDefinedValueTypeDefinition(Json::Value const& _node) +{ + return createASTNode( + _node, + memberAsASTString(_node, "name"), + createNameSourceLocation(_node), + convertJsonToASTNode(member(_node, "underlyingType")) + ); +} + ASTPointer ASTJsonImporter::createParameterList(Json::Value const& _node) { std::vector> parameters; @@ -473,17 +485,17 @@ ASTPointer ASTJsonImporter::createVariableDeclaration(Json: if (mutabilityStr == "constant") { mutability = VariableDeclaration::Mutability::Constant; - astAssert(memberAsBool(_node, "constant"), ""); + astAssert(memberAsBool(_node, "constant")); } else { - astAssert(!memberAsBool(_node, "constant"), ""); + astAssert(!memberAsBool(_node, "constant")); if (mutabilityStr == "mutable") mutability = VariableDeclaration::Mutability::Mutable; else if (mutabilityStr == "immutable") mutability = VariableDeclaration::Mutability::Immutable; else - astAssert(false, ""); + astAssert(false); } return createASTNode( diff --git a/libsolidity/ast/ASTJsonImporter.h b/libsolidity/ast/ASTJsonImporter.h index 3f1f5265c..4566a58a4 100644 --- a/libsolidity/ast/ASTJsonImporter.h +++ b/libsolidity/ast/ASTJsonImporter.h @@ -81,6 +81,7 @@ private: ASTPointer createStructDefinition(Json::Value const& _node); ASTPointer createEnumDefinition(Json::Value const& _node); ASTPointer createEnumValue(Json::Value const& _node); + ASTPointer createUserDefinedValueTypeDefinition(Json::Value const& _node); ASTPointer createParameterList(Json::Value const& _node); ASTPointer createOverrideSpecifier(Json::Value const& _node); ASTPointer createFunctionDefinition(Json::Value const& _node); diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 398387b33..ef03e6339 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -61,6 +61,7 @@ public: 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(UserDefinedValueTypeDefinition& _node) { return visitNode(_node); } virtual bool visit(StructDefinition& _node) { return visitNode(_node); } virtual bool visit(EnumDefinition& _node) { return visitNode(_node); } virtual bool visit(EnumValue& _node) { return visitNode(_node); } @@ -116,6 +117,7 @@ public: virtual void endVisit(IdentifierPath& _node) { endVisitNode(_node); } virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); } virtual void endVisit(UsingForDirective& _node) { endVisitNode(_node); } + virtual void endVisit(UserDefinedValueTypeDefinition& _node) { endVisitNode(_node); } virtual void endVisit(StructDefinition& _node) { endVisitNode(_node); } virtual void endVisit(EnumDefinition& _node) { endVisitNode(_node); } virtual void endVisit(EnumValue& _node) { endVisitNode(_node); } @@ -194,6 +196,7 @@ public: 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); } + virtual bool visit(UserDefinedValueTypeDefinition const& _node) { return visitNode(_node); } virtual bool visit(EnumDefinition const& _node) { return visitNode(_node); } virtual bool visit(EnumValue const& _node) { return visitNode(_node); } virtual bool visit(ParameterList const& _node) { return visitNode(_node); } @@ -248,6 +251,7 @@ public: 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(UserDefinedValueTypeDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(StructDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(EnumDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(EnumValue const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 075e6fe5c..14ebfe621 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -164,6 +164,26 @@ void EnumValue::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void UserDefinedValueTypeDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (m_underlyingType) + m_underlyingType->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void UserDefinedValueTypeDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (m_underlyingType) + m_underlyingType->accept(_visitor); + } + _visitor.endVisit(*this); +} + void UsingForDirective::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/ast/TypeProvider.cpp b/libsolidity/ast/TypeProvider.cpp index cc810708e..6183493b4 100644 --- a/libsolidity/ast/TypeProvider.cpp +++ b/libsolidity/ast/TypeProvider.cpp @@ -566,9 +566,10 @@ MagicType const* TypeProvider::meta(Type const* _type) solAssert( _type && ( _type->category() == Type::Category::Contract || - _type->category() == Type::Category::Integer + _type->category() == Type::Category::Integer || + _type->category() == Type::Category::Enum ), - "Only contracts or integer types supported for now." + "Only enum, contracts or integer types supported for now." ); return createAndGet(_type); } @@ -577,3 +578,8 @@ MappingType const* TypeProvider::mapping(Type const* _keyType, Type const* _valu { return createAndGet(_keyType, _valueType); } + +UserDefinedValueType const* TypeProvider::userDefinedValueType(UserDefinedValueTypeDefinition const& _definition) +{ + return createAndGet(_definition); +} diff --git a/libsolidity/ast/TypeProvider.h b/libsolidity/ast/TypeProvider.h index 6cdfdadc6..78b8378ca 100644 --- a/libsolidity/ast/TypeProvider.h +++ b/libsolidity/ast/TypeProvider.h @@ -201,6 +201,8 @@ public: static MappingType const* mapping(Type const* _keyType, Type const* _valueType); + static UserDefinedValueType const* userDefinedValueType(UserDefinedValueTypeDefinition const& _definition); + private: /// Global TypeProvider instance. static TypeProvider& instance() diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 137afcba3..9d0914682 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1135,7 +1135,7 @@ IntegerType const* RationalNumberType::integerType() const return nullptr; else return TypeProvider::integer( - max(util::bytesRequired(value), 1u) * 8, + max(numberEncodingSize(value), 1u) * 8, negative ? IntegerType::Modifier::Signed : IntegerType::Modifier::Unsigned ); } @@ -1169,7 +1169,7 @@ FixedPointType const* RationalNumberType::fixedPointType() const if (v > u256(-1)) return nullptr; - unsigned totalBits = max(util::bytesRequired(v), 1u) * 8; + unsigned totalBits = max(numberEncodingSize(v), 1u) * 8; solAssert(totalBits <= 256, ""); return TypeProvider::fixedPoint( @@ -2537,6 +2537,37 @@ unsigned EnumType::memberValue(ASTString const& _member) const solAssert(false, "Requested unknown enum value " + _member); } +Type const& UserDefinedValueType::underlyingType() const +{ + Type const* type = m_definition.underlyingType()->annotation().type; + solAssert(type, ""); + solAssert(type->category() != Category::UserDefinedValueType, ""); + return *type; +} + +string UserDefinedValueType::richIdentifier() const +{ + return "t_userDefinedValueType" + parenthesizeIdentifier(m_definition.name()) + to_string(m_definition.id()); +} + +bool UserDefinedValueType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + UserDefinedValueType const& other = dynamic_cast(_other); + return other.definition() == definition(); +} + +string UserDefinedValueType::toString(bool /* _short */) const +{ + return *definition().annotation().canonicalName; +} + +vector> UserDefinedValueType::makeStackItems() const +{ + return underlyingType().stackItems(); +} + BoolResult TupleType::isImplicitlyConvertibleTo(Type const& _other) const { if (auto tupleType = dynamic_cast(&_other)) @@ -2888,6 +2919,8 @@ string FunctionType::richIdentifier() const case Kind::GasLeft: id += "gasleft"; break; case Kind::Event: id += "event"; break; case Kind::Error: id += "error"; break; + case Kind::Wrap: id += "wrap"; break; + case Kind::Unwrap: id += "unwrap"; break; case Kind::SetGas: id += "setgas"; break; case Kind::SetValue: id += "setvalue"; break; case Kind::BlockHash: id += "blockhash"; break; @@ -3043,10 +3076,7 @@ u256 FunctionType::storageSize() const bool FunctionType::leftAligned() const { - if (m_kind == Kind::External) - return true; - else - solAssert(false, "Alignment property of non-exportable function type requested."); + return m_kind == Kind::External; } unsigned FunctionType::storageBytes() const @@ -3471,7 +3501,9 @@ bool FunctionType::isPure() const m_kind == Kind::ABIEncodeWithSelector || m_kind == Kind::ABIEncodeWithSignature || m_kind == Kind::ABIDecode || - m_kind == Kind::MetaType; + m_kind == Kind::MetaType || + m_kind == Kind::Wrap || + m_kind == Kind::Unwrap; } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) @@ -3758,6 +3790,34 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons for (ASTPointer const& enumValue: enumDef.members()) members.emplace_back(enumValue.get(), enumType); } + else if (m_actualType->category() == Category::UserDefinedValueType) + { + auto& userDefined = dynamic_cast(*m_actualType); + members.emplace_back( + "wrap", + TypeProvider::function( + TypePointers{&userDefined.underlyingType()}, + TypePointers{&userDefined}, + strings{string{}}, + strings{string{}}, + FunctionType::Kind::Wrap, + false, /*_arbitraryParameters */ + StateMutability::Pure + ) + ); + members.emplace_back( + "unwrap", + TypeProvider::function( + TypePointers{&userDefined}, + TypePointers{&userDefined.underlyingType()}, + strings{string{}}, + strings{string{}}, + FunctionType::Kind::Unwrap, + false, /* _arbitraryParameters */ + StateMutability::Pure + ) + ); + } else if ( auto const* arrayType = dynamic_cast(m_actualType); arrayType && arrayType->isByteArray() @@ -3965,9 +4025,10 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const solAssert( m_typeArgument && ( m_typeArgument->category() == Type::Category::Contract || - m_typeArgument->category() == Type::Category::Integer + m_typeArgument->category() == Type::Category::Integer || + m_typeArgument->category() == Type::Category::Enum ), - "Only contracts or integer types supported for now" + "Only enums, contracts or integer types supported for now" ); if (m_typeArgument->category() == Type::Category::Contract) @@ -3993,6 +4054,14 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const {"max", integerTypePointer}, }); } + else if (m_typeArgument->category() == Type::Category::Enum) + { + EnumType const* enumTypePointer = dynamic_cast(m_typeArgument); + return MemberList::MemberMap({ + {"min", enumTypePointer}, + {"max", enumTypePointer}, + }); + } } } solAssert(false, "Unknown kind of magic."); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 50a8fb6ca..2220875b1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -174,7 +175,7 @@ public: enum class Category { Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, ArraySlice, - FixedBytes, Contract, Struct, Function, Enum, Tuple, + FixedBytes, Contract, Struct, Function, Enum, UserDefinedValueType, Tuple, Mapping, TypeType, Modifier, Magic, Module, InaccessibleDynamic }; @@ -1071,11 +1072,85 @@ public: /// @returns the value that the string has in the Enum unsigned int memberValue(ASTString const& _member) const; size_t numberOfMembers() const; + unsigned int minValue() const { return 0; } + unsigned int maxValue() const + { + solAssert(numberOfMembers() <= 256, ""); + return static_cast(numberOfMembers()) - 1; + } private: EnumDefinition const& m_enum; }; +/** + * The type of a UserDefinedValueType. + */ +class UserDefinedValueType: public Type +{ +public: + explicit UserDefinedValueType(UserDefinedValueTypeDefinition const& _definition): + m_definition(_definition) + {} + + Category category() const override { return Category::UserDefinedValueType; } + Type const& underlyingType() const; + UserDefinedValueTypeDefinition const& definition() const { return m_definition; } + + TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; } + Type const* encodingType() const override { return &underlyingType(); } + TypeResult interfaceType(bool /* _inLibrary */) const override {return &underlyingType(); } + std::string richIdentifier() const override; + bool operator==(Type const& _other) const override; + + unsigned calldataEncodedSize(bool _padded) const override { return underlyingType().calldataEncodedSize(_padded); } + + bool leftAligned() const override { return underlyingType().leftAligned(); } + bool canBeStored() const override { return underlyingType().canBeStored(); } + u256 storageSize() const override { return underlyingType().storageSize(); } + unsigned storageBytes() const override { return underlyingType().storageBytes(); } + + bool isValueType() const override + { + solAssert(underlyingType().isValueType(), ""); + return true; + } + bool nameable() const override + { + solAssert(underlyingType().nameable(), ""); + return true; + } + + bool containsNestedMapping() const override + { + solAssert(nameable(), "Called for a non nameable type."); + solAssert(!underlyingType().containsNestedMapping(), ""); + return false; + } + + bool hasSimpleZeroValueInMemory() const override + { + solAssert(underlyingType().hasSimpleZeroValueInMemory(), ""); + return true; + } + + bool dataStoredIn(DataLocation _loc) const override + { + solAssert(!underlyingType().dataStoredIn(_loc), ""); + return false; + } + + std::string toString(bool _short) const override; + std::string canonicalName() const override { solAssert(false, ""); } + std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); } + +protected: + std::vector> makeStackItems() const override; + +private: + UserDefinedValueTypeDefinition const& m_definition; +}; + /** * Type that can hold a finite sequence of values of different types. * In some cases, the components are empty pointers (when used as placeholders). @@ -1144,6 +1219,8 @@ public: RIPEMD160, ///< CALL to special contract for ripemd160 Event, ///< syntactic sugar for LOG* Error, ///< creating an error instance in revert or require + Wrap, ///< customType.wrap(...) for user defined value types + Unwrap, ///< customType.unwrap(...) for user defined value types SetGas, ///< modify the default gas value for the function call SetValue, ///< modify the default value transfer for the function call BlockHash, ///< BLOCKHASH diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index d964c1f14..97cf3425d 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -1170,6 +1170,7 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t solAssert(_type.dataStoredIn(DataLocation::Memory), ""); if (_type.isByteArray()) return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory); + solAssert(_type.calldataStride() > 0, ""); string functionName = "abi_decode_available_length_" + @@ -1186,11 +1187,11 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t mstore(array, length) dst := add(array, 0x20) - let src := offset - if gt(add(src, mul(length, )), end) { + let srcEnd := add(offset, mul(length, )) + if gt(srcEnd, end) { () } - for { let i := 0 } lt(i, length) { i := add(i, 1) } + for { let src := offset } lt(src, srcEnd) { src := add(src, ) } { let innerOffset := (src) @@ -1201,7 +1202,6 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t mstore(dst, (elementPos, end)) dst := add(dst, 0x20) - src := add(src, ) } } )"); diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index e3308b2de..c044760a0 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -60,8 +60,3 @@ std::shared_ptr Compiler::runtimeAssemblyPtr() const solAssert(m_context.runtimeContext(), ""); return m_context.runtimeContext()->assemblyPtr(); } - -evmasm::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const -{ - return m_runtimeContext.functionEntryLabelIfExists(_function); -} diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 0d021b723..1267c1a2d 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -62,10 +62,6 @@ public: 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. - evmasm::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const; - private: OptimiserSettings const m_optimiserSettings; CompilerContext m_runtimeContext; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 5503c3ecd..700f53d1f 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -421,7 +421,7 @@ void CompilerContext::appendInlineAssembly( if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( StackTooDeepError() << - errinfo_sourceLocation(_identifier.debugData->location) << + errinfo_sourceLocation(nativeLocationOf(_identifier)) << util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); if (_context == yul::IdentifierContext::RValue) diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index b36397864..cd71d5a4d 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -46,6 +46,7 @@ #include #include #include +#include namespace solidity::frontend { diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 9ae1741dd..5e49868a3 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -40,7 +40,7 @@ using namespace solidity::langutil; using solidity::util::Whiskers; using solidity::util::h256; -using solidity::util::toCompactHexWithPrefix; +using solidity::toCompactHexWithPrefix; unsigned const CompilerUtils::dataStartOffset = 4; size_t const CompilerUtils::freeMemoryPointer = 64; @@ -772,6 +772,33 @@ void CompilerUtils::convertType( Type::Category stackTypeCategory = _typeOnStack.category(); Type::Category targetTypeCategory = _targetType.category(); + if (stackTypeCategory == Type::Category::UserDefinedValueType) + { + solAssert(_cleanupNeeded, ""); + auto& userDefined = dynamic_cast(_typeOnStack); + solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType(), ""); + return convertType( + userDefined.underlyingType(), + _targetType, + _cleanupNeeded, + _chopSignBits, + _asPartOfArgumentDecoding + ); + } + if (targetTypeCategory == Type::Category::UserDefinedValueType) + { + solAssert(_cleanupNeeded, ""); + auto& userDefined = dynamic_cast(_targetType); + solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType()), ""); + return convertType( + _typeOnStack, + userDefined.underlyingType(), + _cleanupNeeded, + _chopSignBits, + _asPartOfArgumentDecoding + ); + } + if (auto contrType = dynamic_cast(&_typeOnStack)) solAssert(!contrType->isSuper(), "Cannot convert magic variable \"super\""); @@ -1525,10 +1552,13 @@ void CompilerUtils::storeStringData(bytesConstRef _data) unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords) { solAssert(_type.isValueType(), ""); + Type const* type = &_type; + if (auto const* userDefined = dynamic_cast(type)) + type = &userDefined->underlyingType(); - unsigned numBytes = _type.calldataEncodedSize(_padToWords); + unsigned numBytes = type->calldataEncodedSize(_padToWords); bool isExternalFunctionType = false; - if (auto const* funType = dynamic_cast(&_type)) + if (auto const* funType = dynamic_cast(type)) if (funType->kind() == FunctionType::Kind::External) isExternalFunctionType = true; if (numBytes == 0) @@ -1543,21 +1573,20 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda splitExternalFunctionType(true); else if (numBytes != 32) { - bool leftAligned = _type.category() == Type::Category::FixedBytes; // add leading or trailing zeros by dividing/multiplying depending on alignment unsigned shiftFactor = (32 - numBytes) * 8; rightShiftNumberOnStack(shiftFactor); - if (leftAligned) + if (type->leftAligned()) { leftShiftNumberOnStack(shiftFactor); cleanupNeeded = false; } - else if (IntegerType const* intType = dynamic_cast(&_type)) + else if (IntegerType const* intType = dynamic_cast(type)) if (!intType->isSigned()) cleanupNeeded = false; } if (_fromCalldata) - convertType(_type, _type, cleanupNeeded, false, true); + convertType(_type, *type, cleanupNeeded, false, true); return numBytes; } @@ -1612,12 +1641,10 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords, "Memory store of more than 32 bytes requested (Type: " + _type.toString(true) + ")." ); - bool leftAligned = _type.category() == Type::Category::FixedBytes; - if (_cleanup) convertType(_type, _type, true); - if (numBytes != 32 && !leftAligned && !_padToWords) + if (numBytes != 32 && !_type.leftAligned() && !_padToWords) // shift the value accordingly before storing leftShiftNumberOnStack((32 - numBytes) * 8); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 721f65614..f51f8c3cd 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -95,9 +95,9 @@ public: /// @returns the number of bytes consumed in memory. unsigned loadFromMemory( unsigned _offset, - Type const& _type = *TypeProvider::uint256(), - bool _fromCalldata = false, - bool _padToWords = false + Type const& _type, + bool _fromCalldata, + bool _padToWords ); /// Dynamic version of @see loadFromMemory, expects the memory offset on the stack. /// Stack pre: memory_offset diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 6c5a0abe0..5abc64636 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -52,6 +52,7 @@ #include #include +#include using namespace std; using namespace solidity; @@ -191,7 +192,12 @@ 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(static_cast(m_context.immutableMemoryOffset(*immutable)), *immutable->annotation().type); + CompilerUtils(m_context).loadFromMemory( + static_cast(m_context.immutableMemoryOffset(*immutable)), + *immutable->annotation().type, + false, + true + ); m_context.pushSubroutineSize(m_context.runtimeSub()); if (immutables.empty()) m_context << Instruction::DUP1; @@ -438,7 +444,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac // retrieve the function signature hash from the calldata if (!interfaceFunctions.empty()) { - CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true); + CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true, false); // stack now is: ? vector> sortedIDs; @@ -817,6 +823,16 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (suffix == "length") stackDiff--; } + else if ( + auto const* functionType = dynamic_cast(variable->type()); + functionType && functionType->kind() == FunctionType::Kind::External + ) + { + solAssert(suffix == "selector" || suffix == "address", ""); + solAssert(variable->type()->sizeOnStack() == 2, ""); + if (suffix == "selector") + stackDiff--; + } else solAssert(false, ""); } @@ -860,14 +876,37 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) } 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", ""); + if (auto const* arrayType = dynamic_cast(variable->type())) + { + if (arrayType->isDynamicallySized()) + { + solAssert(suffix == "offset" || suffix == "length", ""); + solAssert(variable->type()->sizeOnStack() == 2, ""); + if (suffix == "length") + stackDiff--; + } + else + { + solAssert(variable->type()->sizeOnStack() == 1, ""); + solAssert(suffix.empty(), ""); + } + } + else + { + auto const* structType = dynamic_cast(variable->type()); + solAssert(structType, ""); + solAssert(variable->type()->sizeOnStack() == 1, ""); + solAssert(suffix.empty(), ""); + } + } + else if ( + auto const* functionType = dynamic_cast(variable->type()); + functionType && functionType->kind() == FunctionType::Kind::External + ) + { + solAssert(suffix == "selector" || suffix == "address", ""); solAssert(variable->type()->sizeOnStack() == 2, ""); - if (suffix == "length") + if (suffix == "selector") stackDiff--; } else diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 4c86aa980..731cf1a41 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -957,6 +957,35 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) ); break; } + case FunctionType::Kind::Wrap: + case FunctionType::Kind::Unwrap: + { + solAssert(arguments.size() == 1, ""); + Type const* argumentType = arguments.at(0)->annotation().type; + Type const* functionCallType = _functionCall.annotation().type; + solAssert(argumentType, ""); + solAssert(functionCallType, ""); + FunctionType::Kind kind = functionType->kind(); + if (kind == FunctionType::Kind::Wrap) + { + solAssert( + argumentType->isImplicitlyConvertibleTo( + dynamic_cast(*functionCallType).underlyingType() + ), + "" + ); + solAssert(argumentType->isImplicitlyConvertibleTo(*function.parameterTypes()[0]), ""); + } + else + solAssert( + dynamic_cast(*argumentType) == + dynamic_cast(*function.parameterTypes()[0]), + "" + ); + + acceptAndConvert(*arguments[0], *function.parameterTypes()[0]); + break; + } case FunctionType::Kind::BlockHash: { acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true); @@ -1784,12 +1813,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "min" || member == "max") { MagicType const* arg = dynamic_cast(_memberAccess.expression().annotation().type); - IntegerType const* integerType = dynamic_cast(arg->typeArgument()); - - if (member == "min") - m_context << integerType->min(); + if (IntegerType const* integerType = dynamic_cast(arg->typeArgument())) + m_context << (member == "min" ? integerType->min() : integerType->max()); + else if (EnumType const* enumType = dynamic_cast(arg->typeArgument())) + m_context << (member == "min" ? enumType->minValue() : enumType->maxValue()); else - m_context << integerType->max(); + solAssert(false, "min/max not available for the given type."); + } else if ((set{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member)) { @@ -2082,8 +2112,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) solUnimplementedAssert( arrayType->location() == DataLocation::CallData && arrayType->isDynamicallySized() && - !arrayType->baseType()->isDynamicallyEncoded(), - "" + !arrayType->baseType()->isDynamicallyEncoded() ); if (_indexAccess.startExpression()) @@ -2156,6 +2185,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { // no-op } + else if (dynamic_cast(declaration)) + { + // no-op + } else if (dynamic_cast(declaration)) { // no-op diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 2622f2967..bd9252953 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -113,6 +113,7 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool if (!m_padded) { solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type."); + solAssert(m_dataType->category() != Type::Category::UserDefinedValueType, ""); if (m_dataType->category() == Type::Category::FixedBytes) m_context << u256(0) << Instruction::BYTE; m_context << Instruction::SWAP1 << Instruction::MSTORE8; @@ -154,12 +155,14 @@ ImmutableItem::ImmutableItem(CompilerContext& _compilerContext, VariableDeclarat void ImmutableItem::retrieveValue(SourceLocation const&, bool) const { - solUnimplementedAssert(m_dataType->isValueType(), ""); + solUnimplementedAssert(m_dataType->isValueType()); if (m_context.runtimeContext()) CompilerUtils(m_context).loadFromMemory( static_cast(m_context.immutableMemoryOffset(m_variable)), - *m_dataType + *m_dataType, + false, + true ); else for (auto&& slotName: m_context.immutableVariableSlotNames(m_variable)) @@ -169,7 +172,7 @@ void ImmutableItem::retrieveValue(SourceLocation const&, bool) const void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const { CompilerUtils utils(m_context); - solUnimplementedAssert(m_dataType->isValueType(), ""); + solUnimplementedAssert(m_dataType->isValueType()); solAssert(_sourceType.isValueType(), ""); utils.convertType(_sourceType, *m_dataType, true); @@ -178,7 +181,7 @@ void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, b utils.moveIntoStack(m_dataType->sizeOnStack()); else utils.copyToStackTop(m_dataType->sizeOnStack() + 1, m_dataType->sizeOnStack()); - utils.storeInMemoryDynamic(*m_dataType, false); + utils.storeInMemoryDynamic(*m_dataType); m_context << Instruction::POP; } @@ -224,27 +227,17 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const m_context << Instruction::POP << Instruction::SLOAD; else { + Type const* type = m_dataType; + if (type->category() == Type::Category::UserDefinedValueType) + type = type->encodingType(); bool cleaned = false; m_context << Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1 << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; - if (m_dataType->category() == Type::Category::FixedPoint) + if (type->category() == Type::Category::FixedPoint) // implementation should be very similar to the integer case. solUnimplemented("Not yet implemented - FixedPointType."); - if (m_dataType->category() == Type::Category::FixedBytes) - { - CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * m_dataType->storageBytes()); - cleaned = true; - } - else if ( - m_dataType->category() == Type::Category::Integer && - dynamic_cast(*m_dataType).isSigned() - ) - { - m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND; - cleaned = true; - } - else if (FunctionType const* fun = dynamic_cast(m_dataType)) + else if (FunctionType const* fun = dynamic_cast(type)) { if (fun->kind() == FunctionType::Kind::External) { @@ -258,10 +251,24 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const m_context << Instruction::MUL << Instruction::OR; } } + else if (type->leftAligned()) + { + CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * type->storageBytes()); + cleaned = true; + } + else if ( + type->category() == Type::Category::Integer && + dynamic_cast(*type).isSigned() + ) + { + m_context << u256(type->storageBytes() - 1) << Instruction::SIGNEXTEND; + cleaned = true; + } + if (!cleaned) { - solAssert(m_dataType->sizeOnStack() == 1, ""); - m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND; + solAssert(type->sizeOnStack() == 1, ""); + m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND; } } } @@ -327,10 +334,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc Instruction::AND; } } - else if (m_dataType->category() == Type::Category::FixedBytes) + else if (m_dataType->leftAligned()) { - solAssert(_sourceType.category() == Type::Category::FixedBytes, "source not fixed bytes"); - CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * dynamic_cast(*m_dataType).numBytes()); + solAssert(_sourceType.category() == Type::Category::FixedBytes || ( + _sourceType.encodingType() && + _sourceType.encodingType()->category() == Type::Category::FixedBytes + ), "source not fixed bytes"); + CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * m_dataType->storageBytes()); } else { diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp index 57d2a856c..69d30c6c8 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.cpp +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.cpp @@ -33,13 +33,8 @@ using namespace solidity::util; string MultiUseYulFunctionCollector::requestedFunctions() { - string result; - for (auto const& [name, code]: m_requestedFunctions) - { - solAssert(code != "< arguments; vector returnParameters; string body = _creator(arguments, returnParameters); solAssert(!body.empty(), ""); - m_requestedFunctions[_name] = Whiskers(R"( + m_code += Whiskers(R"( function () -> { } @@ -80,7 +75,7 @@ string MultiUseYulFunctionCollector::createFunction( ("args", joinHumanReadable(arguments)) ("retParams", joinHumanReadable(returnParameters)) ("body", body) - .render();; + .render(); } return _name; } diff --git a/libsolidity/codegen/MultiUseYulFunctionCollector.h b/libsolidity/codegen/MultiUseYulFunctionCollector.h index 7883fa668..050b5858f 100644 --- a/libsolidity/codegen/MultiUseYulFunctionCollector.h +++ b/libsolidity/codegen/MultiUseYulFunctionCollector.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace solidity::frontend { @@ -46,9 +47,8 @@ public: std::function&, std::vector&)> const& _creator ); - /// @returns concatenation of all generated functions. - /// Guarantees that the order of functions in the generated code is deterministic and - /// platform-independent. + /// @returns concatenation of all generated functions in the order in which they were + /// generated. /// Clears the internal list, i.e. calling it again will result in an /// empty return value. std::string requestedFunctions(); @@ -57,8 +57,8 @@ public: bool contains(std::string const& _name) const { return m_requestedFunctions.count(_name) > 0; } private: - /// Map from function name to code for a multi-use function. - std::map m_requestedFunctions; + std::set m_requestedFunctions; + std::string m_code; }; } diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index f61a76d32..1ad9aafa7 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1217,7 +1217,7 @@ string YulUtilFunctions::extractByteArrayLengthFunction() std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) { solAssert(_type.location() == DataLocation::Storage, ""); - solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "..."); + solUnimplementedAssert(_type.baseType()->storageBytes() <= 32); if (_type.isByteArray()) return resizeDynamicByteArrayFunction(_type); @@ -1259,7 +1259,7 @@ string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type) solAssert(_type.location() == DataLocation::Storage, ""); solAssert(_type.baseType()->category() != Type::Category::Mapping, ""); solAssert(!_type.isByteArray(), ""); - solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, ""); + solUnimplementedAssert(_type.baseType()->storageBytes() <= 32); string functionName = "cleanup_storage_array_end_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&](vector& _args, vector&) { @@ -1555,7 +1555,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type c if (!_fromType) _fromType = _type.baseType(); else if (_fromType->isValueType()) - solUnimplementedAssert(*_fromType == *_type.baseType(), ""); + solUnimplementedAssert(*_fromType == *_type.baseType()); string functionName = string{"array_push_from_"} + @@ -2416,7 +2416,7 @@ string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _ return m_functionCollector.createFunction(functionName, [&]() { if (_from.baseType()->isValueType()) { - solAssert(_from.baseType() == _to.baseType(), ""); + solAssert(*_from.baseType() == *_to.baseType(), ""); ABIFunctions abi(m_evmVersion, m_revertStrings, m_functionCollector); return Whiskers(R"( function (slot) -> memPtr { @@ -2912,24 +2912,20 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type) )"); templ("functionName", functionName); - unsigned storageBytes = _type.storageBytes(); - if (IntegerType const* type = dynamic_cast(&_type)) - if (type->isSigned() && storageBytes != 32) + Type const* encodingType = &_type; + if (_type.category() == Type::Category::UserDefinedValueType) + encodingType = _type.encodingType(); + unsigned storageBytes = encodingType->storageBytes(); + if (IntegerType const* intType = dynamic_cast(encodingType)) + if (intType->isSigned() && storageBytes != 32) { templ("cleaned", "signextend(" + to_string(storageBytes - 1) + ", value)"); 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 (leftAligned) + else if (encodingType->leftAligned()) templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)"); else templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")"); @@ -2965,7 +2961,7 @@ string YulUtilFunctions::prepareStoreFunction(Type const& _type) } )"); templ("functionName", functionName); - if (_type.category() == Type::Category::FixedBytes) + if (_type.leftAligned()) templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)"); else templ("actualPrepare", "value"); @@ -3168,6 +3164,16 @@ string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType co string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) { + if (_from.category() == Type::Category::UserDefinedValueType) + { + solAssert(_from == _to || _to == dynamic_cast(_from).underlyingType(), ""); + return conversionFunction(dynamic_cast(_from).underlyingType(), _to); + } + if (_to.category() == Type::Category::UserDefinedValueType) + { + solAssert(_from == _to || _from.isImplicitlyConvertibleTo(dynamic_cast(_to).underlyingType()), ""); + return conversionFunction(_from, dynamic_cast(_to).underlyingType()); + } if (_from.category() == Type::Category::Function) { solAssert(_to.category() == Type::Category::Function, ""); @@ -3294,13 +3300,14 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) bodyTemplate("cleanOutput", cleanupFunction(_to)); string convert; + solAssert(_to.category() != Type::Category::UserDefinedValueType, ""); if (auto const* toFixedBytes = dynamic_cast(&_to)) convert = shiftLeftFunction(256 - toFixedBytes->numBytes() * 8); else if (dynamic_cast(&_to)) - solUnimplementedAssert(false, ""); + solUnimplemented(""); else if (dynamic_cast(&_to)) { - solUnimplementedAssert(fromCategory != Type::Category::FixedPoint, ""); + solUnimplementedAssert(fromCategory != Type::Category::FixedPoint); convert = identityFunction(); } else if (toCategory == Type::Category::Enum) @@ -3339,8 +3346,8 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) body = "converted := value"; else { - solUnimplementedAssert(toStructType.location() == DataLocation::Memory, ""); - solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, ""); + solUnimplementedAssert(toStructType.location() == DataLocation::Memory); + solUnimplementedAssert(fromStructType.location() != DataLocation::Memory); if (fromStructType.location() == DataLocation::CallData) body = Whiskers(R"( @@ -3409,7 +3416,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) } case Type::Category::Tuple: { - solUnimplementedAssert(false, "Tuple conversion not implemented."); + solUnimplemented("Tuple conversion not implemented."); break; } case Type::Category::TypeType: @@ -3696,6 +3703,9 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy string YulUtilFunctions::cleanupFunction(Type const& _type) { + if (auto userDefinedValueType = dynamic_cast(&_type)) + return cleanupFunction(userDefinedValueType->underlyingType()); + string functionName = string("cleanup_") + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { Whiskers templ(R"( @@ -3816,6 +3826,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail case Type::Category::Mapping: case Type::Category::FixedBytes: case Type::Category::Contract: + case Type::Category::UserDefinedValueType: { templ("condition", "eq(value, " + cleanupFunction(_type) + "(value))"); break; @@ -4096,7 +4107,7 @@ string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctio else if (auto const* structType = dynamic_cast(&_type)) templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()"); else - solUnimplementedAssert(false, ""); + solUnimplemented(""); } return templ.render(); diff --git a/libsolidity/codegen/ir/Common.cpp b/libsolidity/codegen/ir/Common.cpp index e9bbaa4e0..49a6be5e5 100644 --- a/libsolidity/codegen/ir/Common.cpp +++ b/libsolidity/codegen/ir/Common.cpp @@ -22,9 +22,13 @@ #include +#include + using namespace std; -using namespace solidity::util; +using namespace solidity::langutil; using namespace solidity::frontend; +using namespace solidity::util; +using namespace solidity::yul; namespace solidity::frontend { @@ -127,20 +131,20 @@ string IRNames::zeroValue(Type const& _type, string const& _variableName) return "zero_" + _type.identifier() + _variableName; } -string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context) +string dispenseLocationComment(langutil::SourceLocation const& _location, IRGenerationContext& _context) { solAssert(_location.sourceName, ""); - return "/// @src " - + to_string(_context.sourceIndices().at(*_location.sourceName)) - + ":" - + to_string(_location.start) - + ":" - + to_string(_location.end); + _context.markSourceUsed(*_location.sourceName); + return "/// " + AsmPrinter::formatSourceLocation( + _location, + _context.sourceIndices(), + _context.soliditySourceProvider() + ); } -string sourceLocationComment(ASTNode const& _node, IRGenerationContext const& _context) +string dispenseLocationComment(ASTNode const& _node, IRGenerationContext& _context) { - return sourceLocationComment(_node.location(), _context); + return dispenseLocationComment(_node.location(), _context); } } diff --git a/libsolidity/codegen/ir/Common.h b/libsolidity/codegen/ir/Common.h index 67b0cffdc..3a860f8f1 100644 --- a/libsolidity/codegen/ir/Common.h +++ b/libsolidity/codegen/ir/Common.h @@ -71,10 +71,11 @@ struct IRNames /** * @returns a source location comment in the form of - * `/// @src ::`. + * `/// @src ::` + * and marks the source index as used. */ -std::string sourceLocationComment(langutil::SourceLocation const& _location, IRGenerationContext const& _context); -std::string sourceLocationComment(ASTNode const& _node, IRGenerationContext const& _context); +std::string dispenseLocationComment(langutil::SourceLocation const& _location, IRGenerationContext& _context); +std::string dispenseLocationComment(ASTNode const& _node, IRGenerationContext& _context); } diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index b8affcbd8..fdc617220 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -72,13 +73,15 @@ public: ExecutionContext _executionContext, RevertStrings _revertStrings, OptimiserSettings _optimiserSettings, - std::map _sourceIndices + std::map _sourceIndices, + langutil::CharStreamProvider const* _soliditySourceProvider ): m_evmVersion(_evmVersion), m_executionContext(_executionContext), m_revertStrings(_revertStrings), m_optimiserSettings(std::move(_optimiserSettings)), - m_sourceIndices(std::move(_sourceIndices)) + m_sourceIndices(std::move(_sourceIndices)), + m_soliditySourceProvider(_soliditySourceProvider) {} MultiUseYulFunctionCollector& functionCollector() { return m_functions; } @@ -166,15 +169,20 @@ public: void copyFunctionIDsFrom(IRGenerationContext const& _other); std::map const& sourceIndices() const { return m_sourceIndices; } + void markSourceUsed(std::string const& _name) { m_usedSourceNames.insert(_name); } + std::set const& usedSourceNames() const { return m_usedSourceNames; } bool immutableRegistered(VariableDeclaration const& _varDecl) const { return m_immutableVariables.count(&_varDecl); } + langutil::CharStreamProvider const* soliditySourceProvider() const { return m_soliditySourceProvider; } + private: langutil::EVMVersion m_evmVersion; ExecutionContext m_executionContext; RevertStrings m_revertStrings; OptimiserSettings m_optimiserSettings; std::map m_sourceIndices; + std::set m_usedSourceNames; ContractDefinition const* m_mostDerivedContract = nullptr; std::map m_localVariables; /// Memory offsets reserved for the values of immutable variables during contract creation. @@ -211,6 +219,8 @@ private: std::map m_functionIDs; std::set m_subObjects; + + langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; }; } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 418d112e0..c1bfce4e3 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -45,8 +45,9 @@ using namespace std; using namespace solidity; -using namespace solidity::util; using namespace solidity::frontend; +using namespace solidity::langutil; +using namespace solidity::util; namespace { @@ -115,7 +116,7 @@ pair IRGenerator::run( " * !USE AT YOUR OWN RISK! *\n" " *=====================================================*/\n\n"; - return {warning + ir, warning + asmStack.print()}; + return {warning + ir, warning + asmStack.print(m_context.soliditySourceProvider())}; } string IRGenerator::generate( @@ -131,12 +132,21 @@ string IRGenerator::generate( subObjectsSources += _otherYulSources.at(subObject); return subObjectsSources; }; + auto formatUseSrcMap = [](IRGenerationContext const& _context) -> string + { + return joinHumanReadable( + ranges::views::transform(_context.usedSourceNames(), [_context](string const& _sourceName) { + return to_string(_context.sourceIndices().at(_sourceName)) + ":" + escapeAndQuoteString(_sourceName); + }), + ", " + ); + }; Whiskers t(R"( - /// @use-src + /// @use-src object "" { code { - + @@ -147,9 +157,10 @@ string IRGenerator::generate( } + /// @use-src object "" { code { - + let called_via_delegatecall := iszero(eq(loadimmutable(""), address())) @@ -168,19 +179,8 @@ string IRGenerator::generate( for (VariableDeclaration const* var: ContractType(_contract).immutableVariables()) m_context.registerImmutableVariable(*var); - auto invertedSourceIndicies = invertMap(m_context.sourceIndices()); - - string useSrcMap = joinHumanReadable( - ranges::views::transform(invertedSourceIndicies, [](auto&& _pair) { - return to_string(_pair.first) + ":" + escapeAndQuoteString(_pair.second); - }), - ", " - ); - - t("useSrcMap", useSrcMap); - t("sourceLocationComment", sourceLocationComment(_contract, m_context)); - t("CreationObject", IRNames::creationObject(_contract)); + t("sourceLocationCommentCreation", dispenseLocationComment(_contract)); t("library", _contract.isLibrary()); FunctionDefinition const* constructor = _contract.constructor(); @@ -210,6 +210,7 @@ string IRGenerator::generate( // 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)); + t("useSrcMapCreation", formatUseSrcMap(m_context)); resetContext(_contract, ExecutionContext::Deployed); @@ -220,6 +221,7 @@ string IRGenerator::generate( // Do not register immutables to avoid assignment. t("DeployedObject", IRNames::deployedObject(_contract)); + t("sourceLocationCommentDeployed", dispenseLocationComment(_contract)); t("library_address", IRNames::libraryAddressImmutable()); t("dispatch", dispatchRoutine(_contract)); set deployedFunctionList = generateQueuedFunctions(); @@ -229,6 +231,7 @@ string IRGenerator::generate( t("metadataName", yul::Object::metadataName()); t("cborMetadata", toHex(_cborMetadata)); + t("useSrcMapDeployed", formatUseSrcMap(m_context)); // This has to be called only after all other code generation for the deployed object is complete. bool deployedInvolvesAssembly = m_context.inlineAssemblySeen(); @@ -292,7 +295,7 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefin } )"); - templ("sourceLocationComment", sourceLocationComment(_contract, m_context)); + templ("sourceLocationComment", dispenseLocationComment(_contract)); templ("functionName", funName); templ("panic", m_utils.panicFunction(PanicCode::InvalidInternalFunction)); templ("in", suffixedVariableNameList("in_", 0, arity.in)); @@ -337,6 +340,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + /// @ast-id function () -> { @@ -345,10 +349,11 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) )"); - t("sourceLocationComment", sourceLocationComment(_function, m_context)); + t("astID", to_string(_function.id())); + t("sourceLocationComment", dispenseLocationComment(_function)); t( "contractSourceLocationComment", - sourceLocationComment(m_context.mostDerivedContract(), m_context) + dispenseLocationComment(m_context.mostDerivedContract()) ); t("functionName", functionName); @@ -404,6 +409,7 @@ string IRGenerator::generateModifier( return m_context.functionCollector().createFunction(functionName, [&]() { m_context.resetLocalVariables(); Whiskers t(R"( + /// @ast-id function () -> { @@ -434,10 +440,11 @@ string IRGenerator::generateModifier( _modifierInvocation.name().annotation().referencedDeclaration ); solAssert(modifier, ""); - t("sourceLocationComment", sourceLocationComment(*modifier, m_context)); + t("astID", to_string(modifier->id())); + t("sourceLocationComment", dispenseLocationComment(*modifier)); t( "contractSourceLocationComment", - sourceLocationComment(m_context.mostDerivedContract(), m_context) + dispenseLocationComment(m_context.mostDerivedContract()) ); switch (*_modifierInvocation.name().annotation().requiredLookup) @@ -497,10 +504,10 @@ string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const& } )"); - t("sourceLocationComment", sourceLocationComment(_function, m_context)); + t("sourceLocationComment", dispenseLocationComment(_function)); t( "contractSourceLocationComment", - sourceLocationComment(m_context.mostDerivedContract(), m_context) + dispenseLocationComment(m_context.mostDerivedContract()) ); t("functionName", functionName); vector retParams; @@ -537,18 +544,20 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) if (_varDecl.immutable()) { solAssert(paramTypes.empty(), ""); - solUnimplementedAssert(type->sizeOnStack() == 1, ""); + solUnimplementedAssert(type->sizeOnStack() == 1); return Whiskers(R"( + /// @ast-id function () -> rval { rval := loadimmutable("") } )") - ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ("astID", to_string(_varDecl.id())) + ("sourceLocationComment", dispenseLocationComment(_varDecl)) ( "contractSourceLocationComment", - sourceLocationComment(m_context.mostDerivedContract(), m_context) + dispenseLocationComment(m_context.mostDerivedContract()) ) ("functionName", functionName) ("id", to_string(_varDecl.id())) @@ -558,16 +567,18 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) { solAssert(paramTypes.empty(), ""); return Whiskers(R"( + /// @ast-id function () -> { := () } )") - ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ("astID", to_string(_varDecl.id())) + ("sourceLocationComment", dispenseLocationComment(_varDecl)) ( "contractSourceLocationComment", - sourceLocationComment(m_context.mostDerivedContract(), m_context) + dispenseLocationComment(m_context.mostDerivedContract()) ) ("functionName", functionName) ("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl)) @@ -680,6 +691,7 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) } return Whiskers(R"( + /// @ast-id function () -> { @@ -690,10 +702,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) ("params", joinHumanReadable(parameters)) ("retVariables", joinHumanReadable(returnVariables)) ("code", std::move(code)) - ("sourceLocationComment", sourceLocationComment(_varDecl, m_context)) + ("astID", to_string(_varDecl.id())) + ("sourceLocationComment", dispenseLocationComment(_varDecl)) ( "contractSourceLocationComment", - sourceLocationComment(m_context.mostDerivedContract(), m_context) + dispenseLocationComment(m_context.mostDerivedContract()) ) .render(); }); @@ -801,7 +814,7 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) m_context.resetLocalVariables(); m_context.functionCollector().createFunction(IRNames::constructor(*contract), [&]() { Whiskers t(R"( - + function () { @@ -816,15 +829,18 @@ void IRGenerator::generateConstructors(ContractDefinition const& _contract) for (ASTPointer const& varDecl: contract->constructor()->parameters()) params += m_context.addLocalVariable(*varDecl).stackSlots(); - t("sourceLocationComment", sourceLocationComment( + if (contract->constructor()) + t("astIDComment", "/// @ast-id " + to_string(contract->constructor()->id()) + "\n"); + else + t("astIDComment", ""); + t("sourceLocationComment", dispenseLocationComment( contract->constructor() ? - contract->constructor()->location() : - contract->location(), - m_context + dynamic_cast(*contract->constructor()) : + dynamic_cast(*contract) )); t( "contractSourceLocationComment", - sourceLocationComment(m_context.mostDerivedContract(), m_context) + dispenseLocationComment(m_context.mostDerivedContract()) ); t("params", joinHumanReadable(params)); @@ -909,8 +925,8 @@ string IRGenerator::deployCode(ContractDefinition const& _contract) else for (VariableDeclaration const* immutable: ContractType(_contract).immutableVariables()) { - solUnimplementedAssert(immutable->type()->isValueType(), ""); - solUnimplementedAssert(immutable->type()->sizeOnStack() == 1, ""); + solUnimplementedAssert(immutable->type()->isValueType()); + solUnimplementedAssert(immutable->type()->sizeOnStack() == 1); immutables.emplace_back(map{ {"immutableName"s, to_string(immutable->id())}, {"value"s, "mload(" + to_string(m_context.immutableMemoryOffset(*immutable)) + ")"} @@ -1063,7 +1079,14 @@ void IRGenerator::resetContext(ContractDefinition const& _contract, ExecutionCon m_context.internalDispatchClean(), "Reset internal dispatch map without consuming it." ); - IRGenerationContext newContext(m_evmVersion, _context, m_context.revertStrings(), m_optimiserSettings, m_context.sourceIndices()); + IRGenerationContext newContext( + m_evmVersion, + _context, + m_context.revertStrings(), + m_optimiserSettings, + m_context.sourceIndices(), + m_context.soliditySourceProvider() + ); newContext.copyFunctionIDsFrom(m_context); m_context = move(newContext); @@ -1071,3 +1094,8 @@ void IRGenerator::resetContext(ContractDefinition const& _contract, ExecutionCon for (auto const& var: ContractType(_contract).stateVariables()) m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } + +string IRGenerator::dispenseLocationComment(ASTNode const& _node) +{ + return ::dispenseLocationComment(_node, m_context); +} diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index 85d611108..85b570c7f 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -28,7 +28,10 @@ #include #include #include + +#include #include + #include namespace solidity::frontend @@ -45,11 +48,19 @@ public: langutil::EVMVersion _evmVersion, RevertStrings _revertStrings, OptimiserSettings _optimiserSettings, - std::map _sourceIndices + std::map _sourceIndices, + langutil::CharStreamProvider const* _soliditySourceProvider ): m_evmVersion(_evmVersion), m_optimiserSettings(_optimiserSettings), - m_context(_evmVersion, ExecutionContext::Creation, _revertStrings, std::move(_optimiserSettings), std::move(_sourceIndices)), + m_context( + _evmVersion, + ExecutionContext::Creation, + _revertStrings, + std::move(_optimiserSettings), + std::move(_sourceIndices), + _soliditySourceProvider + ), m_utils(_evmVersion, m_context.revertStrings(), m_context.functionCollector()) {} @@ -119,6 +130,8 @@ private: void resetContext(ContractDefinition const& _contract, ExecutionContext _context); + std::string dispenseLocationComment(ASTNode const& _node); + langutil::EVMVersion const m_evmVersion; OptimiserSettings const m_optimiserSettings; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index bee0e1611..6d7ee86f5 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -108,7 +108,7 @@ private: { auto const& reference = m_references.at(&_identifier); auto const varDecl = dynamic_cast(reference.declaration); - solUnimplementedAssert(varDecl, ""); + solUnimplementedAssert(varDecl); string const& suffix = reference.suffix; string value; @@ -188,6 +188,18 @@ private: solAssert(suffix == "offset" || suffix == "length", ""); value = IRVariable{*varDecl}.part(suffix).name(); } + else if ( + auto const* functionType = dynamic_cast(varDecl->type()); + functionType && functionType->kind() == FunctionType::Kind::External + ) + { + solAssert(suffix == "selector" || suffix == "address", ""); + solAssert(varDecl->type()->sizeOnStack() == 2, ""); + if (suffix == "selector") + value = IRVariable{*varDecl}.part("functionSelector").name(); + else + value = IRVariable{*varDecl}.part("address").name(); + } else solAssert(false, ""); @@ -217,7 +229,7 @@ std::ostringstream& IRGeneratorForStatementsBase::appendCode(bool _addLocationCo m_currentLocation.isValid() && m_lastLocation != m_currentLocation ) - m_code << sourceLocationComment(m_currentLocation, m_context) << "\n"; + m_code << dispenseLocationComment(m_currentLocation, m_context) << "\n"; m_lastLocation = m_currentLocation; @@ -266,7 +278,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va _varDecl.immutable() ? IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ - util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), + toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), m_context.storageLocationOfStateVariable(_varDecl).second }}, *_varDecl.value() @@ -340,7 +352,7 @@ string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const := } )"); - templ("sourceLocationComment", sourceLocationComment(_constant, m_context)); + templ("sourceLocationComment", dispenseLocationComment(_constant, m_context)); templ("functionName", functionName); IRGeneratorForStatements generator(m_context, m_utils); solAssert(_constant.value(), ""); @@ -737,7 +749,7 @@ bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation) ) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n"; } else - solUnimplementedAssert(false, "Unary operator not yet implemented"); + solUnimplemented("Unary operator not yet implemented"); } else if (resultType.category() == Type::Category::FixedBytes) { @@ -755,7 +767,7 @@ bool IRGeneratorForStatements::visit(UnaryOperation const& _unaryOperation) appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression()); } else - solUnimplementedAssert(false, "Unary operator not yet implemented"); + solUnimplemented("Unary operator not yet implemented"); return false; } @@ -798,10 +810,8 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) if (auto type = dynamic_cast(commonType)) isSigned = type->isSigned(); - string args = - expressionAsType(_binOp.leftExpression(), *commonType, true) + - ", " + - expressionAsType(_binOp.rightExpression(), *commonType, true); + string args = expressionAsType(_binOp.leftExpression(), *commonType, true); + args += ", " + expressionAsType(_binOp.rightExpression(), *commonType, true); string expr; if (op == Token::Equal) @@ -1051,6 +1061,24 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) ); break; } + case FunctionType::Kind::Wrap: + case FunctionType::Kind::Unwrap: + { + solAssert(arguments.size() == 1, ""); + FunctionType::Kind kind = functionType->kind(); + if (kind == FunctionType::Kind::Wrap) + solAssert( + type(*arguments.at(0)).isImplicitlyConvertibleTo( + dynamic_cast(type(_functionCall)).underlyingType() + ), + "" + ); + else + solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType, ""); + + define(_functionCall, *arguments.at(0)); + break; + } case FunctionType::Kind::Assert: case FunctionType::Kind::Require: { @@ -1115,16 +1143,14 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory()); IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32)); + string dataAreaFunction = m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()); + string arrayLengthFunction = m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()); define(hashVariable) << "keccak256(" << - m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()) << - "(" << - array.commaSeparatedList() << - "), " << - m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) << - "(" << - array.commaSeparatedList() << - "))\n"; + (dataAreaFunction + "(" + array.commaSeparatedList() + ")") << + ", " << + (arrayLengthFunction + "(" + array.commaSeparatedList() +")") << + ")\n"; IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4)); define(selectorVariable, hashVariable); selector = selectorVariable.name(); @@ -1249,16 +1275,14 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) { auto array = convert(*arguments[0], *arrayType); + string dataAreaFunction = m_utils.arrayDataAreaFunction(*arrayType); + string arrayLengthFunction = m_utils.arrayLengthFunction(*arrayType); define(_functionCall) << "keccak256(" << - m_utils.arrayDataAreaFunction(*arrayType) << - "(" << - array.commaSeparatedList() << - "), " << - m_utils.arrayLengthFunction(*arrayType) << - "(" << - array.commaSeparatedList() << - "))\n"; + (dataAreaFunction + "(" + array.commaSeparatedList() + ")") << + ", " << + (arrayLengthFunction + "(" + array.commaSeparatedList() +")") << + ")\n"; } break; } @@ -1534,7 +1558,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) setLocation(_options); FunctionType const& previousType = dynamic_cast(*_options.expression().annotation().type); - solUnimplementedAssert(!previousType.bound(), ""); + solUnimplementedAssert(!previousType.bound()); // Copy over existing values. for (auto const& item: previousType.stackItems()) @@ -1646,11 +1670,14 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << ")\n"; else if (member == "code") + { + string externalCodeFunction = m_utils.externalCodeFunction(); define(_memberAccess) << - m_utils.externalCodeFunction() << + externalCodeFunction << "(" << expressionAsType(_memberAccess.expression(), *TypeProvider::address()) << ")\n"; + } else if (member == "codehash") define(_memberAccess) << "extcodehash(" << @@ -1701,7 +1728,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { solUnimplementedAssert( dynamic_cast(*_memberAccess.expression().annotation().type).kind() == - FunctionType::Kind::External, "" + FunctionType::Kind::External ); define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("address")); } @@ -1785,12 +1812,26 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else if (member == "min" || member == "max") { MagicType const* arg = dynamic_cast(_memberAccess.expression().annotation().type); - IntegerType const* integerType = dynamic_cast(arg->typeArgument()); - if (member == "min") - define(_memberAccess) << formatNumber(integerType->min()) << "\n"; + string requestedValue; + if (IntegerType const* integerType = dynamic_cast(arg->typeArgument())) + { + if (member == "min") + requestedValue = formatNumber(integerType->min()); + else + requestedValue = formatNumber(integerType->max()); + } + else if (EnumType const* enumType = dynamic_cast(arg->typeArgument())) + { + if (member == "min") + requestedValue = to_string(enumType->minValue()); + else + requestedValue = to_string(enumType->maxValue()); + } else - define(_memberAccess) << formatNumber(integerType->max()) << "\n"; + solAssert(false, "min/max requested on unexpected type."); + + define(_memberAccess) << requestedValue << "\n"; } else if (set{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member)) { @@ -1932,7 +1973,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef); } else if (auto const* variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - handleVariableReference(*variable, _memberAccess); + handleVariableReference(*variable, _memberAccess); else if (memberFunctionType) { switch (memberFunctionType->kind()) @@ -1990,6 +2031,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) } else if (EnumType const* enumType = dynamic_cast(&actualType)) define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n"; + else if (dynamic_cast(&actualType)) + solAssert(member == "wrap" || member == "unwrap", ""); else if (auto const* arrayType = dynamic_cast(&actualType)) solAssert(arrayType->isByteArray() && member == "concat", ""); else @@ -2135,8 +2178,9 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) } case DataLocation::CallData: { + string indexAccessFunction = m_utils.calldataArrayIndexAccessFunction(arrayType); string const indexAccessFunctionCall = - m_utils.calldataArrayIndexAccessFunction(arrayType) + + indexAccessFunction + "(" + IRVariable(_indexAccess.baseExpression()).commaSeparatedList() + ", " + @@ -2232,7 +2276,7 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces break; } default: - solUnimplementedAssert(false, "Index range accesses is implemented only on calldata arrays."); + solUnimplemented("Index range accesses is implemented only on calldata arrays."); } } @@ -2300,6 +2344,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."); @@ -2853,13 +2901,16 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable ")\n"; } else if (auto const* literalType = dynamic_cast(&_value.type())) + { + string writeUInt = m_utils.writeToMemoryFunction(*TypeProvider::uint256()); appendCode() << - m_utils.writeToMemoryFunction(*TypeProvider::uint256()) << + writeUInt << "(" << _memory.address << ", " << m_utils.copyLiteralToMemoryFunction(literalType->value()) + "()" << ")\n"; + } else { solAssert(_lvalue.type.sizeOnStack() == 1, ""); @@ -2871,8 +2922,8 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable [&](IRLValue::Stack const& _stack) { assign(_stack.variable, _value); }, [&](IRLValue::Immutable const& _immutable) { - solUnimplementedAssert(_lvalue.type.isValueType(), ""); - solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1, ""); + solUnimplementedAssert(_lvalue.type.isValueType()); + solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); solAssert(_lvalue.type == *_immutable.variable->type(), ""); size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable); @@ -2931,15 +2982,18 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) define(result, _stack.variable); }, [&](IRLValue::Immutable const& _immutable) { - solUnimplementedAssert(_lvalue.type.isValueType(), ""); - solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1, ""); + solUnimplementedAssert(_lvalue.type.isValueType()); + solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); solAssert(_lvalue.type == *_immutable.variable->type(), ""); if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation) + { + string readFunction = m_utils.readFromMemory(*_immutable.variable->type()); define(result) << - m_utils.readFromMemory(*_immutable.variable->type()) << + readFunction << "(" << to_string(m_context.immutableMemoryOffset(*_immutable.variable)) << ")\n"; + } else define(result) << "loadimmutable(\"" << to_string(_immutable.variable->id()) << "\")\n"; }, diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index dc7547433..bf3e5ea05 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -188,7 +188,8 @@ bool BMC::visit(FunctionDefinition const& _function) { reset(); initFunction(_function); - m_context.addAssertion(state().txTypeConstraints() && state().txFunctionConstraints(_function)); + if (_function.isConstructor() || _function.isPublic()) + m_context.addAssertion(state().txTypeConstraints() && state().txFunctionConstraints(_function)); resetStateVariables(); } @@ -482,6 +483,8 @@ void BMC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BlockHash: case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: + case FunctionType::Kind::Unwrap: + case FunctionType::Kind::Wrap: [[fallthrough]]; default: SMTEncoder::endVisit(_funCall); diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 140b2a27d..fc97ab0ca 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -34,6 +34,7 @@ #include +#include #include #ifdef HAVE_Z3_DLOPEN @@ -552,6 +553,8 @@ void CHC::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::BlockHash: case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: + case FunctionType::Kind::Unwrap: + case FunctionType::Kind::Wrap: [[fallthrough]]; default: SMTEncoder::endVisit(_funCall); @@ -743,13 +746,15 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) /// so we just add the nondet_interface predicate. solAssert(m_currentContract, ""); - if (isTrustedExternalCall(&_funCall.expression())) + auto [callExpr, callOptions] = functionCallExpression(_funCall); + + if (isTrustedExternalCall(callExpr)) { externalFunctionCallToTrustedCode(_funCall); return; } - FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + FunctionType const& funType = dynamic_cast(*callExpr->annotation().type); auto kind = funType.kind(); solAssert( kind == FunctionType::Kind::External || @@ -773,6 +778,19 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) if (!m_currentFunction || m_currentFunction->isConstructor()) return; + if (callOptions) + { + optional valueIndex; + for (auto&& [i, name]: callOptions->names() | ranges::views::enumerate) + if (name && *name == "value") + { + valueIndex = i; + break; + } + solAssert(valueIndex, ""); + state().addBalance(state().thisAddress(), 0 - expr(*callOptions->options().at(*valueIndex))); + } + auto preCallState = vector{state().state()} + currentStateVariables(); if (!usesStaticCall) @@ -829,6 +847,8 @@ void CHC::externalFunctionCallToTrustedCode(FunctionCall const& _funCall) state().newTx(); // set the transaction sender as this contract m_context.addAssertion(state().txMember("msg.sender") == state().thisAddress()); + // set the transaction value as 0 + m_context.addAssertion(state().txMember("msg.value") == 0); // set the origin to be the current transaction origin m_context.addAssertion(state().txMember("tx.origin") == txOrigin); @@ -1451,10 +1471,12 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall) return smtutil::Expression(true); auto contractAddressValue = [this](FunctionCall const& _f) { - FunctionType const& funType = dynamic_cast(*_f.expression().annotation().type); + auto [callExpr, callOptions] = functionCallExpression(_f); + + FunctionType const& funType = dynamic_cast(*callExpr->annotation().type); if (funType.kind() == FunctionType::Kind::Internal) return state().thisAddress(); - if (MemberAccess const* callBase = dynamic_cast(&_f.expression())) + if (MemberAccess const* callBase = dynamic_cast(callExpr)) return expr(callBase->expression()); solAssert(false, "Unreachable!"); }; @@ -1870,7 +1892,10 @@ optional CHC::generateCounterexample(CHCSolverInterface::CexGraph const& if (!pred->isConstructorSummary()) for (unsigned v: callGraph[node]) _dfs(node, v, depth + 1, _dfs); - calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node), m_charStreamProvider)); + + bool appendTxVars = pred->isConstructorSummary() || pred->isFunctionSummary() || pred->isExternalCallUntrusted(); + + calls.push_front(string(depth * 4, ' ') + pred->formatSummaryCall(nodeArgs(node), m_charStreamProvider, appendTxVars)); if (pred->isInternalCall()) calls.front() += " -- internal call"; else if (pred->isExternalCallTrusted()) diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 9c27a4e92..23aa7590e 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -200,7 +200,8 @@ bool Predicate::isInterface() const string Predicate::formatSummaryCall( vector const& _args, - langutil::CharStreamProvider const& _charStreamProvider + langutil::CharStreamProvider const& _charStreamProvider, + bool _appendTxVars ) const { solAssert(isSummary(), ""); @@ -214,19 +215,67 @@ string Predicate::formatSummaryCall( } /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). - /// Here we are interested in preInputVars to format the function call, - /// and in txData to retrieve `msg.value`. + /// Here we are interested in preInputVars to format the function call. - string value; - if (auto v = readTxVars(_args.at(4)).at("msg.value")) + string txModel; + + if (_appendTxVars) { - bigint x(*v); - if (x > 0) - value = "{ value: " + *v + " }"; + set txVars; + if (isFunctionSummary()) + { + solAssert(programFunction(), ""); + if (programFunction()->isPayable()) + txVars.insert("msg.value"); + } + else if (isConstructorSummary()) + { + FunctionDefinition const* fun = programFunction(); + if (fun && fun->isPayable()) + txVars.insert("msg.value"); + } + + struct TxVarsVisitor: public ASTConstVisitor + { + bool visit(MemberAccess const& _memberAccess) + { + Expression const* memberExpr = SMTEncoder::innermostTuple(_memberAccess.expression()); + + Type const* exprType = memberExpr->annotation().type; + solAssert(exprType, ""); + if (exprType->category() == Type::Category::Magic) + if (auto const* identifier = dynamic_cast(memberExpr)) + { + ASTString const& name = identifier->name(); + if (name == "block" || name == "msg" || name == "tx") + txVars.insert(name + "." + _memberAccess.memberName()); + } + + return true; + } + + set txVars; + } txVarsVisitor; + + if (auto fun = programFunction()) + { + fun->accept(txVarsVisitor); + txVars += txVarsVisitor.txVars; + } + + // Here we are interested in txData from the summary predicate. + auto txValues = readTxVars(_args.at(4)); + vector values; + for (auto const& _var: txVars) + if (auto v = txValues.at(_var)) + values.push_back(_var + ": " + *v); + + if (!values.empty()) + txModel = "{ " + boost::algorithm::join(values, ", ") + " }"; } if (auto contract = programContract()) - return contract->name() + ".constructor()" + value; + return contract->name() + ".constructor()" + txModel; auto stateVars = stateVariables(); solAssert(stateVars.has_value(), ""); @@ -237,7 +286,7 @@ string Predicate::formatSummaryCall( 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(); + auto inTypes = SMTEncoder::replaceUserTypes(FunctionType(*fun).parameterTypes()); vector> functionArgsCex = formatExpressions(vector(first, last), inTypes); vector functionArgs; @@ -262,7 +311,7 @@ string Predicate::formatSummaryCall( solAssert(fun->annotation().contract, ""); prefix = fun->annotation().contract->name() + "."; } - return prefix + fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")" + value; + return prefix + fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")" + txModel; } vector> Predicate::summaryStateValues(vector const& _args) const @@ -317,7 +366,7 @@ vector> Predicate::summaryPostInputValues(vector inValues(first, last); solAssert(inValues.size() == inParams.size(), ""); - auto inTypes = FunctionType(*function).parameterTypes(); + auto inTypes = SMTEncoder::replaceUserTypes(FunctionType(*function).parameterTypes()); return formatExpressions(inValues, inTypes); } @@ -339,7 +388,7 @@ vector> Predicate::summaryPostOutputValues(vector outValues(first, _args.end()); solAssert(outValues.size() == function->returnParameters().size(), ""); - auto outTypes = FunctionType(*function).returnParameterTypes(); + auto outTypes = SMTEncoder::replaceUserTypes(FunctionType(*function).returnParameterTypes()); return formatExpressions(outValues, outTypes); } @@ -384,7 +433,27 @@ optional Predicate::expressionToString(smtutil::Expression const& _expr, { solAssert(_expr.sort->kind == Kind::Int, ""); solAssert(_expr.arguments.empty(), ""); - // TODO assert that _expr.name is a number. + + if ( + _type->category() == Type::Category::Address || + _type->category() == Type::Category::FixedBytes + ) + { + try + { + if (_expr.name == "0") + return "0x0"; + // For some reason the code below returns "0x" for "0". + return toHex(toCompactBigEndian(bigint(_expr.name)), HexPrefix::Add, HexCase::Lower); + } + catch (out_of_range const&) + { + } + catch (invalid_argument const&) + { + } + } + return _expr.name; } if (smt::isBool(*_type)) @@ -526,9 +595,9 @@ map> Predicate::readTxVars(smtutil::Expression const& _ {"block.number", TypeProvider::uint256()}, {"block.timestamp", TypeProvider::uint256()}, {"blockhash", TypeProvider::array(DataLocation::Memory, TypeProvider::uint256())}, - {"msg.data", TypeProvider::bytesMemory()}, + {"msg.data", TypeProvider::array(DataLocation::CallData)}, {"msg.sender", TypeProvider::address()}, - {"msg.sig", TypeProvider::uint256()}, + {"msg.sig", TypeProvider::fixedBytes(4)}, {"msg.value", TypeProvider::uint256()}, {"tx.gasprice", TypeProvider::uint256()}, {"tx.origin", TypeProvider::address()} diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index 6f5a05a50..7357c9beb 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -149,7 +149,8 @@ public: /// with _args. std::string formatSummaryCall( std::vector const& _args, - langutil::CharStreamProvider const& _charStreamProvider + langutil::CharStreamProvider const& _charStreamProvider, + bool _appendTxVars = false ) const; /// @returns the values of the state variables from _args at the point diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 6ecb57a7f..11ac5ab7a 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -34,7 +34,7 @@ #include - +#include #include using namespace std; @@ -370,7 +370,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment) Token op = _assignment.assignmentOperator(); solAssert(TokenTraits::isAssignmentOp(op), ""); - if (!smt::isSupportedType(*_assignment.annotation().type)) + if (!isSupportedType(*_assignment.annotation().type)) { // Give it a new index anyway to keep the SSA scheme sound. @@ -406,6 +406,9 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple) if (_tuple.annotation().type->category() == Type::Category::Function) return; + if (_tuple.annotation().type->category() == Type::Category::TypeType) + return; + createExpr(_tuple); if (_tuple.isInlineArray()) @@ -560,6 +563,35 @@ bool SMTEncoder::visit(Conditional const& _op) return false; } +bool SMTEncoder::visit(FunctionCall const& _funCall) +{ + auto functionCallKind = *_funCall.annotation().kind; + if (functionCallKind != FunctionCallKind::FunctionCall) + return true; + + FunctionType const& funType = dynamic_cast(*_funCall.expression().annotation().type); + // We do not want to visit the TypeTypes in the second argument of `abi.decode`. + // Those types are checked/used in SymbolicState::buildABIFunctions. + if (funType.kind() == FunctionType::Kind::ABIDecode) + { + if (auto arg = _funCall.arguments().front()) + arg->accept(*this); + return false; + } + // We do not really need to visit the expression in a wrap/unwrap no-op call, + // so we just ignore the function call expression to avoid "unsupported" warnings. + else if ( + funType.kind() == FunctionType::Kind::Wrap || + funType.kind() == FunctionType::Kind::Unwrap + ) + { + if (auto arg = _funCall.arguments().front()) + arg->accept(*this); + return false; + } + return true; +} + void SMTEncoder::endVisit(FunctionCall const& _funCall) { auto functionCallKind = *_funCall.annotation().kind; @@ -623,6 +655,10 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) case FunctionType::Kind::MulMod: visitAddMulMod(_funCall); break; + case FunctionType::Kind::Unwrap: + case FunctionType::Kind::Wrap: + visitWrapUnwrap(_funCall); + break; case FunctionType::Kind::Send: case FunctionType::Kind::Transfer: { @@ -828,6 +864,13 @@ void SMTEncoder::visitAddMulMod(FunctionCall const& _funCall) defineExpr(_funCall, divModWithSlacks(x * y, k, intType).second); } +void SMTEncoder::visitWrapUnwrap(FunctionCall const& _funCall) +{ + auto const& args = _funCall.arguments(); + solAssert(args.size() == 1, ""); + defineExpr(_funCall, expr(*args.front())); +} + void SMTEncoder::visitObjectCreation(FunctionCall const& _funCall) { auto const& args = _funCall.arguments(); @@ -852,21 +895,7 @@ void SMTEncoder::endVisit(Identifier const& _identifier) if (auto decl = identifierToVariable(_identifier)) { if (decl->isConstant()) - { - if (RationalNumberType const* rationalType = isConstant(_identifier)) - { - if (rationalType->isNegative()) - defineExpr(_identifier, smtutil::Expression(u2s(rationalType->literalValue(nullptr)))); - else - defineExpr(_identifier, smtutil::Expression(rationalType->literalValue(nullptr))); - } - else - { - solAssert(decl->value(), ""); - decl->value()->accept(*this); - defineExpr(_identifier, expr(*decl->value(), _identifier.annotation().type)); - } - } + defineExpr(_identifier, constantExpr(_identifier, *decl)); else defineExpr(_identifier, currentValue(*decl)); } @@ -882,6 +911,12 @@ void SMTEncoder::endVisit(Identifier const& _identifier) // Ignore type identifiers else if (dynamic_cast(_identifier.annotation().type)) return; + // Ignore module identifiers + else if (dynamic_cast(_identifier.annotation().type)) + return; + // Ignore user defined value 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) @@ -937,7 +972,7 @@ void SMTEncoder::visitPublicGetter(FunctionCall const& _funCall) auto var = dynamic_cast(access.annotation().referencedDeclaration); solAssert(var, ""); solAssert(m_context.knownExpression(_funCall), ""); - auto paramExpectedTypes = FunctionType(*var).parameterTypes(); + auto paramExpectedTypes = replaceUserTypes(FunctionType(*var).parameterTypes()); auto actualArguments = _funCall.arguments(); solAssert(actualArguments.size() == paramExpectedTypes.size(), ""); deque symbArguments; @@ -1157,7 +1192,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier) { auto const& fType = dynamic_cast(*_identifier.annotation().type); - if (fType.returnParameterTypes().size() == 1) + if (replaceUserTypes(fType.returnParameterTypes()).size() == 1) { defineGlobalVariable(fType.identifier(), _identifier); m_context.createExpression(_identifier, m_context.globalSymbol(fType.identifier())); @@ -1269,53 +1304,67 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) createExpr(_memberAccess); - auto const& exprType = _memberAccess.expression().annotation().type; + Expression const* memberExpr = innermostTuple(_memberAccess.expression()); + + auto const& exprType = memberExpr->annotation().type; solAssert(exprType, ""); if (exprType->category() == Type::Category::Magic) { - if (auto const* identifier = dynamic_cast(&_memberAccess.expression())) + if (auto const* identifier = dynamic_cast(memberExpr)) { auto const& name = identifier->name(); solAssert(name == "block" || name == "msg" || name == "tx", ""); defineExpr(_memberAccess, state().txMember(name + "." + _memberAccess.memberName())); } - else if (auto magicType = dynamic_cast(exprType); magicType->kind() == MagicType::Kind::MetaType) + else if (auto magicType = dynamic_cast(exprType)) { - auto const& memberName = _memberAccess.memberName(); - if (memberName == "min" || memberName == "max") + if (magicType->kind() == MagicType::Kind::Block) + defineExpr(_memberAccess, state().txMember("block." + _memberAccess.memberName())); + else if (magicType->kind() == MagicType::Kind::Message) + defineExpr(_memberAccess, state().txMember("msg." + _memberAccess.memberName())); + else if (magicType->kind() == MagicType::Kind::Transaction) + defineExpr(_memberAccess, state().txMember("tx." + _memberAccess.memberName())); + else if (magicType->kind() == MagicType::Kind::MetaType) { - IntegerType const& integerType = dynamic_cast(*magicType->typeArgument()); - defineExpr(_memberAccess, memberName == "min" ? integerType.minValue() : integerType.maxValue()); + auto const& memberName = _memberAccess.memberName(); + if (memberName == "min" || memberName == "max") + { + if (IntegerType const* integerType = dynamic_cast(magicType->typeArgument())) + defineExpr(_memberAccess, memberName == "min" ? integerType->minValue() : integerType->maxValue()); + else if (EnumType const* enumType = dynamic_cast(magicType->typeArgument())) + defineExpr(_memberAccess, memberName == "min" ? enumType->minValue() : enumType->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 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 - solUnimplementedAssert(false, ""); + solAssert(false, ""); return false; } else if (smt::isNonRecursiveStruct(*exprType)) { - _memberAccess.expression().accept(*this); - auto const& symbStruct = dynamic_pointer_cast(m_context.expression(_memberAccess.expression())); + memberExpr->accept(*this); + auto const& symbStruct = dynamic_pointer_cast(m_context.expression(*memberExpr)); defineExpr(_memberAccess, symbStruct->member(_memberAccess.memberName())); return false; } else if (exprType->category() == Type::Category::TypeType) { - auto const* decl = expressionToDeclaration(_memberAccess.expression()); + auto const* decl = expressionToDeclaration(*memberExpr); if (dynamic_cast(decl)) { auto enumType = dynamic_cast(accessType); @@ -1335,10 +1384,10 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) } else if (exprType->category() == Type::Category::Address) { - _memberAccess.expression().accept(*this); + memberExpr->accept(*this); if (_memberAccess.memberName() == "balance") { - defineExpr(_memberAccess, state().balance(expr(_memberAccess.expression()))); + defineExpr(_memberAccess, state().balance(expr(*memberExpr))); setSymbolicUnknownValue(*m_context.expression(_memberAccess), m_context); m_uninterpretedTerms.insert(&_memberAccess); return false; @@ -1346,10 +1395,10 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) } else if (exprType->category() == Type::Category::Array) { - _memberAccess.expression().accept(*this); + memberExpr->accept(*this); if (_memberAccess.memberName() == "length") { - auto symbArray = dynamic_pointer_cast(m_context.expression(_memberAccess.expression())); + auto symbArray = dynamic_pointer_cast(m_context.expression(*memberExpr)); solAssert(symbArray, ""); defineExpr(_memberAccess, symbArray->length()); m_uninterpretedTerms.insert(&_memberAccess); @@ -1371,12 +1420,21 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) defineExpr(_memberAccess, functionType->externalIdentifier()); return false; } - else - m_errorReporter.warning( - 7650_error, - _memberAccess.location(), - "Assertion checker does not yet support this expression." - ); + else if (exprType->category() == Type::Category::Module) + { + if (auto const* var = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + solAssert(var->isConstant(), ""); + defineExpr(_memberAccess, constantExpr(_memberAccess, *var)); + return false; + } + } + + m_errorReporter.warning( + 7650_error, + _memberAccess.location(), + "Assertion checker does not yet support this expression." + ); return true; } @@ -1627,7 +1685,7 @@ 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)) + if (isSupportedType(*_expr.annotation().type)) defineExpr(_expr, m_context.globalSymbol(_name)->currentValue()); } @@ -1823,9 +1881,10 @@ smtutil::Expression SMTEncoder::bitwiseOperation( void SMTEncoder::compareOperation(BinaryOperation const& _op) { - auto const& commonType = _op.annotation().commonType; + auto commonType = _op.annotation().commonType; solAssert(commonType, ""); - if (smt::isSupportedType(*commonType)) + + if (isSupportedType(*commonType)) { smtutil::Expression left(expr(_op.leftExpression(), commonType)); smtutil::Expression right(expr(_op.rightExpression(), commonType)); @@ -1958,7 +2017,7 @@ void SMTEncoder::assignment( Expression const* left = cleanExpression(_left); - if (!smt::isSupportedType(*_type)) + if (!isSupportedType(*_type)) { // Give it a new index anyway to keep the SSA scheme sound. if (auto varDecl = identifierToVariable(*left)) @@ -1999,7 +2058,7 @@ void SMTEncoder::assignment( } else if (funType->kind() == FunctionType::Kind::Internal) { - for (auto type: funType->returnParameterTypes()) + for (auto type: replaceUserTypes(funType->returnParameterTypes())) if (type->category() == Type::Category::Mapping || dynamic_cast(type)) resetReferences(type); } @@ -2336,6 +2395,11 @@ bool SMTEncoder::sameTypeOrSubtype(Type const* _a, Type const* _b) return false; } +bool SMTEncoder::isSupportedType(Type const& _type) const +{ + return smt::isSupportedType(*underlyingType(&_type)); +} + Type const* SMTEncoder::typeWithoutPointer(Type const* _type) { if (auto refType = dynamic_cast(_type)) @@ -2397,7 +2461,7 @@ smtutil::Expression SMTEncoder::expr(Expression const& _e, Type const* _targetTy createExpr(_e); } - return m_context.expression(_e)->currentValue(_targetType); + return m_context.expression(_e)->currentValue(underlyingType(_targetType)); } void SMTEncoder::createExpr(Expression const& _e) @@ -2588,6 +2652,32 @@ Expression const* SMTEncoder::innermostTuple(Expression const& _expr) return expr; } +Type const* SMTEncoder::underlyingType(Type const* _type) +{ + if (auto userType = dynamic_cast(_type)) + _type = &userType->underlyingType(); + return _type; +} + +TypePointers SMTEncoder::replaceUserTypes(TypePointers const& _types) +{ + return applyMap(_types, [](auto _type) { + if (auto userType = dynamic_cast(_type)) + return &userType->underlyingType(); + return _type; + }); +} + +pair SMTEncoder::functionCallExpression(FunctionCall const& _funCall) +{ + Expression const* callExpr = &_funCall.expression(); + auto const* callOptions = dynamic_cast(callExpr); + if (callOptions) + callExpr = &callOptions->expression(); + + return {callExpr, callOptions}; +} + Expression const* SMTEncoder::cleanExpression(Expression const& _expr) { auto const* expr = &_expr; @@ -2711,7 +2801,8 @@ FunctionDefinition const* SMTEncoder::functionCallToDefinition( if (*_funCall.annotation().kind != FunctionCallKind::FunctionCall) return {}; - Expression const* calledExpr = &_funCall.expression(); + auto [calledExpr, callOptions] = functionCallExpression(_funCall); + if (TupleExpression const* fun = dynamic_cast(calledExpr)) { solAssert(fun->components().size() == 1, ""); @@ -3019,6 +3110,24 @@ vector SMTEncoder::symbolicArguments(FunctionCall const& _f return args; } +smtutil::Expression SMTEncoder::constantExpr(Expression const& _expr, VariableDeclaration const& _var) +{ + if (RationalNumberType const* rationalType = isConstant(_expr)) + { + if (rationalType->isNegative()) + return smtutil::Expression(u2s(rationalType->literalValue(nullptr))); + else + return smtutil::Expression(rationalType->literalValue(nullptr)); + } + else + { + solAssert(_var.value(), ""); + _var.value()->accept(*this); + return expr(*_var.value(), _expr.annotation().type); + } + solAssert(false, ""); +} + void SMTEncoder::collectFreeFunctions(set const& _sources) { for (auto source: _sources) diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 7cf4b065e..7ca19358d 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -71,6 +71,16 @@ public: /// otherwise _expr. static Expression const* innermostTuple(Expression const& _expr); + /// @returns the underlying type if _type is UserDefinedValueType, + /// and _type otherwise. + static Type const* underlyingType(Type const* _type); + + static TypePointers replaceUserTypes(TypePointers const& _types); + + /// @returns {_funCall.expression(), nullptr} if function call option values are not given, and + /// {_funCall.expression().expression(), _funCall.expression()} if they are. + static std::pair functionCallExpression(FunctionCall const& _funCall); + /// @returns the expression after stripping redundant syntactic sugar. /// Currently supports stripping: /// 1. 1-tuple; i.e. ((x)) -> x @@ -148,6 +158,7 @@ protected: bool visit(BinaryOperation const& _node) override; void endVisit(BinaryOperation const& _node) override; bool visit(Conditional const& _node) override; + bool visit(FunctionCall const& _node) override; void endVisit(FunctionCall const& _node) override; bool visit(ModifierInvocation const& _node) override; void endVisit(Identifier const& _node) override; @@ -202,6 +213,7 @@ protected: void visitCryptoFunction(FunctionCall const& _funCall); void visitGasLeft(FunctionCall const& _funCall); virtual void visitAddMulMod(FunctionCall const& _funCall); + void visitWrapUnwrap(FunctionCall const& _funCall); void visitObjectCreation(FunctionCall const& _funCall); void visitTypeConversion(FunctionCall const& _funCall); void visitStructConstructorCall(FunctionCall const& _funCall); @@ -314,6 +326,8 @@ protected: /// @returns whether _a or a subtype of _a is the same as _b. bool sameTypeOrSubtype(Type const* _a, Type const* _b); + bool isSupportedType(Type const& _type) const; + /// Given the state of the symbolic variables at the end of two different branches, /// create a merged state using the given branch condition. void mergeVariables(smtutil::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse); @@ -388,6 +402,8 @@ protected: /// type conversion. std::vector symbolicArguments(FunctionCall const& _funCall, ContractDefinition const* _contextContract); + smtutil::Expression constantExpr(Expression const& _expr, VariableDeclaration const& _var); + /// Traverses all source units available collecting free functions /// and internal library functions in m_freeFunctions. void collectFreeFunctions(std::set const& _sources); diff --git a/libsolidity/formal/SymbolicState.cpp b/libsolidity/formal/SymbolicState.cpp index 57ff725ba..e136f347d 100644 --- a/libsolidity/formal/SymbolicState.cpp +++ b/libsolidity/formal/SymbolicState.cpp @@ -270,6 +270,8 @@ void SymbolicState::buildABIFunctions(set const& _abiFuncti t = TypeProvider::uint256(); else if (t->category() == frontend::Type::Category::StringLiteral) t = TypeProvider::bytesMemory(); + else if (auto userType = dynamic_cast(t)) + t = &userType->underlyingType(); }; replaceTypes(inTypes); replaceTypes(outTypes); diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index ba82a1666..36d9a0f35 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -233,6 +233,10 @@ pair> newSymbolicVariable( bool abstract = false; shared_ptr var; frontend::Type const* type = &_type; + + if (auto userType = dynamic_cast(type)) + return newSymbolicVariable(userType->underlyingType(), _uniqueName, _context); + if (!isSupportedTypeDeclaration(_type)) { abstract = true; diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index c4a2dfe5c..cf395ffa0 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -56,7 +56,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) FunctionType const* externalFunctionType = it.second->interfaceFunctionType(); solAssert(!!externalFunctionType, ""); - Json::Value method; + Json::Value method{Json::objectValue}; method["type"] = "function"; method["name"] = it.second->declaration().name(); method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); @@ -80,7 +80,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) FunctionType constrType(*constructor); FunctionType const* externalFunctionType = constrType.interfaceFunctionType(); solAssert(!!externalFunctionType, ""); - Json::Value method; + Json::Value method{Json::objectValue}; method["type"] = "constructor"; method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); method["inputs"] = formatTypeList( @@ -96,23 +96,22 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) { auto const* externalFunctionType = FunctionType(*fallbackOrReceive).interfaceFunctionType(); solAssert(!!externalFunctionType, ""); - Json::Value method; + Json::Value method{Json::objectValue}; method["type"] = TokenTraits::toString(fallbackOrReceive->kind()); method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability()); abi.emplace(std::move(method)); } for (auto const& it: _contractDef.interfaceEvents()) { - Json::Value event; + Json::Value event{Json::objectValue}; event["type"] = "event"; event["name"] = it->name(); event["anonymous"] = it->isAnonymous(); - Json::Value params(Json::arrayValue); + Json::Value params{Json::arrayValue}; for (auto const& p: it->parameters()) { Type const* type = p->annotation().type->interfaceType(false); solAssert(type, ""); - Json::Value input; auto param = formatType(p->name(), *type, *p->annotation().type, false); param["indexed"] = p->isIndexed(); params.append(std::move(param)); @@ -123,7 +122,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) for (ErrorDefinition const* error: _contractDef.interfaceErrors()) { - Json::Value errorJson; + Json::Value errorJson{Json::objectValue}; errorJson["type"] = "error"; errorJson["name"] = error->name(); errorJson["inputs"] = Json::arrayValue; @@ -151,7 +150,7 @@ Json::Value ABI::formatTypeList( bool _forLibrary ) { - Json::Value params(Json::arrayValue); + Json::Value params{Json::arrayValue}; solAssert(_names.size() == _encodingTypes.size(), "Names and types vector size does not match"); solAssert(_names.size() == _solidityTypes.size(), ""); for (unsigned i = 0; i < _names.size(); ++i) @@ -169,7 +168,7 @@ Json::Value ABI::formatType( bool _forLibrary ) { - Json::Value ret; + Json::Value ret{Json::objectValue}; ret["name"] = _name; ret["internalType"] = _solidityType.toString(true); string suffix = (_forLibrary && _encodingType.dataStoredIn(DataLocation::Storage)) ? " storage" : ""; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index cbdfff454..e49c15387 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -78,10 +78,11 @@ #include +#include + #include #include - -#include +#include using namespace std; using namespace solidity; @@ -252,7 +253,7 @@ void CompilerStack::setRevertStringBehaviour(RevertStrings _revertStrings) { if (m_stackState >= ParsedAndImported) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set revert string settings before parsing.")); - solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug, ""); + solUnimplementedAssert(_revertStrings != RevertStrings::VerboseDebug); m_revertStrings = _revertStrings; } @@ -338,7 +339,7 @@ bool CompilerStack::parse() Source& source = m_sources[path]; source.ast = parser.parse(*source.charStream); if (!source.ast) - solAssert(!Error::containsOnlyWarnings(m_errorReporter.errors()), "Parser returned null but did not report error."); + solAssert(Error::containsErrors(m_errorReporter.errors()), "Parser returned null but did not report error."); else { source.ast->annotation().path = path; @@ -357,7 +358,7 @@ bool CompilerStack::parse() m_stackState = Parsed; else m_stackState = ParsedAndImported; - if (!Error::containsOnlyWarnings(m_errorReporter.errors())) + if (Error::containsErrors(m_errorReporter.errors())) m_hasError = true; storeContractDefinitions(); @@ -741,9 +742,10 @@ Json::Value CompilerStack::generatedSources(string const& _contractName, bool _r return sources.init([&]{ Json::Value sources{Json::arrayValue}; // If there is no compiler, then no bytecode was generated and thus no - // sources were generated. + // sources were generated (or we compiled "via IR"). if (c.compiler) { + solAssert(!m_viaIR, ""); string source = _runtime ? c.compiler->runtimeGeneratedYulUtilityCode() : @@ -1000,20 +1002,12 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const return methodIdentifiers; } -string const& CompilerStack::metadata(string const& _contractName) const +bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) const { if (m_stackState < AnalysisPerformed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); - 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)); + return createCBORMetadata(contract(_contractName), _forIR); } string const& CompilerStack::metadata(Contract const& _contract) const @@ -1023,7 +1017,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const solAssert(_contract.contract, ""); - return _contract.metadata.init([&]{ return createMetadata(_contract); }); + return _contract.metadata.init([&]{ return createMetadata(_contract, m_viaIR); }); } CharStream const& CompilerStack::charStream(string const& _sourceName) const @@ -1062,16 +1056,10 @@ size_t CompilerStack::functionEntryPoint( if (m_stackState != CompilationSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Compilation was not successful.")); - shared_ptr const& compiler = contract(_contractName).compiler; - if (!compiler) - return 0; - evmasm::AssemblyItem tag = compiler->functionEntryLabel(_function); - if (tag.type() == evmasm::UndefinedItem) - return 0; - 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; + for (auto&& [name, data]: contract(_contractName).runtimeObject.functionDebugData) + if (data.sourceID == _function.id()) + if (data.instructionIndex) + return *data.instructionIndex; return 0; } @@ -1265,6 +1253,7 @@ void CompilerStack::compileContract( map>& _otherCompilers ) { + solAssert(!m_viaIR, ""); solAssert(m_stackState >= AnalysisPerformed, ""); if (m_hasError) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Called compile with errors.")); @@ -1283,7 +1272,8 @@ void CompilerStack::compileContract( shared_ptr compiler = make_shared(m_evmVersion, m_revertStrings, m_optimiserSettings); compiledContract.compiler = compiler; - bytes cborEncodedMetadata = createCBORMetadata(compiledContract); + solAssert(!m_viaIR, ""); + bytes cborEncodedMetadata = createCBORMetadata(compiledContract, /* _forIR */ false); try { @@ -1329,10 +1319,10 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) for (auto const& pair: m_contracts) otherYulSources.emplace(pair.second.contract, pair.second.yulIR); - IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings, sourceIndices()); + IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings, sourceIndices(), this); tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run( _contract, - createCBORMetadata(compiledContract), + createCBORMetadata(compiledContract, /* _forIR */ true), otherYulSources ); } @@ -1434,9 +1424,9 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } -string CompilerStack::createMetadata(Contract const& _contract) const +string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) const { - Json::Value meta; + Json::Value meta{Json::objectValue}; meta["version"] = 1; meta["language"] = m_importedSources ? "SolidityAST" : "Solidity"; meta["compiler"]["version"] = VersionStringStrict; @@ -1510,8 +1500,8 @@ 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; + if (_forIR) + meta["settings"]["viaIR"] = _forIR; meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = *_contract.contract->annotation().canonicalName; @@ -1569,7 +1559,7 @@ public: // The already encoded key-value pairs ret += m_data; // 16-bit big endian length - ret += util::toCompactBigEndian(size, 2); + ret += toCompactBigEndian(size, 2); return ret; } @@ -1617,7 +1607,7 @@ private: bytes m_data; }; -bytes CompilerStack::createCBORMetadata(Contract const& _contract) const +bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR) const { if (m_metadataFormat == MetadataFormat::NoMetadata) return bytes{}; @@ -1626,7 +1616,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract) const _contract.contract->sourceUnit().annotation().experimentalFeatures ); - string meta = metadata(_contract); + string meta = (_forIR == m_viaIR ? metadata(_contract) : createMetadata(_contract, _forIR)); MetadataCBOREncoder encoder; @@ -1637,7 +1627,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract) const else solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); - if (experimentalMode || m_viaIR) + if (experimentalMode || _forIR) encoder.pushBool("experimental", true); if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag) encoder.pushBytes("solc", VersionCompactBytes); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 9c713da24..bbef4dc64 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -326,11 +326,16 @@ public: /// @returns a JSON representing a map of method identifiers (hashes) to function names. Json::Value methodIdentifiers(std::string const& _contractName) const; - /// @returns the Contract Metadata - std::string const& metadata(std::string const& _contractName) const; + /// @returns the Contract Metadata matching the pipeline selected using the viaIR setting. + std::string const& metadata(std::string const& _contractName) const { return metadata(contract(_contractName)); } - /// @returns the cbor-encoded metadata. - bytes cborMetadata(std::string const& _contractName) const; + /// @returns the CBOR-encoded metadata matching the pipeline selected using the viaIR setting. + bytes cborMetadata(std::string const& _contractName) const { return cborMetadata(_contractName, m_viaIR); } + + /// @returns the CBOR-encoded metadata. + /// @param _forIR If true, the metadata for the IR codegen is used. Otherwise it's the metadata + /// for the EVM codegen + bytes cborMetadata(std::string const& _contractName, bool _forIR) 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 +344,7 @@ public: /// This is mostly a workaround to avoid bytecode and gas differences between compiler builds /// caused by differences in metadata. Should only be used for testing. void setMetadataFormat(MetadataFormat _metadataFormat) { m_metadataFormat = _metadataFormat; } + private: /// The state per source unit. Filled gradually during parsing. struct Source @@ -437,11 +443,14 @@ private: /// Can only be called after state is SourcesSet. Source const& source(std::string const& _sourceName) const; + /// @param _forIR If true, include a flag that indicates that the bytecode comes from the + /// experimental IR codegen. /// @returns the metadata JSON as a compact string for the given contract. - std::string createMetadata(Contract const& _contract) const; + std::string createMetadata(Contract const& _contract, bool _forIR) const; /// @returns the metadata CBOR for the given serialised metadata JSON. - bytes createCBORMetadata(Contract const& _contract) const; + /// @param _forIR If true, use the metadata for the IR codegen. Otherwise the one for EVM codegen. + bytes createCBORMetadata(Contract const& _contract, bool _forIR) 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. @@ -459,9 +468,9 @@ private: /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& natspecDev(Contract const&) const; - /// @returns the Contract Metadata + /// @returns the Contract Metadata matching the pipeline selected using the viaIR setting. /// This will generate the metadata and store it in the Contract object if it is not present yet. - std::string const& metadata(Contract const&) const; + std::string const& metadata(Contract const& _contract) const; /// @returns the offset of the entry point of the given function into the list of assembly items /// or zero if it is not found or does not exist. diff --git a/libsolidity/interface/FileReader.cpp b/libsolidity/interface/FileReader.cpp index f00102e9e..2a1859d21 100644 --- a/libsolidity/interface/FileReader.cpp +++ b/libsolidity/interface/FileReader.cpp @@ -21,29 +21,72 @@ #include #include +#include #include +#include + +#include + +#include using solidity::frontend::ReadCallback; using solidity::langutil::InternalCompilerError; using solidity::util::errinfo_comment; using solidity::util::readFileAsString; +using solidity::util::joinHumanReadable; +using std::map; +using std::reference_wrapper; using std::string; +using std::vector; namespace solidity::frontend { +FileReader::FileReader( + boost::filesystem::path _basePath, + vector const& _includePaths, + FileSystemPathSet _allowedDirectories +): + m_allowedDirectories(std::move(_allowedDirectories)), + m_sourceCodes() +{ + setBasePath(_basePath); + for (boost::filesystem::path const& includePath: _includePaths) + addIncludePath(includePath); + + for (boost::filesystem::path const& allowedDir: m_allowedDirectories) + solAssert(!allowedDir.empty(), ""); +} + void FileReader::setBasePath(boost::filesystem::path const& _path) { - m_basePath = (_path.empty() ? "" : normalizeCLIPathForVFS(_path)); + if (_path.empty()) + { + // Empty base path is a special case that does not make sense when include paths are used. + solAssert(m_includePaths.empty(), ""); + m_basePath = ""; + } + else + m_basePath = normalizeCLIPathForVFS(_path); +} + +void FileReader::addIncludePath(boost::filesystem::path const& _path) +{ + solAssert(!m_basePath.empty(), ""); + solAssert(!_path.empty(), ""); + m_includePaths.push_back(normalizeCLIPathForVFS(_path)); +} + +void FileReader::allowDirectory(boost::filesystem::path _path) +{ + solAssert(!_path.empty(), ""); + m_allowedDirectories.insert(std::move(_path)); } void FileReader::setSource(boost::filesystem::path const& _path, SourceCode _source) { - boost::filesystem::path normalizedPath = normalizeCLIPathForVFS(_path); - boost::filesystem::path prefix = (m_basePath.empty() ? normalizeCLIPathForVFS(".") : m_basePath); - - m_sourceCodes[stripPrefixIfPresent(prefix, normalizedPath).generic_string()] = std::move(_source); + m_sourceCodes[cliPathToSourceUnitName(_path)] = std::move(_source); } void FileReader::setStdin(SourceCode _source) @@ -69,31 +112,52 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so if (strippedSourceUnitName.find("file://") == 0) strippedSourceUnitName.erase(0, 7); - auto canonicalPath = boost::filesystem::weakly_canonical(m_basePath / strippedSourceUnitName); - bool isAllowed = false; - for (auto const& allowedDir: m_allowedDirectories) + vector candidates; + vector> prefixes = {m_basePath}; + prefixes += (m_includePaths | ranges::to>>); + + for (auto const& prefix: prefixes) { - // If dir is a prefix of boostPath, we are fine. - if ( - std::distance(allowedDir.begin(), allowedDir.end()) <= std::distance(canonicalPath.begin(), canonicalPath.end()) && - std::equal(allowedDir.begin(), allowedDir.end(), canonicalPath.begin()) - ) + boost::filesystem::path canonicalPath = normalizeCLIPathForVFS(prefix / strippedSourceUnitName, SymlinkResolution::Enabled); + + if (boost::filesystem::exists(canonicalPath)) + candidates.push_back(std::move(canonicalPath)); + } + + auto pathToQuotedString = [](boost::filesystem::path const& _path){ return "\"" + _path.string() + "\""; }; + + if (candidates.empty()) + return ReadCallback::Result{false, "File not found."}; + + if (candidates.size() >= 2) + return ReadCallback::Result{ + false, + "Ambiguous import. " + "Multiple matching files found inside base path and/or include paths: " + + joinHumanReadable(candidates | ranges::views::transform(pathToQuotedString), ", ") + + "." + }; + + FileSystemPathSet extraAllowedPaths = {m_basePath.empty() ? "." : m_basePath}; + extraAllowedPaths += m_includePaths; + + bool isAllowed = false; + for (boost::filesystem::path const& allowedDir: m_allowedDirectories + extraAllowedPaths) + if (isPathPrefix(normalizeCLIPathForVFS(allowedDir, SymlinkResolution::Enabled), candidates[0])) { isAllowed = true; break; } - } + if (!isAllowed) return ReadCallback::Result{false, "File outside of allowed directories."}; - if (!boost::filesystem::exists(canonicalPath)) - return ReadCallback::Result{false, "File not found."}; - - if (!boost::filesystem::is_regular_file(canonicalPath)) + if (!boost::filesystem::is_regular_file(candidates[0])) return ReadCallback::Result{false, "Not a valid file."}; // NOTE: we ignore the FileNotFound exception as we manually check above - auto contents = readFileAsString(canonicalPath); + auto contents = readFileAsString(candidates[0]); + solAssert(m_sourceCodes.count(_sourceUnitName) == 0, ""); m_sourceCodes[_sourceUnitName] = contents; return ReadCallback::Result{true, contents}; } @@ -101,13 +165,55 @@ ReadCallback::Result FileReader::readFile(string const& _kind, string const& _so { return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; } + catch (std::exception const& _exception) + { + return ReadCallback::Result{false, "Exception in read callback: " + boost::diagnostic_information(_exception)}; + } catch (...) { return ReadCallback::Result{false, "Unknown exception in read callback."}; } } -boost::filesystem::path FileReader::normalizeCLIPathForVFS(boost::filesystem::path const& _path) +string FileReader::cliPathToSourceUnitName(boost::filesystem::path const& _cliPath) +{ + vector prefixes = {m_basePath.empty() ? normalizeCLIPathForVFS(".") : m_basePath}; + prefixes += m_includePaths; + + boost::filesystem::path normalizedPath = normalizeCLIPathForVFS(_cliPath); + for (boost::filesystem::path const& prefix: prefixes) + if (isPathPrefix(prefix, normalizedPath)) + { + // Multiple prefixes can potentially match the path. We take the first one. + normalizedPath = stripPrefixIfPresent(prefix, normalizedPath); + break; + } + + return normalizedPath.generic_string(); +} + +map FileReader::detectSourceUnitNameCollisions(FileSystemPathSet const& _cliPaths) +{ + map nameToPaths; + for (boost::filesystem::path const& cliPath: _cliPaths) + { + string sourceUnitName = cliPathToSourceUnitName(cliPath); + boost::filesystem::path normalizedPath = normalizeCLIPathForVFS(cliPath); + nameToPaths[sourceUnitName].insert(normalizedPath); + } + + map collisions; + for (auto&& [sourceUnitName, cliPaths]: nameToPaths) + if (cliPaths.size() >= 2) + collisions[sourceUnitName] = std::move(cliPaths); + + return collisions; +} + +boost::filesystem::path FileReader::normalizeCLIPathForVFS( + boost::filesystem::path const& _path, + SymlinkResolution _symlinkResolution +) { // Detailed normalization rules: // - Makes the path either be absolute or have slash as root (note that on Windows paths with @@ -121,7 +227,8 @@ boost::filesystem::path FileReader::normalizeCLIPathForVFS(boost::filesystem::pa // path to the current working directory. // // Also note that this function: - // - Does NOT resolve symlinks (except for symlinks in the path to the current working directory). + // - Does NOT resolve symlinks (except for symlinks in the path to the current working directory) + // unless explicitly requested. // - Does NOT check if the path refers to a file or a directory. If the path ends with a slash, // the slash is preserved even if it's a file. // - The only exception are paths where the file name is a dot (e.g. '.' or 'a/b/.'). These @@ -135,9 +242,27 @@ boost::filesystem::path FileReader::normalizeCLIPathForVFS(boost::filesystem::pa // Windows it does not. To get consistent results we resolve them on all platforms. boost::filesystem::path absolutePath = boost::filesystem::absolute(_path, canonicalWorkDir); - // NOTE: boost path preserves certain differences that are ignored by its operator ==. - // E.g. "a//b" vs "a/b" or "a/b/" vs "a/b/.". lexically_normal() does remove these differences. - boost::filesystem::path normalizedPath = absolutePath.lexically_normal(); + boost::filesystem::path normalizedPath; + if (_symlinkResolution == SymlinkResolution::Enabled) + { + // NOTE: weakly_canonical() will not convert a relative path into an absolute one if no + // directory included in the path actually exists. + normalizedPath = boost::filesystem::weakly_canonical(absolutePath); + + // The three corner cases in which lexically_normal() includes a trailing slash in the + // normalized path but weakly_canonical() does not. Note that the trailing slash is not + // ignored when comparing paths with ==. + if ((_path == "." || _path == "./" || _path == "../") && !boost::ends_with(normalizedPath.generic_string(), "/")) + normalizedPath = normalizedPath.parent_path() / (normalizedPath.filename().string() + "/"); + } + else + { + solAssert(_symlinkResolution == SymlinkResolution::Disabled, ""); + + // NOTE: boost path preserves certain differences that are ignored by its operator ==. + // E.g. "a//b" vs "a/b" or "a/b/" vs "a/b/.". lexically_normal() does remove these differences. + normalizedPath = absolutePath.lexically_normal(); + } solAssert(normalizedPath.is_absolute() || normalizedPath.root_path() == "/", ""); // If the path is on the same drive as the working dir, for portability we prefer not to diff --git a/libsolidity/interface/FileReader.h b/libsolidity/interface/FileReader.h index bbe24d264..71f5819df 100644 --- a/libsolidity/interface/FileReader.h +++ b/libsolidity/interface/FileReader.h @@ -39,22 +39,26 @@ public: using PathMap = std::map; using FileSystemPathSet = std::set; - /// Constructs a FileReader with a base path and a set of allowed directories that - /// will be used when requesting files from this file reader instance. + enum SymlinkResolution { + Disabled, ///< Do not resolve symbolic links in the path. + Enabled, ///< Follow symbolic links. The path should contain no symlinks. + }; + + /// Constructs a FileReader with a base path and sets of include paths and allowed directories + /// that will be used when requesting files from this file reader instance. explicit FileReader( boost::filesystem::path _basePath = {}, + std::vector const& _includePaths = {}, FileSystemPathSet _allowedDirectories = {} - ): - m_allowedDirectories(std::move(_allowedDirectories)), - m_sourceCodes() - { - setBasePath(_basePath); - } + ); void setBasePath(boost::filesystem::path const& _path); boost::filesystem::path const& basePath() const noexcept { return m_basePath; } - void allowDirectory(boost::filesystem::path _path) { m_allowedDirectories.insert(std::move(_path)); } + void addIncludePath(boost::filesystem::path const& _path); + std::vector const& includePaths() const noexcept { return m_includePaths; } + + void allowDirectory(boost::filesystem::path _path); FileSystemPathSet const& allowedDirectories() const noexcept { return m_allowedDirectories; } StringMap const& sourceCodes() const noexcept { return m_sourceCodes; } @@ -88,13 +92,29 @@ public: return [this](std::string const& _kind, std::string const& _path) { return readFile(_kind, _path); }; } + /// Creates a source unit name by normalizing a path given on the command line and, if possible, + /// making it relative to base path or one of the include directories. + std::string cliPathToSourceUnitName(boost::filesystem::path const& _cliPath); + + /// Checks if a set contains any paths that lead to different files but would receive identical + /// source unit names. Files are considered the same if their paths are exactly the same after + /// normalization (without following symlinks). + /// @returns a map containing all the conflicting source unit names and the paths that would + /// receive them. The returned paths are normalized. + std::map detectSourceUnitNameCollisions(FileSystemPathSet const& _cliPaths); + /// Normalizes a filesystem path to make it include all components up to the filesystem root, /// remove small, inconsequential differences that do not affect the meaning and make it look - /// the same on all platforms (if possible). Symlinks in the path are not resolved. + /// the same on all platforms (if possible). /// The resulting path uses forward slashes as path separators, has no redundant separators, /// has no redundant . or .. segments and has no root name if removing it does not change the meaning. /// The path does not have to actually exist. - static boost::filesystem::path normalizeCLIPathForVFS(boost::filesystem::path const& _path); + /// @param _path Path to normalize. + /// @param _symlinkResolution If @a Disabled, any symlinks present in @a _path are preserved. + static boost::filesystem::path normalizeCLIPathForVFS( + boost::filesystem::path const& _path, + SymlinkResolution _symlinkResolution = SymlinkResolution::Disabled + ); /// @returns true if all the path components of @a _prefix are present at the beginning of @a _path. /// Both paths must be absolute (or have slash as root) and normalized (no . or .. segments, no @@ -128,6 +148,9 @@ private: /// Base path, used for resolving relative paths in imports. boost::filesystem::path m_basePath; + /// Additional directories used for resolving relative paths in imports. + std::vector m_includePaths; + /// list of allowed directories to read files from FileSystemPathSet m_allowedDirectories; diff --git a/libsolidity/interface/ImportRemapper.cpp b/libsolidity/interface/ImportRemapper.cpp index 134101f40..9d5561013 100644 --- a/libsolidity/interface/ImportRemapper.cpp +++ b/libsolidity/interface/ImportRemapper.cpp @@ -20,10 +20,12 @@ #include using std::equal; +using std::find; using std::move; +using std::nullopt; using std::optional; using std::string; -using std::string; +using std::string_view; using std::vector; namespace solidity::frontend @@ -77,24 +79,29 @@ SourceUnitName ImportRemapper::apply(ImportPath const& _path, string const& _con return path; } -optional ImportRemapper::parseRemapping(string const& _remapping) +bool ImportRemapper::isRemapping(string_view _input) { - auto eq = find(_remapping.begin(), _remapping.end(), '='); - if (eq == _remapping.end()) - return {}; + return _input.find("=") != string::npos; +} - auto colon = find(_remapping.begin(), eq, ':'); +optional ImportRemapper::parseRemapping(string_view _input) +{ + auto equals = find(_input.cbegin(), _input.cend(), '='); + if (equals == _input.end()) + return nullopt; - Remapping r; + auto const colon = find(_input.cbegin(), equals, ':'); - r.context = colon == eq ? string() : string(_remapping.begin(), colon); - r.prefix = colon == eq ? string(_remapping.begin(), eq) : string(colon + 1, eq); - r.target = string(eq + 1, _remapping.end()); + Remapping remapping{ + (colon == equals ? "" : string(_input.cbegin(), colon)), + (colon == equals ? string(_input.cbegin(), equals) : string(colon + 1, equals)), + string(equals + 1, _input.cend()), + }; - if (r.prefix.empty()) - return {}; + if (remapping.prefix.empty()) + return nullopt; - return r; + return remapping; } } diff --git a/libsolidity/interface/ImportRemapper.h b/libsolidity/interface/ImportRemapper.h index 4451f0291..66946d091 100644 --- a/libsolidity/interface/ImportRemapper.h +++ b/libsolidity/interface/ImportRemapper.h @@ -56,8 +56,11 @@ public: SourceUnitName apply(ImportPath const& _path, std::string const& _context) const; - // Parses a remapping of the format "context:prefix=target". - static std::optional parseRemapping(std::string const& _remapping); + /// @returns true if the string can be parsed as a remapping + static bool isRemapping(std::string_view _input); + + /// Parses a remapping of the format "context:prefix=target". + static std::optional parseRemapping(std::string_view _input); private: /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index 402d8e15c..533cbaa3d 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -37,7 +37,7 @@ using namespace solidity::frontend; Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) { - Json::Value doc; + Json::Value doc{Json::objectValue}; doc["version"] = Json::Value(c_natspecVersion); doc["kind"] = Json::Value("user"); @@ -50,7 +50,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) if (!value.empty()) { // add the constructor, only if we have any documentation to add - Json::Value user; + Json::Value user{Json::objectValue}; user["notice"] = Json::Value(value); doc["methods"]["constructor"] = user; } @@ -90,7 +90,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) string value = extractDoc(error->annotation().docTags, "notice"); if (!value.empty()) { - Json::Value errorDoc; + Json::Value errorDoc{Json::objectValue}; errorDoc["notice"] = value; doc["errors"][error->functionType(true)->externalSignature()].append(move(errorDoc)); } @@ -225,7 +225,10 @@ Json::Value Natspec::extractCustomDoc(multimap const& _tags) for (auto const& [tag, value]: _tags) if (boost::starts_with(tag, "custom")) concatenated[tag] += value.content; - Json::Value result; + // We do not want to create an object if there are no custom tags found. + if (concatenated.empty()) + return Json::nullValue; + Json::Value result{Json::objectValue}; for (auto& [tag, value]: concatenated) result[tag] = move(value); return result; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 4071a1fbe..7d354a405 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -50,7 +50,7 @@ namespace { Json::Value formatError( - bool _warning, + Error::Severity _severity, string const& _type, string const& _component, string const& _message, @@ -59,10 +59,10 @@ Json::Value formatError( Json::Value const& _secondarySourceLocation = Json::Value() ) { - Json::Value error = Json::objectValue; + Json::Value error{Json::objectValue}; error["type"] = _type; error["component"] = _component; - error["severity"] = _warning ? "warning" : "error"; + error["severity"] = Error::formatErrorSeverityLowercase(_severity); error["message"] = _message; error["formattedMessage"] = (_formattedMessage.length() > 0) ? _formattedMessage : _message; if (_sourceLocation.isObject()) @@ -74,31 +74,30 @@ Json::Value formatError( Json::Value formatFatalError(string const& _type, string const& _message) { - Json::Value output = Json::objectValue; + Json::Value output{Json::objectValue}; output["errors"] = Json::arrayValue; - output["errors"].append(formatError(false, _type, "general", _message)); + output["errors"].append(formatError(Error::Severity::Error, _type, "general", _message)); return output; } Json::Value formatSourceLocation(SourceLocation const* location) { - Json::Value sourceLocation; - if (location && location->sourceName) - { - sourceLocation["file"] = *location->sourceName; - sourceLocation["start"] = location->start; - sourceLocation["end"] = location->end; - } + if (!location || !location->sourceName) + return Json::nullValue; + Json::Value sourceLocation{Json::objectValue}; + sourceLocation["file"] = *location->sourceName; + sourceLocation["start"] = location->start; + sourceLocation["end"] = location->end; return sourceLocation; } Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _secondaryLocation) { if (!_secondaryLocation) - return {}; + return Json::nullValue; - Json::Value secondarySourceLocation = Json::arrayValue; + Json::Value secondarySourceLocation{Json::arrayValue}; for (auto const& location: _secondaryLocation->infos) { Json::Value msg = formatSourceLocation(&location.second); @@ -111,7 +110,7 @@ Json::Value formatSecondarySourceLocation(SecondarySourceLocation const* _second Json::Value formatErrorWithException( CharStreamProvider const& _charStreamProvider, util::Exception const& _exception, - bool const& _warning, + Error::Severity _severity, string const& _type, string const& _component, string const& _message, @@ -132,7 +131,7 @@ Json::Value formatErrorWithException( message = _message; Json::Value error = formatError( - _warning, + _severity, _type, _component, message, @@ -330,7 +329,7 @@ bool isIRRequested(Json::Value const& _outputSelection) Json::Value formatLinkReferences(std::map const& linkReferences) { - Json::Value ret(Json::objectValue); + Json::Value ret{Json::objectValue}; for (auto const& ref: linkReferences) { @@ -345,7 +344,7 @@ Json::Value formatLinkReferences(std::map const& linkRefere Json::Value fileObject = ret.get(file, Json::objectValue); Json::Value libraryArray = fileObject.get(name, Json::arrayValue); - Json::Value entry = Json::objectValue; + Json::Value entry{Json::objectValue}; entry["start"] = Json::UInt(ref.first); entry["length"] = 20; @@ -359,7 +358,7 @@ Json::Value formatLinkReferences(std::map const& linkRefere Json::Value formatImmutableReferences(map>> const& _immutableReferences) { - Json::Value ret(Json::objectValue); + Json::Value ret{Json::objectValue}; for (auto const& immutableReference: _immutableReferences) { @@ -367,7 +366,7 @@ Json::Value formatImmutableReferences(map>> co Json::Value array(Json::arrayValue); for (size_t byteOffset: byteOffsets) { - Json::Value byteRange(Json::objectValue); + Json::Value byteRange{Json::objectValue}; byteRange["start"] = Json::UInt(byteOffset); byteRange["length"] = Json::UInt(32); // immutable references are currently always 32 bytes wide array.append(byteRange); @@ -386,7 +385,7 @@ Json::Value collectEVMObject( function const& _artifactRequested ) { - Json::Value output = Json::objectValue; + Json::Value output{Json::objectValue}; if (_artifactRequested("object")) output["object"] = _object.toHex(); if (_artifactRequested("opcodes")) @@ -660,7 +659,7 @@ std::variant StandardCompiler: string content = sources[sourceName]["content"].asString(); if (!hash.empty() && !hashMatchesContent(hash, content)) ret.errors.append(formatError( - false, + Error::Severity::Error, "IOError", "general", "Mismatch between content and supplied hash for \"" + sourceName + "\"" @@ -685,7 +684,7 @@ std::variant StandardCompiler: { if (!hash.empty() && !hashMatchesContent(hash, result.responseOrErrorMessage)) ret.errors.append(formatError( - false, + Error::Severity::Error, "IOError", "general", "Mismatch between content and supplied hash for \"" + sourceName + "\" at \"" + url.asString() + "\"" @@ -705,7 +704,7 @@ std::variant StandardCompiler: { /// If the import succeeded, let mark all the others as warnings, otherwise all of them are errors. ret.errors.append(formatError( - found ? true : false, + found ? Error::Severity::Warning : Error::Severity::Error, "IOError", "general", failure @@ -1058,7 +1057,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting errors.append(formatErrorWithException( compilerStack, *error, - err.type() == Error::Type::Warning, + Error::errorSeverity(err.type()), err.typeName(), "general", "", @@ -1072,7 +1071,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting errors.append(formatErrorWithException( compilerStack, _error, - false, + Error::Severity::Error, _error.typeName(), "general", "Uncaught error: " @@ -1082,7 +1081,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (FatalError const& _exception) { errors.append(formatError( - false, + Error::Severity::Error, "FatalError", "general", "Uncaught fatal error: " + boost::diagnostic_information(_exception) @@ -1093,7 +1092,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting errors.append(formatErrorWithException( compilerStack, _exception, - false, + Error::Severity::Error, "CompilerError", "general", "Compiler error (" + _exception.lineInfo() + ")" @@ -1104,7 +1103,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting errors.append(formatErrorWithException( compilerStack, _exception, - false, + Error::Severity::Error, "InternalCompilerError", "general", "Internal compiler error (" + _exception.lineInfo() + ")" @@ -1115,7 +1114,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting errors.append(formatErrorWithException( compilerStack, _exception, - false, + Error::Severity::Error, "UnimplementedFeatureError", "general", "Unimplemented feature (" + _exception.lineInfo() + ")" @@ -1126,7 +1125,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting errors.append(formatErrorWithException( compilerStack, _exception, - false, + Error::Severity::Error, "YulException", "general", "Yul exception" @@ -1137,7 +1136,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting errors.append(formatErrorWithException( compilerStack, _exception, - false, + Error::Severity::Error, "SMTLogicException", "general", "SMT logic exception" @@ -1146,7 +1145,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (util::Exception const& _exception) { errors.append(formatError( - false, + Error::Severity::Error, "Exception", "general", "Exception during compilation: " + boost::diagnostic_information(_exception) @@ -1155,7 +1154,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (std::exception const& _e) { errors.append(formatError( - false, + Error::Severity::Error, "Exception", "general", "Unknown exception during compilation" + (_e.what() ? ": " + string(_e.what()) : ".") @@ -1164,7 +1163,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting catch (...) { errors.append(formatError( - false, + Error::Severity::Error, "Exception", "general", "Unknown exception during compilation." @@ -1312,16 +1311,49 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) { - if (_inputsAndSettings.sources.size() != 1) - return formatFatalError("JSONError", "Yul mode only supports exactly one input file."); - if (!_inputsAndSettings.smtLib2Responses.empty()) - 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.revertStrings != RevertStrings::Default) - return formatFatalError("JSONError", "Field \"settings.debug.revertStrings\" cannot be used for Yul."); - Json::Value output = Json::objectValue; + output["errors"] = std::move(_inputsAndSettings.errors); + + if (_inputsAndSettings.sources.size() != 1) + { + output["errors"].append(formatError( + Error::Severity::Error, + "JSONError", + "general", + "Yul mode only supports exactly one input file." + )); + return output; + } + if (!_inputsAndSettings.smtLib2Responses.empty()) + { + output["errors"].append(formatError( + Error::Severity::Error, + "JSONError", + "general", + "Yul mode does not support smtlib2responses." + )); + return output; + } + if (!_inputsAndSettings.remappings.empty()) + { + output["errors"].append(formatError( + Error::Severity::Error, + "JSONError", + "general", + "Field \"settings.remappings\" cannot be used for Yul." + )); + return output; + } + if (_inputsAndSettings.revertStrings != RevertStrings::Default) + { + output["errors"].append(formatError( + Error::Severity::Error, + "JSONError", + "general", + "Field \"settings.debug.revertStrings\" cannot be used for Yul." + )); + return output; + } AssemblyStack stack( _inputsAndSettings.evmVersion, @@ -1333,31 +1365,37 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) // Inconsistent state - stop here to receive error reports from users if (!stack.parseAndAnalyze(sourceName, sourceContents) && stack.errors().empty()) - return formatFatalError("InternalCompilerError", "No error reported, but compilation failed."); + { + output["errors"].append(formatError( + Error::Severity::Error, + "InternalCompilerError", + "general", + "No error reported, but compilation failed." + )); + return output; + } if (!stack.errors().empty()) { - Json::Value errors = Json::arrayValue; for (auto const& error: stack.errors()) { auto err = dynamic_pointer_cast(error); - errors.append(formatErrorWithException( + output["errors"].append(formatErrorWithException( stack, *error, - err->type() == Error::Type::Warning, + Error::errorSeverity(err->type()), err->typeName(), "general", "" )); } - output["errors"] = errors; return output; } // TODO: move this warning to AssemblyStack output["errors"] = Json::arrayValue; - output["errors"].append(formatError(true, "Warning", "general", "Yul is still experimental. Please use the output with care.")); + output["errors"].append(formatError(Error::Severity::Warning, "Warning", "general", "Yul is still experimental. Please use the output with care.")); string contractName = stack.parserResult()->name.str(); diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index 297333b6b..0017b6de1 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -23,11 +23,7 @@ #include -#include -#include -#include #include -#include using namespace std; diff --git a/libsolidity/interface/Version.h b/libsolidity/interface/Version.h index a5965d42e..bc05fb403 100644 --- a/libsolidity/interface/Version.h +++ b/libsolidity/interface/Version.h @@ -23,10 +23,15 @@ #pragma once -#include +#include +#include #include -namespace solidity::frontend +namespace solidity +{ +using bytes = std::vector; + +namespace frontend { extern char const* VersionNumber; @@ -36,3 +41,4 @@ extern bytes const VersionCompactBytes; extern bool const VersionIsRelease; } +} diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 3f30a07c9..fea062d01 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,9 @@ ASTPointer Parser::parse(CharStream& _charStream) case Token::Enum: nodes.push_back(parseEnumDefinition()); break; + case Token::Type: + nodes.push_back(parseUserDefinedValueTypeDefinition()); + break; case Token::Function: nodes.push_back(parseFunctionDefinition(true)); break; @@ -364,6 +368,8 @@ ASTPointer Parser::parseContractDefinition() subNodes.push_back(parseStructDefinition()); else if (currentTokenValue == Token::Enum) subNodes.push_back(parseEnumDefinition()); + else if (currentTokenValue == Token::Type) + subNodes.push_back(parseUserDefinedValueTypeDefinition()); else if ( // Workaround because `error` is not a keyword. currentTokenValue == Token::Identifier && @@ -1010,6 +1016,22 @@ ASTPointer Parser::parseUserDefinedTypeName() return nodeFactory.createNode(identifierPath); } +ASTPointer Parser::parseUserDefinedValueTypeDefinition() +{ + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Type); + auto&& [name, nameLocation] = expectIdentifierWithLocation(); + expectToken(Token::Is); + ASTPointer typeName = parseTypeName(); + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode( + name, + move(nameLocation), + typeName + ); +} + ASTPointer Parser::parseIdentifierPath() { RecursionGuard recursionGuard(*this); @@ -1304,7 +1326,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con if (block == nullptr) BOOST_THROW_EXCEPTION(FatalError()); - location.end = block->debugData->location.end; + location.end = nativeLocationOf(*block).end; return make_shared(nextID(), location, _docString, dialect, block); } @@ -2057,7 +2079,8 @@ bool Parser::variableDeclarationStart() optional Parser::findLicenseString(std::vector> const& _nodes) { // We circumvent the scanner here, because it skips non-docstring comments. - static regex const licenseRegex("SPDX-License-Identifier:\\s*([a-zA-Z0-9 ()+.-]+)"); + static regex const licenseNameRegex("([a-zA-Z0-9 ()+.-]+)"); + static regex const licenseDeclarationRegex("SPDX-License-Identifier:\\s*(.+?)([\n\r]|(\\*/))"); // Search inside all parts of the source not covered by parsed nodes. // This will leave e.g. "global comments". @@ -2072,21 +2095,33 @@ optional Parser::findLicenseString(std::vector> cons sequencesToSearch.emplace_back(source.begin() + node->location().end, source.end()); } - vector matches; + vector licenseNames; for (auto const& [start, end]: sequencesToSearch) { - smatch match; - if (regex_search(start, end, match, licenseRegex)) - { - string license{boost::trim_copy(string(match[1]))}; - if (!license.empty()) - matches.emplace_back(std::move(license)); - } + auto declarationsBegin = std::sregex_iterator(start, end, licenseDeclarationRegex); + auto declarationsEnd = std::sregex_iterator(); + + for (std::sregex_iterator declIt = declarationsBegin; declIt != declarationsEnd; ++declIt) + if (!declIt->empty()) + { + string license = boost::trim_copy(string((*declIt)[1])); + licenseNames.emplace_back(std::move(license)); + } } - if (matches.size() == 1) - return matches.front(); - else if (matches.empty()) + if (licenseNames.size() == 1) + { + string const& license = licenseNames.front(); + if (regex_match(license, licenseNameRegex)) + return license; + else + parserError( + 1114_error, + {-1, -1, m_scanner->currentLocation().sourceName}, + "Invalid SPDX license identifier." + ); + } + else if (licenseNames.empty()) parserWarning( 1878_error, {-1, -1, m_scanner->currentLocation().sourceName}, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5374966d7..e656c936d 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -95,6 +95,7 @@ private: ASTPointer parseFunctionDefinition(bool _freeFunction = false); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); + ASTPointer parseUserDefinedValueTypeDefinition(); ASTPointer parseEnumValue(); ASTPointer parseVariableDeclaration( VarDeclParserOptions const& _options = {}, diff --git a/libsolutil/Assertions.h b/libsolutil/Assertions.h index b143e6ed7..f04e90a1f 100644 --- a/libsolutil/Assertions.h +++ b/libsolutil/Assertions.h @@ -27,6 +27,8 @@ #include +#include + namespace solidity::util { @@ -38,16 +40,32 @@ namespace solidity::util #define ETH_FUNC __func__ #endif -/// Assertion that throws an exception containing the given description if it is not met. -/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong."); -/// Do NOT supply an exception object as the second parameter. -#define assertThrow(_condition, _exceptionType, _description) \ +namespace assertions +{ + +inline std::string stringOrDefault(std::string _string, std::string _defaultString) +{ + // NOTE: Putting this in a function rather than directly in a macro prevents the string from + // being evaluated multiple times if it's not just a literal. + return (!_string.empty() ? _string : _defaultString); +} + +} + +/// Base macro that can be used to implement assertion macros. +/// Throws an exception containing the given description if the condition is not met. +/// Allows you to provide the default description for the case where the user of your macro does +/// not provide any. +/// The second parameter must be an exception class (rather than an instance). +#define assertThrowWithDefaultDescription(_condition, _exceptionType, _description, _defaultDescription) \ do \ { \ if (!(_condition)) \ ::boost::throw_exception( \ _exceptionType() << \ - ::solidity::util::errinfo_comment(_description) << \ + ::solidity::util::errinfo_comment( \ + ::solidity::util::assertions::stringOrDefault(_description, _defaultDescription) \ + ) << \ ::boost::throw_function(ETH_FUNC) << \ ::boost::throw_file(__FILE__) << \ ::boost::throw_line(__LINE__) \ @@ -55,4 +73,10 @@ namespace solidity::util } \ while (false) +/// Assertion that throws an exception containing the given description if it is not met. +/// Use it as assertThrow(1 == 1, ExceptionType, "Mathematics is wrong."); +/// The second parameter must be an exception class (rather than an instance). +#define assertThrow(_condition, _exceptionType, _description) \ + assertThrowWithDefaultDescription(_condition, _exceptionType, _description, "Assertion failed") + } diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index 535b2b90f..e055317be 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -2,7 +2,6 @@ set(sources Algorithms.h AnsiColorized.h Assertions.h - Common.cpp Common.h CommonData.cpp CommonData.h @@ -24,6 +23,8 @@ set(sources Keccak256.h LazyInit.h LEB128.h + Numeric.cpp + Numeric.h picosha2.h Result.h SetOnce.h diff --git a/libsolutil/Common.h b/libsolutil/Common.h index 75de12747..5c7814613 100644 --- a/libsolutil/Common.h +++ b/libsolutil/Common.h @@ -45,8 +45,6 @@ #error "Unsupported Boost version. At least 1.65 required." #endif -#include - #include #include #include @@ -61,66 +59,12 @@ using bytes = std::vector; using bytesRef = util::vector_ref; using bytesConstRef = util::vector_ref; -// Numeric types. -using bigint = boost::multiprecision::number>; -using u256 = boost::multiprecision::number>; -using s256 = boost::multiprecision::number>; - // Map types. using StringMap = std::map; // String types. using strings = std::vector; -/// Interprets @a _u as a two's complement signed number and returns the resulting s256. -inline s256 u2s(u256 _u) -{ - static bigint const c_end = bigint(1) << 256; - if (boost::multiprecision::bit_test(_u, 255)) - return s256(-(c_end - _u)); - else - return s256(_u); -} - -/// @returns the two's complement signed representation of the signed number _u. -inline u256 s2u(s256 _u) -{ - static bigint const c_end = bigint(1) << 256; - if (_u >= 0) - return u256(_u); - else - return u256(c_end + _u); -} - -inline u256 exp256(u256 _base, u256 _exponent) -{ - using boost::multiprecision::limb_type; - u256 result = 1; - while (_exponent) - { - if (boost::multiprecision::bit_test(_exponent, 0)) - result *= _base; - _base *= _base; - _exponent >>= 1; - } - 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; - ss << std::hex; - std::copy(_bytes.begin(), _bytes.end(), std::ostream_iterator(ss, ",")); - std::string result = ss.str(); - result.pop_back(); - os << "[" + result + "]"; - return os; -} - /// RAII utility class whose destructor calls a given function. class ScopeGuard { diff --git a/libsolutil/CommonData.h b/libsolutil/CommonData.h index 7c2370370..0d5a75282 100644 --- a/libsolutil/CommonData.h +++ b/libsolutil/CommonData.h @@ -36,6 +36,8 @@ #include #include #include +#include +#include /// Operators need to stay in the global namespace. @@ -52,6 +54,21 @@ template std::vector& operator+=(std::vector& _a, U&& _ std::move(_b.begin(), _b.end(), std::back_inserter(_a)); return _a; } + +/// Concatenate the contents of a container onto a list +template std::list& operator+=(std::list& _a, U& _b) +{ + for (auto const& i: _b) + _a.push_back(T(i)); + return _a; +} +/// Concatenate the contents of a container onto a list, move variant. +template std::list& operator+=(std::list& _a, U&& _b) +{ + std::move(_b.begin(), _b.end(), std::back_inserter(_a)); + return _a; +} + /// Concatenate the contents of a container onto a multiset template std::multiset& operator+=(std::multiset& _a, U& _b) { @@ -321,6 +338,44 @@ void joinMap(std::map& _a, std::map&& _b, F _conflictSolver) } } +namespace detail +{ + +template +auto findOffset(Container&& _container, Value&& _value, int) +-> decltype(_container.find(_value) == _container.end(), std::distance(_container.begin(), _container.find(_value)), std::optional()) +{ + auto it = _container.find(std::forward(_value)); + auto end = _container.end(); + if (it == end) + return std::nullopt; + return std::distance(_container.begin(), it); +} +template +auto findOffset(Range&& _range, Value&& _value, void*) +-> decltype(std::find(std::begin(_range), std::end(_range), std::forward(_value)) == std::end(_range), std::optional()) +{ + auto begin = std::begin(_range); + auto end = std::end(_range); + auto it = std::find(begin, end, std::forward(_value)); + if (it == end) + return std::nullopt; + return std::distance(begin, it); +} + +} + +/// @returns an std::optional containing the offset of the first element in @a _range that is equal to @a _value, +/// if any, or std::nullopt otherwise. +/// Uses a linear search (``std::find``) unless @a _range is a container and provides a +/// suitable ``.find`` function (e.g. it will use the logarithmic ``.find`` function in ``std::set`` instead). +template +auto findOffset(Range&& _range, std::remove_reference_t const& _value) +-> decltype(detail::findOffset(std::forward(_range), _value, 0)) +{ + return detail::findOffset(std::forward(_range), _value, 0); +} + // String conversion functions, mainly to/from hex/nibble/byte representations. enum class WhenError @@ -378,94 +433,6 @@ inline bytes asBytes(std::string const& _b) return bytes((uint8_t const*)_b.data(), (uint8_t const*)(_b.data() + _b.size())); } -// Big-endian to/from host endian conversion functions. - -/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection. -/// The size of the collection object will be unchanged. If it is too small, it will not represent the -/// value properly, if too big then the additional elements will be zeroed out. -/// @a Out will typically be either std::string or bytes. -/// @a T will typically by unsigned, u160, u256 or bigint. -template -inline void toBigEndian(T _val, Out& o_out) -{ - static_assert(std::is_same::value || !std::numeric_limits::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift - for (auto i = o_out.size(); i != 0; _val >>= 8, i--) - { - T v = _val & (T)0xff; - o_out[i - 1] = (typename Out::value_type)(uint8_t)v; - } -} - -/// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. -/// @a In will typically be either std::string or bytes. -/// @a T will typically by unsigned, u256 or bigint. -template -inline T fromBigEndian(In const& _bytes) -{ - T ret = (T)0; - for (auto i: _bytes) - ret = (T)((ret << 8) | (uint8_t)(typename std::make_unsigned::type)i); - return ret; -} -inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } - -/// Convenience function for toBigEndian. -/// @returns a byte array just big enough to represent @a _val. -template -inline bytes toCompactBigEndian(T _val, unsigned _min = 0) -{ - static_assert(std::is_same::value || !std::numeric_limits::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift - unsigned i = 0; - for (T v = _val; v; ++i, v >>= 8) {} - bytes ret(std::max(_min, i), 0); - toBigEndian(_val, ret); - return ret; -} - -/// Convenience function for conversion of a u256 to hex -inline std::string toHex(u256 val, HexPrefix prefix = HexPrefix::DontAdd) -{ - std::string str = toHex(toBigEndian(val)); - return (prefix == HexPrefix::Add) ? "0x" + str : str; -} - -template -inline std::string toCompactHexWithPrefix(T _value) -{ - return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); -} - -/// Returns decimal representation for small numbers and hex for large numbers. -inline std::string formatNumber(bigint const& _value) -{ - if (_value < 0) - return "-" + formatNumber(-_value); - if (_value > 0x1000000) - return toHex(toCompactBigEndian(_value, 1), HexPrefix::Add); - else - return _value.str(); -} - -inline std::string formatNumber(u256 const& _value) -{ - if (_value > 0x1000000) - return toCompactHexWithPrefix(_value); - else - return _value.str(); -} - - -// Algorithms for string and string-like collections. - -/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. -template -inline unsigned bytesRequired(T _i) -{ - static_assert(std::is_same::value || !std::numeric_limits::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift - unsigned i = 0; - for (; _i != 0; ++i, _i >>= 8) {} - return i; -} template bool contains(T const& _t, V const& _v) { diff --git a/libsolutil/CommonIO.cpp b/libsolutil/CommonIO.cpp index 30552b6ad..9da5b2422 100644 --- a/libsolutil/CommonIO.cpp +++ b/libsolutil/CommonIO.cpp @@ -74,16 +74,9 @@ string solidity::util::readFileAsString(boost::filesystem::path const& _file) string solidity::util::readUntilEnd(istream& _stdin) { - string ret; - while (!_stdin.eof()) - { - string tmp; - // NOTE: this will read until EOF or NL - getline(_stdin, tmp); - ret.append(tmp); - ret.append("\n"); - } - return ret; + ostringstream ss; + ss << _stdin.rdbuf(); + return ss.str(); } #if defined(_WIN32) diff --git a/libsolutil/CommonIO.h b/libsolutil/CommonIO.h index e8b87fe85..ab0000f86 100644 --- a/libsolutil/CommonIO.h +++ b/libsolutil/CommonIO.h @@ -31,7 +31,21 @@ #include #include -namespace solidity::util +namespace solidity +{ + +inline std::ostream& operator<<(std::ostream& os, bytes const& _bytes) +{ + std::ostringstream ss; + ss << std::hex; + std::copy(_bytes.begin(), _bytes.end(), std::ostream_iterator(ss, ",")); + std::string result = ss.str(); + result.pop_back(); + os << "[" + result + "]"; + return os; +} + +namespace util { /// Retrieves and returns the contents of the given file as a std::string. @@ -62,3 +76,4 @@ std::string absolutePath(std::string const& _path, std::string const& _reference std::string sanitizePath(std::string const& _path); } +} diff --git a/libsolutil/FixedHash.h b/libsolutil/FixedHash.h index dffeb7a17..97a80603a 100644 --- a/libsolutil/FixedHash.h +++ b/libsolutil/FixedHash.h @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include diff --git a/libsolutil/IpfsHash.cpp b/libsolutil/IpfsHash.cpp index bf1527261..a34ee035b 100644 --- a/libsolutil/IpfsHash.cpp +++ b/libsolutil/IpfsHash.cpp @@ -21,6 +21,7 @@ #include #include #include +#include using namespace std; using namespace solidity; diff --git a/libsolutil/JSON.h b/libsolutil/JSON.h index 3a326a5e3..43dddfd4a 100644 --- a/libsolutil/JSON.h +++ b/libsolutil/JSON.h @@ -38,8 +38,8 @@ struct JsonFormat { enum Format { - Compact, - Pretty + Compact, // No unnecessary whitespace (including new lines and indentation) + Pretty, // Nicely indented, with new lines }; static constexpr uint32_t defaultIndent = 2; diff --git a/libsolutil/Common.cpp b/libsolutil/Numeric.cpp similarity index 97% rename from libsolutil/Common.cpp rename to libsolutil/Numeric.cpp index 3f77e5d25..f1d4057e0 100644 --- a/libsolutil/Common.cpp +++ b/libsolutil/Numeric.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include diff --git a/libsolutil/Numeric.h b/libsolutil/Numeric.h new file mode 100644 index 000000000..557f7e2bc --- /dev/null +++ b/libsolutil/Numeric.h @@ -0,0 +1,171 @@ +/* + 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 +/** + * Definition of u256 and similar types and helper functions. + */ + +#pragma once + +#include +#include + +#include +#if (BOOST_VERSION < 106500) +#error "Unsupported Boost version. At least 1.65 required." +#endif + +#include + +#include + +namespace solidity +{ + +// Numeric types. +using bigint = boost::multiprecision::number>; +using u256 = boost::multiprecision::number>; +using s256 = boost::multiprecision::number>; + +/// Interprets @a _u as a two's complement signed number and returns the resulting s256. +inline s256 u2s(u256 _u) +{ + static bigint const c_end = bigint(1) << 256; + if (boost::multiprecision::bit_test(_u, 255)) + return s256(-(c_end - _u)); + else + return s256(_u); +} + +/// @returns the two's complement signed representation of the signed number _u. +inline u256 s2u(s256 _u) +{ + static bigint const c_end = bigint(1) << 256; + if (_u >= 0) + return u256(_u); + else + return u256(c_end + _u); +} + +inline u256 exp256(u256 _base, u256 _exponent) +{ + using boost::multiprecision::limb_type; + u256 result = 1; + while (_exponent) + { + if (boost::multiprecision::bit_test(_exponent, 0)) + result *= _base; + _base *= _base; + _exponent >>= 1; + } + 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); + + +// Big-endian to/from host endian conversion functions. + +/// Converts a templated integer value to the big-endian byte-stream represented on a templated collection. +/// The size of the collection object will be unchanged. If it is too small, it will not represent the +/// value properly, if too big then the additional elements will be zeroed out. +/// @a Out will typically be either std::string or bytes. +/// @a T will typically by unsigned, u160, u256 or bigint. +template +inline void toBigEndian(T _val, Out& o_out) +{ + static_assert(std::is_same::value || !std::numeric_limits::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift + for (auto i = o_out.size(); i != 0; _val >>= 8, i--) + { + T v = _val & (T)0xff; + o_out[i - 1] = (typename Out::value_type)(uint8_t)v; + } +} + +/// Converts a big-endian byte-stream represented on a templated collection to a templated integer value. +/// @a In will typically be either std::string or bytes. +/// @a T will typically by unsigned, u256 or bigint. +template +inline T fromBigEndian(In const& _bytes) +{ + T ret = (T)0; + for (auto i: _bytes) + ret = (T)((ret << 8) | (uint8_t)(typename std::make_unsigned::type)i); + return ret; +} +inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } + +/// Convenience function for toBigEndian. +/// @returns a byte array just big enough to represent @a _val. +template +inline bytes toCompactBigEndian(T _val, unsigned _min = 0) +{ + static_assert(std::is_same::value || !std::numeric_limits::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift + unsigned i = 0; + for (T v = _val; v; ++i, v >>= 8) {} + bytes ret(std::max(_min, i), 0); + toBigEndian(_val, ret); + return ret; +} + +/// Convenience function for conversion of a u256 to hex +inline std::string toHex(u256 val) +{ + return util::toHex(toBigEndian(val)); +} + +template +inline std::string toCompactHexWithPrefix(T _value) +{ + return "0x" + util::toHex(toCompactBigEndian(_value, 1)); +} + +/// Returns decimal representation for small numbers and hex for large numbers. +inline std::string formatNumber(bigint const& _value) +{ + if (_value < 0) + return "-" + formatNumber(-_value); + if (_value > 0x1000000) + return "0x" + util::toHex(toCompactBigEndian(_value, 1)); + else + return _value.str(); +} + +inline std::string formatNumber(u256 const& _value) +{ + if (_value > 0x1000000) + return toCompactHexWithPrefix(_value); + else + return _value.str(); +} + + +// Algorithms for string and string-like collections. + +/// Determine bytes required to encode the given integer value. @returns 0 if @a _i is zero. +template +inline unsigned numberEncodingSize(T _i) +{ + static_assert(std::is_same::value || !std::numeric_limits::is_signed, "only unsigned types or bigint supported"); //bigint does not carry sign bit on shift + unsigned i = 0; + for (; _i != 0; ++i, _i >>= 8) {} + return i; +} + +} diff --git a/libsolutil/StringUtils.h b/libsolutil/StringUtils.h index 646cda505..09aa30be9 100644 --- a/libsolutil/StringUtils.h +++ b/libsolutil/StringUtils.h @@ -24,11 +24,13 @@ #pragma once +#include +#include + +#include #include #include -#include - namespace solidity::util { diff --git a/libyul/AST.h b/libyul/AST.h index 54d44f6ae..1c7b8e02a 100644 --- a/libyul/AST.h +++ b/libyul/AST.h @@ -29,6 +29,7 @@ #include #include +#include namespace solidity::yul { @@ -37,12 +38,36 @@ using Type = YulString; struct DebugData { - explicit DebugData(langutil::SourceLocation _location): location(std::move(_location)) {} - langutil::SourceLocation location; - static std::shared_ptr create(langutil::SourceLocation _location = {}) + explicit DebugData( + langutil::SourceLocation _nativeLocation, + langutil::SourceLocation _originLocation = {}, + std::optional _astID = {} + ): + nativeLocation(std::move(_nativeLocation)), + originLocation(std::move(_originLocation)), + astID(std::move(_astID)) + {} + + static std::shared_ptr create( + langutil::SourceLocation _nativeLocation = {}, + langutil::SourceLocation _originLocation = {}, + std::optional _astID = {} + ) { - return std::make_shared(_location); + return std::make_shared( + std::move(_nativeLocation), + std::move(_originLocation), + std::move(_astID) + ); } + + /// Location in the Yul code. + langutil::SourceLocation nativeLocation; + /// Location in the original source that the Yul code was produced from. + /// Optional. Only present if the Yul source contains location annotations. + langutil::SourceLocation originLocation; + /// ID in the (Solidity) source AST. + std::optional astID; }; struct TypedName { std::shared_ptr debugData; YulString name; Type type; }; @@ -83,32 +108,40 @@ struct Continue { std::shared_ptr debugData; }; /// Leave statement (valid within function) struct Leave { std::shared_ptr debugData; }; -struct LocationExtractor +/// Extracts the IR source location from a Yul node. +template inline langutil::SourceLocation nativeLocationOf(T const& _node) { - template langutil::SourceLocation operator()(T const& _node) const - { - return _node.debugData ? _node.debugData->location : langutil::SourceLocation{}; - } -}; - -/// Extracts the source location from a Yul node. -template inline langutil::SourceLocation locationOf(T const& _node) -{ - return std::visit(LocationExtractor(), _node); + return _node.debugData ? _node.debugData->nativeLocation : langutil::SourceLocation{}; } -struct DebugDataExtractor +/// Extracts the IR source location from a Yul node. +template inline langutil::SourceLocation nativeLocationOf(std::variant const& _node) { - template std::shared_ptr const& operator()(T const& _node) const - { - return _node.debugData; - } -}; + return std::visit([](auto const& _arg) { return nativeLocationOf(_arg); }, _node); +} + +/// Extracts the original source location from a Yul node. +template inline langutil::SourceLocation originLocationOf(T const& _node) +{ + return _node.debugData ? _node.debugData->originLocation : langutil::SourceLocation{}; +} + +/// Extracts the original source location from a Yul node. +template inline langutil::SourceLocation originLocationOf(std::variant const& _node) +{ + return std::visit([](auto const& _arg) { return originLocationOf(_arg); }, _node); +} /// Extracts the debug data from a Yul node. -template inline std::shared_ptr const& debugDataOf(T const& _node) +template inline std::shared_ptr debugDataOf(T const& _node) { - return std::visit(DebugDataExtractor(), _node); + return _node.debugData; +} + +/// Extracts the debug data from a Yul node. +template inline std::shared_ptr debugDataOf(std::variant const& _node) +{ + return std::visit([](auto const& _arg) { return debugDataOf(_arg); }, _node); } } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 43b78d6b2..c2bdc42f7 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -96,22 +96,22 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, vector AsmAnalyzer::operator()(Literal const& _literal) { - expectValidType(_literal.type, _literal.debugData->location); + expectValidType(_literal.type, nativeLocationOf(_literal)); if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32) m_errorReporter.typeError( 3069_error, - _literal.debugData->location, + nativeLocationOf(_literal), "String literal too long (" + to_string(_literal.value.str().size()) + " > 32)" ); else if (_literal.kind == LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) - m_errorReporter.typeError(6708_error, _literal.debugData->location, "Number literal too large (> 256 bits)"); + m_errorReporter.typeError(6708_error, nativeLocationOf(_literal), "Number literal too large (> 256 bits)"); else if (_literal.kind == LiteralKind::Boolean) yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); if (!m_dialect.validTypeForLiteral(_literal.kind, _literal.value, _literal.type)) m_errorReporter.typeError( 5170_error, - _literal.debugData->location, + nativeLocationOf(_literal), "Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"." ); @@ -131,7 +131,7 @@ vector AsmAnalyzer::operator()(Identifier const& _identifier) if (!m_activeVariables.count(&_var)) m_errorReporter.declarationError( 4990_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Variable " + _identifier.name.str() + " used before it was declared." ); type = _var.type; @@ -140,7 +140,7 @@ vector AsmAnalyzer::operator()(Identifier const& _identifier) { m_errorReporter.typeError( 6041_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Function " + _identifier.name.str() + " used without being called." ); } @@ -158,7 +158,7 @@ vector AsmAnalyzer::operator()(Identifier const& _identifier) // Only add an error message if the callback did not do it. m_errorReporter.declarationError( 8198_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Identifier \"" + _identifier.name.str() + "\" not found." ); @@ -174,7 +174,7 @@ void AsmAnalyzer::operator()(ExpressionStatement const& _statement) if (watcher.ok() && !types.empty()) m_errorReporter.typeError( 3083_error, - _statement.debugData->location, + nativeLocationOf(_statement), "Top-level expressions are not supposed to return values (this expression returns " + to_string(types.size()) + " value" + @@ -194,7 +194,7 @@ void AsmAnalyzer::operator()(Assignment const& _assignment) if (!variables.insert(_variableName.name).second) m_errorReporter.declarationError( 9005_error, - _assignment.debugData->location, + nativeLocationOf(_assignment), "Variable " + _variableName.name.str() + " occurs multiple times on the left-hand side of the assignment." @@ -205,7 +205,7 @@ void AsmAnalyzer::operator()(Assignment const& _assignment) if (types.size() != numVariables) m_errorReporter.declarationError( 8678_error, - _assignment.debugData->location, + nativeLocationOf(_assignment), "Variable count for assignment to \"" + joinHumanReadable(applyMap(_assignment.variableNames, [](auto const& _identifier){ return _identifier.name.str(); })) + "\" does not match number of values (" + @@ -233,8 +233,8 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) ); for (auto const& variable: _varDecl.variables) { - expectValidIdentifier(variable.name, variable.debugData->location); - expectValidType(variable.type, variable.debugData->location); + expectValidIdentifier(variable.name, nativeLocationOf(variable)); + expectValidType(variable.type, nativeLocationOf(variable)); } if (_varDecl.value) @@ -243,7 +243,7 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) if (types.size() != numVariables) m_errorReporter.declarationError( 3812_error, - _varDecl.debugData->location, + nativeLocationOf(_varDecl), "Variable count mismatch for declaration of \"" + joinHumanReadable(applyMap(_varDecl.variables, [](auto const& _identifier){ return _identifier.name.str(); })) + + "\": " + @@ -262,7 +262,7 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) if (variable.type != givenType) m_errorReporter.typeError( 3947_error, - variable.debugData->location, + nativeLocationOf(variable), "Assigning value of type \"" + givenType.str() + "\" to variable of type \"" + variable.type.str() + "\"." ); } @@ -277,14 +277,14 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) void AsmAnalyzer::operator()(FunctionDefinition const& _funDef) { yulAssert(!_funDef.name.empty(), ""); - expectValidIdentifier(_funDef.name, _funDef.debugData->location); + expectValidIdentifier(_funDef.name, nativeLocationOf(_funDef)); Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); yulAssert(virtualBlock, ""); Scope& varScope = scope(virtualBlock); for (auto const& var: _funDef.parameters + _funDef.returnVariables) { - expectValidIdentifier(var.name, var.debugData->location); - expectValidType(var.type, var.debugData->location); + expectValidIdentifier(var.name, nativeLocationOf(var)); + expectValidType(var.type, nativeLocationOf(var)); m_activeVariables.insert(&std::get(varScope.identifiers.at(var.name))); } @@ -313,7 +313,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) { m_errorReporter.typeError( 4202_error, - _funCall.functionName.debugData->location, + nativeLocationOf(_funCall.functionName), "Attempt to call variable instead of function." ); }, @@ -327,7 +327,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (!validateInstructions(_funCall)) m_errorReporter.declarationError( 4619_error, - _funCall.functionName.debugData->location, + nativeLocationOf(_funCall.functionName), "Function \"" + _funCall.functionName.name.str() + "\" not found." ); yulAssert(!watcher.ok(), "Expected a reported error."); @@ -336,7 +336,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (parameterTypes && _funCall.arguments.size() != parameterTypes->size()) m_errorReporter.typeError( 7000_error, - _funCall.functionName.debugData->location, + nativeLocationOf(_funCall.functionName), "Function \"" + _funCall.functionName.name.str() + "\" expects " + to_string(parameterTypes->size()) + " arguments but got " + @@ -356,13 +356,13 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (!holds_alternative(arg)) m_errorReporter.typeError( 9114_error, - _funCall.functionName.debugData->location, + nativeLocationOf(_funCall.functionName), "Function expects direct literals as arguments." ); else if (*literalArgumentKind != get(arg).kind) m_errorReporter.typeError( 5859_error, - get(arg).debugData->location, + nativeLocationOf(arg), "Function expects " + to_string(*literalArgumentKind) + " literal." ); else if (*literalArgumentKind == LiteralKind::String) @@ -373,7 +373,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (!m_dataNames.count(get(arg).value)) m_errorReporter.typeError( 3517_error, - get(arg).debugData->location, + nativeLocationOf(arg), "Unknown data object \"" + std::get(arg).value.str() + "\"." ); } @@ -382,7 +382,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (get(arg).value.empty()) m_errorReporter.typeError( 1844_error, - get(arg).debugData->location, + nativeLocationOf(arg), "The \"verbatim_*\" builtins cannot be used with empty bytecode." ); } @@ -397,7 +397,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (parameterTypes && parameterTypes->size() == argTypes.size()) for (size_t i = 0; i < parameterTypes->size(); ++i) - expectType((*parameterTypes)[i], argTypes[i], locationOf(_funCall.arguments[i])); + expectType((*parameterTypes)[i], argTypes[i], nativeLocationOf(_funCall.arguments[i])); if (watcher.ok()) { @@ -425,7 +425,7 @@ void AsmAnalyzer::operator()(Switch const& _switch) if (_switch.cases.size() == 1 && !_switch.cases[0].value) m_errorReporter.warning( 9592_error, - _switch.debugData->location, + nativeLocationOf(_switch), "\"switch\" statement with only a default case." ); @@ -438,7 +438,7 @@ void AsmAnalyzer::operator()(Switch const& _switch) { auto watcher = m_errorReporter.errorWatcher(); - expectType(valueType, _case.value->type, _case.value->debugData->location); + expectType(valueType, _case.value->type, nativeLocationOf(*_case.value)); // We cannot use "expectExpression" here because *_case.value is not an // Expression and would be converted to an Expression otherwise. @@ -448,7 +448,7 @@ void AsmAnalyzer::operator()(Switch const& _switch) if (watcher.ok() && !cases.insert(valueOfLiteral(*_case.value)).second) m_errorReporter.declarationError( 6792_error, - _case.debugData->location, + nativeLocationOf(_case), "Duplicate case \"" + valueOfLiteral(*_case.value).str() + "\" defined." @@ -500,7 +500,7 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr) if (types.size() != 1) m_errorReporter.typeError( 3950_error, - locationOf(_expr), + nativeLocationOf(_expr), "Expected expression to evaluate to one value, but got " + to_string(types.size()) + " values instead." @@ -522,7 +522,7 @@ void AsmAnalyzer::expectBoolExpression(Expression const& _expr) if (type != m_dialect.boolType) m_errorReporter.typeError( 1733_error, - locationOf(_expr), + nativeLocationOf(_expr), "Expected a value of boolean type \"" + m_dialect.boolType.str() + "\" but got \"" + @@ -540,11 +540,11 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) { if (!holds_alternative(*var)) - m_errorReporter.typeError(2657_error, _variable.debugData->location, "Assignment requires variable."); + m_errorReporter.typeError(2657_error, nativeLocationOf(_variable), "Assignment requires variable."); else if (!m_activeVariables.count(&std::get(*var))) m_errorReporter.declarationError( 1133_error, - _variable.debugData->location, + nativeLocationOf(_variable), "Variable " + _variable.name.str() + " used before it was declared." ); else @@ -563,11 +563,11 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT if (!found && watcher.ok()) // Only add message if the callback did not. - m_errorReporter.declarationError(4634_error, _variable.debugData->location, "Variable not found or variable not lvalue."); + m_errorReporter.declarationError(4634_error, nativeLocationOf(_variable), "Variable not found or variable not lvalue."); if (variableType && *variableType != _valueType) m_errorReporter.typeError( 9547_error, - _variable.debugData->location, + nativeLocationOf(_variable), "Assigning a value of type \"" + _valueType.str() + "\" to a variable of type \"" + @@ -713,5 +713,5 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio bool AsmAnalyzer::validateInstructions(FunctionCall const& _functionCall) { - return validateInstructions(_functionCall.functionName.name.str(), _functionCall.functionName.debugData->location); + return validateInstructions(_functionCall.functionName.name.str(), nativeLocationOf(_functionCall.functionName)); } diff --git a/libyul/AsmJsonConverter.cpp b/libyul/AsmJsonConverter.cpp index fe595e7d2..1807396c5 100644 --- a/libyul/AsmJsonConverter.cpp +++ b/libyul/AsmJsonConverter.cpp @@ -33,7 +33,7 @@ namespace solidity::yul Json::Value AsmJsonConverter::operator()(Block const& _node) const { - Json::Value ret = createAstNode(_node.debugData->location, "YulBlock"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulBlock"); ret["statements"] = vectorOfVariantsToJson(_node.statements); return ret; } @@ -41,7 +41,7 @@ Json::Value AsmJsonConverter::operator()(Block const& _node) const Json::Value AsmJsonConverter::operator()(TypedName const& _node) const { yulAssert(!_node.name.empty(), "Invalid variable name."); - Json::Value ret = createAstNode(_node.debugData->location, "YulTypedName"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulTypedName"); ret["name"] = _node.name.str(); ret["type"] = _node.type.str(); return ret; @@ -49,7 +49,7 @@ Json::Value AsmJsonConverter::operator()(TypedName const& _node) const Json::Value AsmJsonConverter::operator()(Literal const& _node) const { - Json::Value ret = createAstNode(_node.debugData->location, "YulLiteral"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulLiteral"); switch (_node.kind) { case LiteralKind::Number: @@ -76,7 +76,7 @@ Json::Value AsmJsonConverter::operator()(Literal const& _node) const Json::Value AsmJsonConverter::operator()(Identifier const& _node) const { yulAssert(!_node.name.empty(), "Invalid identifier"); - Json::Value ret = createAstNode(_node.debugData->location, "YulIdentifier"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulIdentifier"); ret["name"] = _node.name.str(); return ret; } @@ -84,7 +84,7 @@ Json::Value AsmJsonConverter::operator()(Identifier const& _node) const Json::Value AsmJsonConverter::operator()(Assignment const& _node) const { yulAssert(_node.variableNames.size() >= 1, "Invalid assignment syntax"); - Json::Value ret = createAstNode(_node.debugData->location, "YulAssignment"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulAssignment"); for (auto const& var: _node.variableNames) ret["variableNames"].append((*this)(var)); ret["value"] = _node.value ? std::visit(*this, *_node.value) : Json::nullValue; @@ -93,7 +93,7 @@ Json::Value AsmJsonConverter::operator()(Assignment const& _node) const Json::Value AsmJsonConverter::operator()(FunctionCall const& _node) const { - Json::Value ret = createAstNode(_node.debugData->location, "YulFunctionCall"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulFunctionCall"); ret["functionName"] = (*this)(_node.functionName); ret["arguments"] = vectorOfVariantsToJson(_node.arguments); return ret; @@ -101,14 +101,14 @@ Json::Value AsmJsonConverter::operator()(FunctionCall const& _node) const Json::Value AsmJsonConverter::operator()(ExpressionStatement const& _node) const { - Json::Value ret = createAstNode(_node.debugData->location, "YulExpressionStatement"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulExpressionStatement"); ret["expression"] = std::visit(*this, _node.expression); return ret; } Json::Value AsmJsonConverter::operator()(VariableDeclaration const& _node) const { - Json::Value ret = createAstNode(_node.debugData->location, "YulVariableDeclaration"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulVariableDeclaration"); for (auto const& var: _node.variables) ret["variables"].append((*this)(var)); @@ -120,7 +120,7 @@ Json::Value AsmJsonConverter::operator()(VariableDeclaration const& _node) const Json::Value AsmJsonConverter::operator()(FunctionDefinition const& _node) const { yulAssert(!_node.name.empty(), "Invalid function name."); - Json::Value ret = createAstNode(_node.debugData->location, "YulFunctionDefinition"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulFunctionDefinition"); ret["name"] = _node.name.str(); for (auto const& var: _node.parameters) ret["parameters"].append((*this)(var)); @@ -133,7 +133,7 @@ Json::Value AsmJsonConverter::operator()(FunctionDefinition const& _node) const Json::Value AsmJsonConverter::operator()(If const& _node) const { yulAssert(_node.condition, "Invalid if condition."); - Json::Value ret = createAstNode(_node.debugData->location, "YulIf"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulIf"); ret["condition"] = std::visit(*this, *_node.condition); ret["body"] = (*this)(_node.body); return ret; @@ -142,7 +142,7 @@ Json::Value AsmJsonConverter::operator()(If const& _node) const Json::Value AsmJsonConverter::operator()(Switch const& _node) const { yulAssert(_node.expression, "Invalid expression pointer."); - Json::Value ret = createAstNode(_node.debugData->location, "YulSwitch"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulSwitch"); ret["expression"] = std::visit(*this, *_node.expression); for (auto const& var: _node.cases) ret["cases"].append((*this)(var)); @@ -151,7 +151,7 @@ Json::Value AsmJsonConverter::operator()(Switch const& _node) const Json::Value AsmJsonConverter::operator()(Case const& _node) const { - Json::Value ret = createAstNode(_node.debugData->location, "YulCase"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulCase"); ret["value"] = _node.value ? (*this)(*_node.value) : "default"; ret["body"] = (*this)(_node.body); return ret; @@ -160,7 +160,7 @@ Json::Value AsmJsonConverter::operator()(Case const& _node) const Json::Value AsmJsonConverter::operator()(ForLoop const& _node) const { yulAssert(_node.condition, "Invalid for loop condition."); - Json::Value ret = createAstNode(_node.debugData->location, "YulForLoop"); + Json::Value ret = createAstNode(nativeLocationOf(_node), "YulForLoop"); ret["pre"] = (*this)(_node.pre); ret["condition"] = std::visit(*this, *_node.condition); ret["post"] = (*this)(_node.post); @@ -170,17 +170,17 @@ Json::Value AsmJsonConverter::operator()(ForLoop const& _node) const Json::Value AsmJsonConverter::operator()(Break const& _node) const { - return createAstNode(_node.debugData->location, "YulBreak"); + return createAstNode(nativeLocationOf(_node), "YulBreak"); } Json::Value AsmJsonConverter::operator()(Continue const& _node) const { - return createAstNode(_node.debugData->location, "YulContinue"); + return createAstNode(nativeLocationOf(_node), "YulContinue"); } Json::Value AsmJsonConverter::operator()(Leave const& _node) const { - return createAstNode(_node.debugData->location, "YulLeave"); + return createAstNode(nativeLocationOf(_node), "YulLeave"); } Json::Value AsmJsonConverter::createAstNode(langutil::SourceLocation const& _location, string _nodeType) const diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index f74fac4de..8fb4eaeb3 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -53,9 +53,12 @@ template T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; - SourceLocation location = createSourceLocation(_node); - yulAssert(location.hasText(), "Invalid source location in Asm AST"); - r.debugData = DebugData::create(location); + SourceLocation nativeLocation = createSourceLocation(_node); + yulAssert(nativeLocation.hasText(), "Invalid source location in Asm AST"); + // TODO: We should add originLocation to the AST. + // While it's not included, we'll use nativeLocation for it because we only support importing + // inline assembly as a part of a Solidity AST and there these locations are always the same. + r.debugData = DebugData::create(nativeLocation, nativeLocation); return r; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index d1465c737..ba4bf1115 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -46,17 +46,6 @@ using namespace solidity::yul; namespace { -[[nodiscard]] -shared_ptr updateLocationEndFrom( - shared_ptr const& _debugData, - langutil::SourceLocation const& _location -) -{ - SourceLocation updatedLocation = _debugData->location; - updatedLocation.end = _location.end; - return make_shared(updatedLocation); -} - optional toInt(string const& _value) { try @@ -76,15 +65,45 @@ std::shared_ptr Parser::createDebugData() const switch (m_useSourceLocationFrom) { case UseSourceLocationFrom::Scanner: - return DebugData::create(ParserBase::currentLocation()); + return DebugData::create(ParserBase::currentLocation(), ParserBase::currentLocation()); case UseSourceLocationFrom::LocationOverride: - return DebugData::create(m_locationOverride); + return DebugData::create(m_locationOverride, m_locationOverride); case UseSourceLocationFrom::Comments: - return m_debugDataOverride; + return DebugData::create(ParserBase::currentLocation(), m_locationFromComment, m_astIDFromComment); } solAssert(false, ""); } +void Parser::updateLocationEndFrom( + shared_ptr& _debugData, + SourceLocation const& _location +) const +{ + solAssert(_debugData, ""); + + switch (m_useSourceLocationFrom) + { + case UseSourceLocationFrom::Scanner: + { + DebugData updatedDebugData = *_debugData; + updatedDebugData.nativeLocation.end = _location.end; + updatedDebugData.originLocation.end = _location.end; + _debugData = make_shared(move(updatedDebugData)); + break; + } + case UseSourceLocationFrom::LocationOverride: + // Ignore the update. The location we're overriding with is not supposed to change + break; + case UseSourceLocationFrom::Comments: + { + DebugData updatedDebugData = *_debugData; + updatedDebugData.nativeLocation.end = _location.end; + _debugData = make_shared(move(updatedDebugData)); + break; + } + } +} + unique_ptr Parser::parse(CharStream& _charStream) { m_scanner = make_shared(_charStream); @@ -103,8 +122,8 @@ unique_ptr Parser::parseInline(std::shared_ptr const& _scanner) try { m_scanner = _scanner; - if (m_sourceNames) - fetchSourceLocationFromComment(); + if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments) + fetchDebugDataFromComment(); return make_unique(parseBlock()); } catch (FatalError const&) @@ -119,49 +138,149 @@ langutil::Token Parser::advance() { auto const token = ParserBase::advance(); if (m_useSourceLocationFrom == UseSourceLocationFrom::Comments) - fetchSourceLocationFromComment(); + fetchDebugDataFromComment(); return token; } -void Parser::fetchSourceLocationFromComment() +void Parser::fetchDebugDataFromComment() { solAssert(m_sourceNames.has_value(), ""); - if (m_scanner->currentCommentLiteral().empty()) - return; - - static regex const lineRE = std::regex( - R"~~~((^|\s*)@src\s+(-1|\d+):(-1|\d+):(-1|\d+)(\s+|$))~~~", - std::regex_constants::ECMAScript | std::regex_constants::optimize + static regex const tagRegex = regex( + R"~~((?:^|\s+)(@[a-zA-Z0-9\-_]+)(?:\s+|$))~~", // tag, e.g: @src + regex_constants::ECMAScript | regex_constants::optimize ); - string const text = m_scanner->currentCommentLiteral(); - auto from = sregex_iterator(text.begin(), text.end(), lineRE); - auto to = sregex_iterator(); + string_view commentLiteral = m_scanner->currentCommentLiteral(); + match_results match; - for (auto const& matchResult: ranges::make_subrange(from, to)) + langutil::SourceLocation originLocation = m_locationFromComment; + // Empty for each new node. + optional astID; + + while (regex_search(commentLiteral.cbegin(), commentLiteral.cend(), match, tagRegex)) { - solAssert(matchResult.size() == 6, ""); + solAssert(match.size() == 2, ""); + commentLiteral = commentLiteral.substr(static_cast(match.position() + match.length())); - auto const sourceIndex = toInt(matchResult[2].str()); - auto const start = toInt(matchResult[3].str()); - auto const end = toInt(matchResult[4].str()); - - auto const commentLocation = m_scanner->currentCommentLocation(); - m_debugDataOverride = DebugData::create(); - if (!sourceIndex || !start || !end) - m_errorReporter.syntaxError(6367_error, commentLocation, "Invalid value in source location mapping. Could not parse location specification."); - else if (sourceIndex == -1) - m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, nullptr}); - else if (!(sourceIndex >= 0 && m_sourceNames->count(static_cast(*sourceIndex)))) - m_errorReporter.syntaxError(2674_error, commentLocation, "Invalid source mapping. Source index not defined via @use-src."); - else + if (match[1] == "@src") { - shared_ptr sourceName = m_sourceNames->at(static_cast(*sourceIndex)); - solAssert(sourceName, ""); - m_debugDataOverride = DebugData::create(SourceLocation{*start, *end, move(sourceName)}); + if (auto parseResult = parseSrcComment(commentLiteral, m_scanner->currentCommentLocation())) + tie(commentLiteral, originLocation) = *parseResult; + else + break; } + else if (match[1] == "@ast-id") + { + if (auto parseResult = parseASTIDComment(commentLiteral, m_scanner->currentCommentLocation())) + tie(commentLiteral, astID) = *parseResult; + else + break; + } + else + // Ignore unrecognized tags. + continue; } + + m_locationFromComment = originLocation; + m_astIDFromComment = astID; +} + +optional> Parser::parseSrcComment( + string_view const _arguments, + langutil::SourceLocation const& _commentLocation +) +{ + static regex const argsRegex = regex( + R"~~(^(-1|\d+):(-1|\d+):(-1|\d+)(?:\s+|$))~~" // index and location, e.g.: 1:234:-1 + R"~~(("(?:[^"\\]|\\.)*"?)?)~~", // optional code snippet, e.g.: "string memory s = \"abc\";..." + regex_constants::ECMAScript | regex_constants::optimize + ); + match_results match; + if (!regex_search(_arguments.cbegin(), _arguments.cend(), match, argsRegex)) + { + m_errorReporter.syntaxError( + 8387_error, + _commentLocation, + "Invalid values in source location mapping. Could not parse location specification." + ); + return nullopt; + } + + solAssert(match.size() == 5, ""); + string_view tail = _arguments.substr(static_cast(match.position() + match.length())); + + if (match[4].matched && ( + !boost::algorithm::ends_with(match[4].str(), "\"") || + boost::algorithm::ends_with(match[4].str(), "\\\"") + )) + { + m_errorReporter.syntaxError( + 1544_error, + _commentLocation, + "Invalid code snippet in source location mapping. Quote is not terminated." + ); + return {{tail, SourceLocation{}}}; + } + + optional const sourceIndex = toInt(match[1].str()); + optional const start = toInt(match[2].str()); + optional const end = toInt(match[3].str()); + + if (!sourceIndex.has_value() || !start.has_value() || !end.has_value()) + m_errorReporter.syntaxError( + 6367_error, + _commentLocation, + "Invalid value in source location mapping. " + "Expected non-negative integer values or -1 for source index and location." + ); + else if (sourceIndex == -1) + return {{tail, SourceLocation{start.value(), end.value(), nullptr}}}; + else if (!(sourceIndex >= 0 && m_sourceNames->count(static_cast(sourceIndex.value())))) + m_errorReporter.syntaxError( + 2674_error, + _commentLocation, + "Invalid source mapping. Source index not defined via @use-src." + ); + else + { + shared_ptr sourceName = m_sourceNames->at(static_cast(sourceIndex.value())); + solAssert(sourceName, ""); + return {{tail, SourceLocation{start.value(), end.value(), move(sourceName)}}}; + } + return {{tail, SourceLocation{}}}; +} + +optional>> Parser::parseASTIDComment( + string_view _arguments, + langutil::SourceLocation const& _commentLocation +) +{ + static regex const argRegex = regex( + R"~~(^(\d+)(?:\s|$))~~", + regex_constants::ECMAScript | regex_constants::optimize + ); + match_results match; + optional astID; + bool matched = regex_search(_arguments.cbegin(), _arguments.cend(), match, argRegex); + string_view tail = _arguments; + if (matched) + { + solAssert(match.size() == 2, ""); + tail = _arguments.substr(static_cast(match.position() + match.length())); + + astID = toInt(match[1].str()); + } + + if (!matched || !astID || *astID < 0 || static_cast(*astID) != *astID) + { + m_errorReporter.syntaxError(1749_error, _commentLocation, "Invalid argument for @ast-id."); + astID = nullopt; + } + if (matched) + return {{_arguments, astID}}; + else + return nullopt; } Block Parser::parseBlock() @@ -171,8 +290,7 @@ Block Parser::parseBlock() expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - block.debugData = updateLocationEndFrom(block.debugData, currentLocation()); + updateLocationEndFrom(block.debugData, currentLocation()); advance(); return block; } @@ -194,8 +312,7 @@ Statement Parser::parseStatement() advance(); _if.condition = make_unique(parseExpression()); _if.body = parseBlock(); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - _if.debugData = updateLocationEndFrom(_if.debugData, _if.body.debugData->location); + updateLocationEndFrom(_if.debugData, nativeLocationOf(_if.body)); return Statement{move(_if)}; } case Token::Switch: @@ -213,8 +330,7 @@ Statement Parser::parseStatement() fatalParserError(4904_error, "Case not allowed after default case."); if (_switch.cases.empty()) fatalParserError(2418_error, "Switch statement without any cases."); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - _switch.debugData = updateLocationEndFrom(_switch.debugData, _switch.cases.back().body.debugData->location); + updateLocationEndFrom(_switch.debugData, nativeLocationOf(_switch.cases.back().body)); return Statement{move(_switch)}; } case Token::For: @@ -296,8 +412,7 @@ Statement Parser::parseStatement() expectToken(Token::AssemblyAssign); assignment.value = make_unique(parseExpression()); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - assignment.debugData = updateLocationEndFrom(assignment.debugData, locationOf(*assignment.value)); + updateLocationEndFrom(assignment.debugData, nativeLocationOf(*assignment.value)); return Statement{move(assignment)}; } @@ -327,8 +442,7 @@ Case Parser::parseCase() else yulAssert(false, "Case or default case expected."); _case.body = parseBlock(); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - _case.debugData = updateLocationEndFrom(_case.debugData, _case.body.debugData->location); + updateLocationEndFrom(_case.debugData, nativeLocationOf(_case.body)); return _case; } @@ -348,8 +462,7 @@ ForLoop Parser::parseForLoop() forLoop.post = parseBlock(); m_currentForLoopComponent = ForLoopComponent::ForLoopBody; forLoop.body = parseBlock(); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - forLoop.debugData = updateLocationEndFrom(forLoop.debugData, forLoop.body.debugData->location); + updateLocationEndFrom(forLoop.debugData, nativeLocationOf(forLoop.body)); m_currentForLoopComponent = outerForLoopComponent; @@ -369,7 +482,7 @@ Expression Parser::parseExpression() if (m_dialect.builtin(_identifier.name)) fatalParserError( 7104_error, - _identifier.debugData->location, + nativeLocationOf(_identifier), "Builtin function \"" + _identifier.name.str() + "\" must be called." ); return move(_identifier); @@ -428,8 +541,7 @@ variant Parser::parseLiteralOrIdentifier() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - literal.debugData = updateLocationEndFrom(literal.debugData, currentLocation()); + updateLocationEndFrom(literal.debugData, currentLocation()); literal.type = expectAsmIdentifier(); } @@ -461,11 +573,10 @@ VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::AssemblyAssign); varDecl.value = make_unique(parseExpression()); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - varDecl.debugData = updateLocationEndFrom(varDecl.debugData, locationOf(*varDecl.value)); + updateLocationEndFrom(varDecl.debugData, nativeLocationOf(*varDecl.value)); } - else if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - varDecl.debugData = updateLocationEndFrom(varDecl.debugData, varDecl.variables.back().debugData->location); + else + updateLocationEndFrom(varDecl.debugData, nativeLocationOf(varDecl.variables.back())); return varDecl; } @@ -511,8 +622,7 @@ FunctionDefinition Parser::parseFunctionDefinition() m_insideFunction = true; funDef.body = parseBlock(); m_insideFunction = preInsideFunction; - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - funDef.debugData = updateLocationEndFrom(funDef.debugData, funDef.body.debugData->location); + updateLocationEndFrom(funDef.debugData, nativeLocationOf(funDef.body)); m_currentForLoopComponent = outerForLoopComponent; return funDef; @@ -539,8 +649,7 @@ FunctionCall Parser::parseCall(variant&& _initialOp) ret.arguments.emplace_back(parseExpression()); } } - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - ret.debugData = updateLocationEndFrom(ret.debugData, currentLocation()); + updateLocationEndFrom(ret.debugData, currentLocation()); expectToken(Token::RParen); return ret; } @@ -553,8 +662,7 @@ TypedName Parser::parseTypedName() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - typedName.debugData = updateLocationEndFrom(typedName.debugData, currentLocation()); + updateLocationEndFrom(typedName.debugData, currentLocation()); typedName.type = expectAsmIdentifier(); } else diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index da6eb7226..da4f09ed2 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace solidity::yul { @@ -60,7 +61,6 @@ public: ParserBase(_errorReporter), m_dialect(_dialect), m_locationOverride{_locationOverride ? *_locationOverride : langutil::SourceLocation{}}, - m_debugDataOverride{}, m_useSourceLocationFrom{ _locationOverride ? UseSourceLocationFrom::LocationOverride : @@ -68,8 +68,8 @@ public: } {} - /// Constructs a Yul parser that is using the source locations - /// from the comments (via @src). + /// Constructs a Yul parser that is using the debug data + /// from the comments (via @src and other tags). explicit Parser( langutil::ErrorReporter& _errorReporter, Dialect const& _dialect, @@ -78,7 +78,6 @@ public: ParserBase(_errorReporter), m_dialect(_dialect), m_sourceNames{std::move(_sourceNames)}, - m_debugDataOverride{DebugData::create()}, m_useSourceLocationFrom{ m_sourceNames.has_value() ? UseSourceLocationFrom::Comments : @@ -98,18 +97,34 @@ public: protected: langutil::SourceLocation currentLocation() const override { - if (m_useSourceLocationFrom == UseSourceLocationFrom::Scanner) - return ParserBase::currentLocation(); - return m_locationOverride; + if (m_useSourceLocationFrom == UseSourceLocationFrom::LocationOverride) + return m_locationOverride; + + return ParserBase::currentLocation(); } langutil::Token advance() override; - void fetchSourceLocationFromComment(); + void fetchDebugDataFromComment(); + + std::optional> parseSrcComment( + std::string_view _arguments, + langutil::SourceLocation const& _commentLocation + ); + + std::optional>> parseASTIDComment( + std::string_view _arguments, + langutil::SourceLocation const& _commentLocation + ); /// Creates a DebugData object with the correct source location set. std::shared_ptr createDebugData() const; + void updateLocationEndFrom( + std::shared_ptr& _debugData, + langutil::SourceLocation const& _location + ) const; + /// Creates an inline assembly node with the current source location. template T createWithLocation() const { @@ -143,7 +158,8 @@ private: std::optional>> m_sourceNames; langutil::SourceLocation m_locationOverride; - std::shared_ptr m_debugDataOverride; + langutil::SourceLocation m_locationFromComment; + std::optional m_astIDFromComment; UseSourceLocationFrom m_useSourceLocationFrom = UseSourceLocationFrom::Scanner; ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None; bool m_insideFunction = false; diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index 2a7b705f8..68f21bcd7 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -38,12 +39,13 @@ using namespace std; using namespace solidity; +using namespace solidity::langutil; using namespace solidity::util; using namespace solidity::yul; string AsmPrinter::operator()(Literal const& _literal) { - string const locationComment = formatSourceLocationComment(_literal); + string const locationComment = formatDebugData(_literal); switch (_literal.kind) { @@ -63,19 +65,19 @@ string AsmPrinter::operator()(Literal const& _literal) string AsmPrinter::operator()(Identifier const& _identifier) { yulAssert(!_identifier.name.empty(), "Invalid identifier."); - return formatSourceLocationComment(_identifier) + _identifier.name.str(); + return formatDebugData(_identifier) + _identifier.name.str(); } string AsmPrinter::operator()(ExpressionStatement const& _statement) { - string const locationComment = formatSourceLocationComment(_statement); + string const locationComment = formatDebugData(_statement); return locationComment + std::visit(*this, _statement.expression); } string AsmPrinter::operator()(Assignment const& _assignment) { - string const locationComment = formatSourceLocationComment(_assignment); + string const locationComment = formatDebugData(_assignment); yulAssert(_assignment.variableNames.size() >= 1, ""); string variables = (*this)(_assignment.variableNames.front()); @@ -87,7 +89,7 @@ string AsmPrinter::operator()(Assignment const& _assignment) string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) { - string out = formatSourceLocationComment(_variableDeclaration); + string out = formatDebugData(_variableDeclaration); out += "let "; out += boost::algorithm::join( @@ -108,7 +110,7 @@ string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) { yulAssert(!_functionDefinition.name.empty(), "Invalid function name."); - string out = formatSourceLocationComment(_functionDefinition); + string out = formatDebugData(_functionDefinition); out += "function " + _functionDefinition.name.str() + "("; out += boost::algorithm::join( _functionDefinition.parameters | ranges::views::transform( @@ -133,7 +135,7 @@ string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) string AsmPrinter::operator()(FunctionCall const& _functionCall) { - string const locationComment = formatSourceLocationComment(_functionCall); + string const locationComment = formatDebugData(_functionCall); string const functionName = (*this)(_functionCall.functionName); return locationComment + @@ -148,7 +150,7 @@ string AsmPrinter::operator()(If const& _if) { yulAssert(_if.condition, "Invalid if condition."); - string out = formatSourceLocationComment(_if); + string out = formatDebugData(_if); out += "if " + std::visit(*this, *_if.condition); string body = (*this)(_if.body); @@ -163,7 +165,7 @@ string AsmPrinter::operator()(Switch const& _switch) { yulAssert(_switch.expression, "Invalid expression pointer."); - string out = formatSourceLocationComment(_switch); + string out = formatDebugData(_switch); out += "switch " + std::visit(*this, *_switch.expression); for (auto const& _case: _switch.cases) @@ -180,7 +182,7 @@ string AsmPrinter::operator()(Switch const& _switch) string AsmPrinter::operator()(ForLoop const& _forLoop) { yulAssert(_forLoop.condition, "Invalid for loop condition."); - string const locationComment = formatSourceLocationComment(_forLoop); + string const locationComment = formatDebugData(_forLoop); string pre = (*this)(_forLoop.pre); string condition = std::visit(*this, *_forLoop.condition); @@ -201,23 +203,23 @@ string AsmPrinter::operator()(ForLoop const& _forLoop) string AsmPrinter::operator()(Break const& _break) { - return formatSourceLocationComment(_break) + "break"; + return formatDebugData(_break) + "break"; } string AsmPrinter::operator()(Continue const& _continue) { - return formatSourceLocationComment(_continue) + "continue"; + return formatDebugData(_continue) + "continue"; } // '_leave' and '__leave' is reserved in VisualStudio string AsmPrinter::operator()(Leave const& leave_) { - return formatSourceLocationComment(leave_) + "leave"; + return formatDebugData(leave_) + "leave"; } string AsmPrinter::operator()(Block const& _block) { - string const locationComment = formatSourceLocationComment(_block); + string const locationComment = formatDebugData(_block); if (_block.statements.empty()) return locationComment + "{ }"; @@ -237,7 +239,7 @@ string AsmPrinter::operator()(Block const& _block) string AsmPrinter::formatTypedName(TypedName _variable) { yulAssert(!_variable.name.empty(), "Invalid variable name."); - return formatSourceLocationComment(_variable) + _variable.name.str() + appendTypeName(_variable.type); + return formatDebugData(_variable) + _variable.name.str() + appendTypeName(_variable.type); } string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const @@ -256,30 +258,73 @@ string AsmPrinter::appendTypeName(YulString _type, bool _isBoolLiteral) const return ":" + _type.str(); } -string AsmPrinter::formatSourceLocationComment(shared_ptr const& _debugData, bool _statement) +string AsmPrinter::formatSourceLocation( + SourceLocation const& _location, + map const& _nameToSourceIndex, + CharStreamProvider const* _soliditySourceProvider +) { - if ( - !_debugData || - m_lastLocation == _debugData->location || - m_nameToSourceIndex.empty() - ) - return ""; - - m_lastLocation = _debugData->location; + yulAssert(!_nameToSourceIndex.empty(), ""); string sourceIndex = "-1"; - if (_debugData->location.sourceName) - sourceIndex = to_string(m_nameToSourceIndex.at(*_debugData->location.sourceName)); + string solidityCodeSnippet = ""; + if (_location.sourceName) + { + sourceIndex = to_string(_nameToSourceIndex.at(*_location.sourceName)); + + if (_soliditySourceProvider) + { + solidityCodeSnippet = escapeAndQuoteString( + _soliditySourceProvider->charStream(*_location.sourceName).singleLineSnippet(_location) + ); + + // On top of escaping quotes we also escape the slash inside any `*/` to guard against + // it prematurely terminating multi-line comment blocks. We do not escape all slashes + // because the ones without `*` are not dangerous and ignoring them reduces visual noise. + boost::replace_all(solidityCodeSnippet, "*/", "*\\/"); + } + } string sourceLocation = "@src " + sourceIndex + ":" + - to_string(_debugData->location.start) + + to_string(_location.start) + ":" + - to_string(_debugData->location.end); - return - _statement ? - "/// " + sourceLocation + "\n" : - "/** " + sourceLocation + " */ "; + to_string(_location.end); + + return sourceLocation + (solidityCodeSnippet.empty() ? "" : " ") + solidityCodeSnippet; +} + +string AsmPrinter::formatDebugData(shared_ptr const& _debugData, bool _statement) +{ + if (!_debugData) + return ""; + + vector items; + if (auto id = _debugData->astID) + items.emplace_back("@ast-id " + to_string(*id)); + + if ( + m_lastLocation != _debugData->originLocation && + !m_nameToSourceIndex.empty() + ) + { + m_lastLocation = _debugData->originLocation; + + items.emplace_back(formatSourceLocation( + _debugData->originLocation, + m_nameToSourceIndex, + m_soliditySourceProvider + )); + } + + string commentBody = joinHumanReadable(items, " "); + if (commentBody.empty()) + return ""; + else + return + _statement ? + "/// " + commentBody + "\n" : + "/** " + commentBody + " */ "; } diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 940f663b4..3bb683191 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -28,6 +28,7 @@ #include +#include #include #include @@ -46,9 +47,11 @@ class AsmPrinter public: explicit AsmPrinter( Dialect const* _dialect = nullptr, - std::optional>> _sourceIndexToName = {} + std::optional>> _sourceIndexToName = {}, + langutil::CharStreamProvider const* _soliditySourceProvider = nullptr ): - m_dialect(_dialect) + m_dialect(_dialect), + m_soliditySourceProvider(_soliditySourceProvider) { if (_sourceIndexToName) for (auto&& [index, name]: *_sourceIndexToName) @@ -58,8 +61,9 @@ public: explicit AsmPrinter( Dialect const& _dialect, - std::optional>> _sourceIndexToName = {} - ): AsmPrinter(&_dialect, _sourceIndexToName) {} + std::optional>> _sourceIndexToName = {}, + langutil::CharStreamProvider const* _soliditySourceProvider = nullptr + ): AsmPrinter(&_dialect, _sourceIndexToName, _soliditySourceProvider) {} std::string operator()(Literal const& _literal); std::string operator()(Identifier const& _identifier); @@ -76,20 +80,27 @@ public: std::string operator()(Leave const& _continue); std::string operator()(Block const& _block); + static std::string formatSourceLocation( + langutil::SourceLocation const& _location, + std::map const& _nameToSourceIndex, + langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr + ); + private: std::string formatTypedName(TypedName _variable); std::string appendTypeName(YulString _type, bool _isBoolLiteral = false) const; - std::string formatSourceLocationComment(std::shared_ptr const& _debugData, bool _statement); + std::string formatDebugData(std::shared_ptr const& _debugData, bool _statement); template - std::string formatSourceLocationComment(T const& _node) + std::string formatDebugData(T const& _node) { bool isExpression = std::is_constructible::value; - return formatSourceLocationComment(_node.debugData, !isExpression); + return formatDebugData(_node.debugData, !isExpression); } Dialect const* const m_dialect = nullptr; - std::map m_nameToSourceIndex; + std::map m_nameToSourceIndex; langutil::SourceLocation m_lastLocation = {}; + langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; }; } diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 8a00608cf..1ed55a2d6 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -314,11 +314,11 @@ AssemblyStack::assembleEVMWithDeployed(optional _deployName) const return {make_shared(assembly), {}}; } -string AssemblyStack::print() const +string AssemblyStack::print(CharStreamProvider const* _soliditySourceProvider) const { yulAssert(m_parserResult, ""); yulAssert(m_parserResult->code, ""); - return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion)) + "\n"; + return m_parserResult->toString(&languageToDialect(m_language, m_evmVersion), _soliditySourceProvider) + "\n"; } shared_ptr AssemblyStack::parserResult() const diff --git a/libyul/AssemblyStack.h b/libyul/AssemblyStack.h index 3fcd15998..91ca78af7 100644 --- a/libyul/AssemblyStack.h +++ b/libyul/AssemblyStack.h @@ -116,7 +116,7 @@ public: langutil::ErrorList const& errors() const { return m_errors; } /// Pretty-print the input after having parsed it. - std::string print() const; + std::string print(langutil::CharStreamProvider const* _soliditySourceProvider = nullptr) const; /// Return the parsed and analyzed object. std::shared_ptr parserResult() const; diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 5e816d988..2cc5578e8 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -68,6 +68,11 @@ add_library(yul backends/evm/EVMMetrics.h backends/evm/NoOutputAssembly.h backends/evm/NoOutputAssembly.cpp + backends/evm/OptimizedEVMCodeTransform.cpp + backends/evm/OptimizedEVMCodeTransform.h + backends/evm/StackHelpers.h + backends/evm/StackLayoutGenerator.cpp + backends/evm/StackLayoutGenerator.h backends/evm/VariableReferenceCounter.h backends/evm/VariableReferenceCounter.cpp backends/wasm/EVMToEwasmTranslator.cpp diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h index 3be61bd18..1ad7785e9 100644 --- a/libyul/Exceptions.h +++ b/libyul/Exceptions.h @@ -26,6 +26,10 @@ #include +#include +#include +#include + namespace solidity::yul { @@ -52,7 +56,21 @@ struct StackTooDeepError: virtual YulException }; /// Assertion that throws an YulAssertion containing the given description if it is not met. -#define yulAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::solidity::yul::YulAssertion, DESCRIPTION) +#if !BOOST_PP_VARIADICS_MSVC +#define yulAssert(...) BOOST_PP_OVERLOAD(yulAssert_,__VA_ARGS__)(__VA_ARGS__) +#else +#define yulAssert(...) BOOST_PP_CAT(BOOST_PP_OVERLOAD(yulAssert_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY()) +#endif + +#define yulAssert_1(CONDITION) \ + yulAssert_2(CONDITION, "") + +#define yulAssert_2(CONDITION, DESCRIPTION) \ + assertThrowWithDefaultDescription( \ + CONDITION, \ + ::solidity::yul::YulAssertion, \ + DESCRIPTION, \ + "Yul assertion failed" \ + ) } diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 05ad9cb82..eee016aa4 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -35,8 +35,9 @@ using namespace std; using namespace solidity; -using namespace solidity::yul; +using namespace solidity::langutil; using namespace solidity::util; +using namespace solidity::yul; namespace { @@ -50,38 +51,35 @@ string indent(std::string const& _input) } -string Data::toString(Dialect const*, optional) const +string Data::toString(Dialect const*, CharStreamProvider const*) const { return "data \"" + name.str() + "\" hex\"" + util::toHex(data) + "\""; } -string Object::toString(Dialect const* _dialect) const +string Object::toString( + Dialect const* _dialect, + CharStreamProvider const* _soliditySourceProvider +) const { + yulAssert(code, "No code"); + yulAssert(debugData, "No debug data"); + string useSrcComment; - if (debugData && debugData->sourceNames) + if (debugData->sourceNames) useSrcComment = "/// @use-src " + joinHumanReadable(ranges::views::transform(*debugData->sourceNames, [](auto&& _pair) { return to_string(_pair.first) + ":" + util::escapeAndQuoteString(*_pair.second); })) + "\n"; - return useSrcComment + toString(_dialect, debugData ? debugData->sourceNames : optional{}); -} -string Object::toString(Dialect const* _dialect, std::optional _sourceNames) const -{ - yulAssert(code, "No code"); - string inner = "code " + AsmPrinter{_dialect, _sourceNames}(*code); + string inner = "code " + AsmPrinter{_dialect, debugData->sourceNames, _soliditySourceProvider}(*code); for (auto const& obj: subObjects) - { - if (auto const* o = dynamic_cast(obj.get())) - yulAssert(!o->debugData || !o->debugData->sourceNames, ""); - inner += "\n" + obj->toString(_dialect, _sourceNames); - } + inner += "\n" + obj->toString(_dialect, _soliditySourceProvider); - return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; + return useSrcComment + "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; } set Object::qualifiedDataNames() const diff --git a/libyul/Object.h b/libyul/Object.h index 9b7519b1e..1b08821ce 100644 --- a/libyul/Object.h +++ b/libyul/Object.h @@ -24,10 +24,13 @@ #include #include +#include + #include #include #include +#include namespace solidity::yul { @@ -49,11 +52,10 @@ struct ObjectNode /// Name of the object. /// Can be empty since .yul files can also just contain code, without explicitly placing it in an object. YulString name; -protected: - virtual std::string toString(Dialect const* _dialect, std::optional _sourceNames) const = 0; - - /// Object should have access to toString - friend struct Object; + virtual std::string toString( + Dialect const* _dialect, + langutil::CharStreamProvider const* _soliditySourceProvider + ) const = 0; }; /** @@ -65,8 +67,10 @@ struct Data: public ObjectNode bytes data; -protected: - std::string toString(Dialect const* _dialect, std::optional _sourceNames) const override; + std::string toString( + Dialect const* _dialect, + langutil::CharStreamProvider const* _soliditySourceProvider + ) const override; }; @@ -83,7 +87,10 @@ struct Object: public ObjectNode { public: /// @returns a (parseable) string representation. - std::string toString(Dialect const* _dialect) const; + std::string toString( + Dialect const* _dialect, + langutil::CharStreamProvider const* _soliditySourceProvider = nullptr + ) const; /// @returns the set of names of data objects accessible from within the code of /// this object, including the name of object itself @@ -114,8 +121,6 @@ public: /// @returns the name of the special metadata data object. static std::string metadataName() { return ".metadata"; } -protected: - std::string toString(Dialect const* _dialect, std::optional _sourceNames) const override; }; } diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 8ce5896cf..afd4b7d94 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -45,14 +45,15 @@ shared_ptr ObjectParser::parse(shared_ptr const& _scanner, bool { shared_ptr object; m_scanner = _scanner; - m_sourceNameMapping = tryParseSourceNameMapping(); if (currentToken() == Token::LBrace) { // Special case: Code-only form. object = make_shared(); object->name = "object"_yulstring; - object->code = parseBlock(); + auto sourceNameMapping = tryParseSourceNameMapping(); + object->debugData = make_shared(ObjectDebugData{sourceNameMapping}); + object->code = parseBlock(sourceNameMapping); if (!object->code) return nullptr; } @@ -60,7 +61,6 @@ shared_ptr ObjectParser::parse(shared_ptr const& _scanner, bool object = parseObject(); if (!_reuseScanner) expectToken(Token::EOS); - object->debugData = make_shared(ObjectDebugData{m_sourceNameMapping}); return object; } catch (FatalError const&) @@ -75,16 +75,20 @@ shared_ptr ObjectParser::parseObject(Object* _containingObject) { RecursionGuard guard(*this); + shared_ptr ret = make_shared(); + + auto sourceNameMapping = tryParseSourceNameMapping(); + ret->debugData = make_shared(ObjectDebugData{sourceNameMapping}); + if (currentToken() != Token::Identifier || currentLiteral() != "object") fatalParserError(4294_error, "Expected keyword \"object\"."); advance(); - shared_ptr ret = make_shared(); ret->name = parseUniqueName(_containingObject); expectToken(Token::LBrace); - ret->code = parseCode(); + ret->code = parseCode(move(sourceNameMapping)); while (currentToken() != Token::RBrace) { @@ -103,13 +107,13 @@ shared_ptr ObjectParser::parseObject(Object* _containingObject) return ret; } -shared_ptr ObjectParser::parseCode() +shared_ptr ObjectParser::parseCode(optional _sourceNames) { if (currentToken() != Token::Identifier || currentLiteral() != "code") fatalParserError(4846_error, "Expected keyword \"code\"."); advance(); - return parseBlock(); + return parseBlock(move(_sourceNames)); } optional ObjectParser::tryParseSourceNameMapping() const @@ -166,9 +170,9 @@ optional ObjectParser::tryParseSourceNameMapping() const return nullopt; } -shared_ptr ObjectParser::parseBlock() +shared_ptr ObjectParser::parseBlock(optional _sourceNames) { - Parser parser(m_errorReporter, m_dialect, m_sourceNameMapping); + Parser parser(m_errorReporter, m_dialect, move(_sourceNames)); shared_ptr block = parser.parseInline(m_scanner); yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); return block; diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h index 86a5ef77c..716a191ea 100644 --- a/libyul/ObjectParser.h +++ b/libyul/ObjectParser.h @@ -55,13 +55,11 @@ public: /// @returns an empty shared pointer on error. std::shared_ptr parse(std::shared_ptr const& _scanner, bool _reuseScanner); - std::optional const& sourceNameMapping() const noexcept { return m_sourceNameMapping; } - private: std::optional tryParseSourceNameMapping() const; std::shared_ptr parseObject(Object* _containingObject = nullptr); - std::shared_ptr parseCode(); - std::shared_ptr parseBlock(); + std::shared_ptr parseCode(std::optional _sourceNames); + std::shared_ptr parseBlock(std::optional _sourceNames); void parseData(Object& _containingObject); /// Tries to parse a name that is non-empty and unique inside the containing object. @@ -69,8 +67,6 @@ private: void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr _subObject); Dialect const& m_dialect; - - std::optional m_sourceNameMapping; }; } diff --git a/libyul/ScopeFiller.cpp b/libyul/ScopeFiller.cpp index 5c4599eda..8672d898f 100644 --- a/libyul/ScopeFiller.cpp +++ b/libyul/ScopeFiller.cpp @@ -53,7 +53,7 @@ bool ScopeFiller::operator()(ExpressionStatement const& _expr) bool ScopeFiller::operator()(VariableDeclaration const& _varDecl) { for (auto const& variable: _varDecl.variables) - if (!registerVariable(variable, _varDecl.debugData->location, *m_currentScope)) + if (!registerVariable(variable, nativeLocationOf(_varDecl), *m_currentScope)) return false; return true; } @@ -68,7 +68,7 @@ bool ScopeFiller::operator()(FunctionDefinition const& _funDef) bool success = true; for (auto const& var: _funDef.parameters + _funDef.returnVariables) - if (!registerVariable(var, _funDef.debugData->location, varScope)) + if (!registerVariable(var, nativeLocationOf(_funDef), varScope)) success = false; if (!(*this)(_funDef.body)) @@ -162,7 +162,7 @@ bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef) //@TODO secondary location m_errorReporter.declarationError( 6052_error, - _funDef.debugData->location, + nativeLocationOf(_funDef), "Function name " + _funDef.name.str() + " already taken in this scope." ); return false; diff --git a/libyul/Utilities.cpp b/libyul/Utilities.cpp index 38fe379aa..8db0e44ff 100644 --- a/libyul/Utilities.cpp +++ b/libyul/Utilities.cpp @@ -42,7 +42,7 @@ string solidity::yul::reindent(string const& _code) { int constexpr indentationWidth = 4; - auto const static countBraces = [](string const& _s) noexcept -> int + auto constexpr static countBraces = [](string const& _s) noexcept -> int { auto const i = _s.find("//"); auto const e = i == _s.npos ? end(_s) : next(begin(_s), static_cast(i)); diff --git a/libyul/Utilities.h b/libyul/Utilities.h index b9077eeae..e4d496cd4 100644 --- a/libyul/Utilities.h +++ b/libyul/Utilities.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include namespace solidity::yul diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index 9e1a28dab..414d7d115 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -27,6 +27,7 @@ #include #include +#include #include #include diff --git a/libyul/backends/evm/ConstantOptimiser.cpp b/libyul/backends/evm/ConstantOptimiser.cpp index 09d87da83..aecd00bf4 100644 --- a/libyul/backends/evm/ConstantOptimiser.cpp +++ b/libyul/backends/evm/ConstantOptimiser.cpp @@ -128,7 +128,7 @@ Representation const& RepresentationFinder::findRepresentation(u256 const& _valu Representation routine = represent(_value); - if (bytesRequired(~_value) < bytesRequired(_value)) + if (numberEncodingSize(~_value) < numberEncodingSize(_value)) // Negated is shorter to represent routine = min(move(routine), represent("not"_yulstring, findRepresentation(~_value))); diff --git a/libyul/backends/evm/ControlFlowGraph.h b/libyul/backends/evm/ControlFlowGraph.h index 60bec498b..6196697fc 100644 --- a/libyul/backends/evm/ControlFlowGraph.h +++ b/libyul/backends/evm/ControlFlowGraph.h @@ -24,8 +24,11 @@ #include #include #include +#include #include +#include + #include #include #include @@ -54,8 +57,19 @@ struct FunctionCallReturnLabelSlot /// the function. struct FunctionReturnLabelSlot { - bool operator==(FunctionReturnLabelSlot const&) const { return true; } - bool operator<(FunctionReturnLabelSlot const&) const { return false; } + std::reference_wrapper function; + bool operator==(FunctionReturnLabelSlot const& _rhs) const + { + // There can never be return label slots of different functions on stack simultaneously. + yulAssert(&function.get() == &_rhs.function.get(), ""); + return true; + } + bool operator<(FunctionReturnLabelSlot const& _rhs) const + { + // There can never be return label slots of different functions on stack simultaneously. + yulAssert(&function.get() == &_rhs.function.get(), ""); + return false; + } static constexpr bool canBeFreelyGenerated = false; }; /// A slot containing the current value of a particular variable. @@ -131,6 +145,9 @@ struct CFG std::shared_ptr debugData; std::reference_wrapper function; std::reference_wrapper functionCall; + /// True, if the call is recursive, i.e. entering the function involves a control flow path (potentially involving + /// more intermediate function calls) that leads back to this very call. + bool recursive = false; }; struct Assignment { @@ -157,18 +174,25 @@ struct CFG struct MainExit {}; struct ConditionalJump { + std::shared_ptr debugData; StackSlot condition; BasicBlock* nonZero = nullptr; BasicBlock* zero = nullptr; }; struct Jump { + std::shared_ptr debugData; BasicBlock* target = nullptr; /// The only backwards jumps are jumps from loop post to loop condition. bool backwards = false; }; - struct FunctionReturn { CFG::FunctionInfo* info = nullptr; }; + struct FunctionReturn + { + std::shared_ptr debugData; + CFG::FunctionInfo* info = nullptr; + }; struct Terminated {}; + std::shared_ptr debugData; std::vector entries; std::vector operations; std::variant exit = MainExit{}; @@ -201,9 +225,9 @@ struct CFG /// the switch case literals when transforming the control flow of a switch to a sequence of conditional jumps. std::list ghostCalls; - BasicBlock& makeBlock() + BasicBlock& makeBlock(std::shared_ptr _debugData) { - return blocks.emplace_back(BasicBlock{}); + return blocks.emplace_back(BasicBlock{move(_debugData), {}, {}}); } }; diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.cpp b/libyul/backends/evm/ControlFlowGraphBuilder.cpp index f3a1d5823..fd5b4682d 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.cpp +++ b/libyul/backends/evm/ControlFlowGraphBuilder.cpp @@ -47,22 +47,14 @@ using namespace solidity; using namespace solidity::yul; using namespace std; -std::unique_ptr ControlFlowGraphBuilder::build( - AsmAnalysisInfo const& _analysisInfo, - Dialect const& _dialect, - Block const& _block -) +namespace +{ +// Removes edges to blocks that are not reachable. +void cleanUnreachable(CFG& _cfg) { - auto result = std::make_unique(); - result->entry = &result->makeBlock(); - - ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect); - builder.m_currentBlock = result->entry; - builder(_block); - // Determine which blocks are reachable from the entry. - util::BreadthFirstSearch reachabilityCheck{{result->entry}}; - for (auto const& functionInfo: result->functionInfo | ranges::views::values) + util::BreadthFirstSearch reachabilityCheck{{_cfg.entry}}; + for (auto const& functionInfo: _cfg.functionInfo | ranges::views::values) reachabilityCheck.verticesToTraverse.emplace_back(functionInfo.entry); reachabilityCheck.run([&](CFG::BasicBlock* _node, auto&& _addChild) { @@ -85,6 +77,71 @@ std::unique_ptr ControlFlowGraphBuilder::build( cxx20::erase_if(node->entries, [&](CFG::BasicBlock* entry) -> bool { return !reachabilityCheck.visited.count(entry); }); +} +// Sets the ``recursive`` member to ``true`` for all recursive function calls. +void markRecursiveCalls(CFG& _cfg) +{ + map> callsPerBlock; + auto const& findCalls = [&](CFG::BasicBlock* _block) + { + if (auto* calls = util::valueOrNullptr(callsPerBlock, _block)) + return *calls; + vector& calls = callsPerBlock[_block]; + util::BreadthFirstSearch{{_block}}.run([&](CFG::BasicBlock* _block, auto _addChild) { + for (auto& operation: _block->operations) + if (auto* functionCall = get_if(&operation.operation)) + calls.emplace_back(functionCall); + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) {}, + [&](CFG::BasicBlock::Jump const& _jump) + { + _addChild(_jump.target); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + _addChild(_conditionalJump.zero); + _addChild(_conditionalJump.nonZero); + }, + [&](CFG::BasicBlock::FunctionReturn const&) {}, + [&](CFG::BasicBlock::Terminated const&) {}, + }, _block->exit); + }); + return calls; + }; + for (auto& functionInfo: _cfg.functionInfo | ranges::views::values) + for (CFG::FunctionCall* call: findCalls(functionInfo.entry)) + { + util::BreadthFirstSearch breadthFirstSearch{{call}}; + breadthFirstSearch.run([&](CFG::FunctionCall* _call, auto _addChild) { + auto& calledFunctionInfo = _cfg.functionInfo.at(&_call->function.get()); + if (&calledFunctionInfo == &functionInfo) + { + call->recursive = true; + breadthFirstSearch.abort(); + return; + } + for (CFG::FunctionCall* nestedCall: findCalls(_cfg.functionInfo.at(&_call->function.get()).entry)) + _addChild(nestedCall); + }); + } +} +} + +std::unique_ptr ControlFlowGraphBuilder::build( + AsmAnalysisInfo const& _analysisInfo, + Dialect const& _dialect, + Block const& _block +) +{ + auto result = std::make_unique(); + result->entry = &result->makeBlock(debugDataOf(_block)); + + ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect); + builder.m_currentBlock = result->entry; + builder(_block); + + cleanUnreachable(*result); + markRecursiveCalls(*result); // TODO: It might be worthwhile to run some further simplifications on the graph itself here. // E.g. if there is a jump to a node that has the jumping node as its only entry, the nodes can be fused, etc. @@ -176,7 +233,7 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt) if (builtin->controlFlowSideEffects.terminates) { m_currentBlock->exit = CFG::BasicBlock::Terminated{}; - m_currentBlock = &m_graph.makeBlock(); + m_currentBlock = &m_graph.makeBlock(debugDataOf(*m_currentBlock)); } } @@ -189,15 +246,19 @@ void ControlFlowGraphBuilder::operator()(Block const& _block) void ControlFlowGraphBuilder::operator()(If const& _if) { - auto&& [ifBranch, afterIf] = makeConditionalJump(std::visit(*this, *_if.condition)); - m_currentBlock = ifBranch; + auto& ifBranch = m_graph.makeBlock(debugDataOf(_if.body)); + auto& afterIf = m_graph.makeBlock(debugDataOf(*m_currentBlock)); + makeConditionalJump(debugDataOf(_if), std::visit(*this, *_if.condition), ifBranch, afterIf); + m_currentBlock = &ifBranch; (*this)(_if.body); - jump(*afterIf); + jump(debugDataOf(_if.body), afterIf); } void ControlFlowGraphBuilder::operator()(Switch const& _switch) { yulAssert(m_currentBlock, ""); + shared_ptr preSwitchDebugData = debugDataOf(_switch); + auto ghostVariableId = m_graph.ghostVariables.size(); YulString ghostVariableName("GHOST[" + to_string(ghostVariableId) + "]"); auto& ghostVar = m_graph.ghostVariables.emplace_back(Scope::Variable{""_yulstring, ghostVariableName}); @@ -216,43 +277,46 @@ void ControlFlowGraphBuilder::operator()(Switch const& _switch) // Artificially generate: // eq(, ) - auto makeValueCompare = [&](Literal const& _value) { + auto makeValueCompare = [&](Case const& _case) { yul::FunctionCall const& ghostCall = m_graph.ghostCalls.emplace_back(yul::FunctionCall{ - _value.debugData, + debugDataOf(_case), yul::Identifier{{}, "eq"_yulstring}, - {_value, Identifier{{}, ghostVariableName}} + {*_case.value, Identifier{{}, ghostVariableName}} }); CFG::Operation& operation = m_currentBlock->operations.emplace_back(CFG::Operation{ - Stack{ghostVarSlot, LiteralSlot{valueOfLiteral(_value), _value.debugData}}, + Stack{ghostVarSlot, LiteralSlot{valueOfLiteral(*_case.value), debugDataOf(*_case.value)}}, Stack{TemporarySlot{ghostCall, 0}}, - CFG::BuiltinCall{_switch.debugData, *equalityBuiltin, ghostCall, 2}, + CFG::BuiltinCall{debugDataOf(_case), *equalityBuiltin, ghostCall, 2}, }); return operation.output.front(); }; - CFG::BasicBlock& afterSwitch = m_graph.makeBlock(); + CFG::BasicBlock& afterSwitch = m_graph.makeBlock(preSwitchDebugData); yulAssert(!_switch.cases.empty(), ""); for (auto const& switchCase: _switch.cases | ranges::views::drop_last(1)) { yulAssert(switchCase.value, ""); - auto&& [caseBranch, elseBranch] = makeConditionalJump(makeValueCompare(*switchCase.value)); - m_currentBlock = caseBranch; + auto& caseBranch = m_graph.makeBlock(debugDataOf(switchCase.body)); + auto& elseBranch = m_graph.makeBlock(debugDataOf(_switch)); + makeConditionalJump(debugDataOf(switchCase), makeValueCompare(switchCase), caseBranch, elseBranch); + m_currentBlock = &caseBranch; (*this)(switchCase.body); - jump(afterSwitch); - m_currentBlock = elseBranch; + jump(debugDataOf(switchCase.body), afterSwitch); + m_currentBlock = &elseBranch; } Case const& switchCase = _switch.cases.back(); if (switchCase.value) { - CFG::BasicBlock& caseBranch = m_graph.makeBlock(); - makeConditionalJump(makeValueCompare(*switchCase.value), caseBranch, afterSwitch); + CFG::BasicBlock& caseBranch = m_graph.makeBlock(debugDataOf(switchCase.body)); + makeConditionalJump(debugDataOf(switchCase), makeValueCompare(switchCase), caseBranch, afterSwitch); m_currentBlock = &caseBranch; } (*this)(switchCase.body); - jump(afterSwitch); + jump(debugDataOf(switchCase.body), afterSwitch); } void ControlFlowGraphBuilder::operator()(ForLoop const& _loop) { + shared_ptr preLoopDebugData = debugDataOf(_loop); ScopedSaveAndRestore scopeRestore(m_scope, m_info.scopes.at(&_loop.pre).get()); (*this)(_loop.pre); @@ -260,10 +324,10 @@ void ControlFlowGraphBuilder::operator()(ForLoop const& _loop) if (auto const* literalCondition = get_if(_loop.condition.get())) constantCondition = valueOfLiteral(*literalCondition) != 0; - CFG::BasicBlock& loopCondition = m_graph.makeBlock(); - CFG::BasicBlock& loopBody = m_graph.makeBlock(); - CFG::BasicBlock& post = m_graph.makeBlock(); - CFG::BasicBlock& afterLoop = m_graph.makeBlock(); + CFG::BasicBlock& loopCondition = m_graph.makeBlock(debugDataOf(*_loop.condition)); + CFG::BasicBlock& loopBody = m_graph.makeBlock(debugDataOf(_loop.body)); + CFG::BasicBlock& post = m_graph.makeBlock(debugDataOf(_loop.post)); + CFG::BasicBlock& afterLoop = m_graph.makeBlock(preLoopDebugData); ScopedSaveAndRestore scopedSaveAndRestore(m_forLoopInfo, ForLoopInfo{afterLoop, post}); @@ -271,48 +335,49 @@ void ControlFlowGraphBuilder::operator()(ForLoop const& _loop) { if (*constantCondition) { - jump(loopBody); + jump(debugDataOf(_loop.pre), loopBody); (*this)(_loop.body); - jump(post); + jump(debugDataOf(_loop.body), post); (*this)(_loop.post); - jump(loopBody, true); + jump(debugDataOf(_loop.post), loopBody, true); } else - jump(afterLoop); + jump(debugDataOf(_loop.pre), afterLoop); } else { - jump(loopCondition); - makeConditionalJump(std::visit(*this, *_loop.condition), loopBody, afterLoop); + jump(debugDataOf(_loop.pre), loopCondition); + makeConditionalJump(debugDataOf(*_loop.condition), std::visit(*this, *_loop.condition), loopBody, afterLoop); m_currentBlock = &loopBody; (*this)(_loop.body); - jump(post); + jump(debugDataOf(_loop.body), post); (*this)(_loop.post); - jump(loopCondition, true); + jump(debugDataOf(_loop.post), loopCondition, true); } m_currentBlock = &afterLoop; } -void ControlFlowGraphBuilder::operator()(Break const&) +void ControlFlowGraphBuilder::operator()(Break const& _break) { yulAssert(m_forLoopInfo.has_value(), ""); - jump(m_forLoopInfo->afterLoop); - m_currentBlock = &m_graph.makeBlock(); + jump(debugDataOf(_break), m_forLoopInfo->afterLoop); + m_currentBlock = &m_graph.makeBlock(debugDataOf(*m_currentBlock)); } -void ControlFlowGraphBuilder::operator()(Continue const&) +void ControlFlowGraphBuilder::operator()(Continue const& _continue) { yulAssert(m_forLoopInfo.has_value(), ""); - jump(m_forLoopInfo->post); - m_currentBlock = &m_graph.makeBlock(); + jump(debugDataOf(_continue), m_forLoopInfo->post); + m_currentBlock = &m_graph.makeBlock(debugDataOf(*m_currentBlock)); } -void ControlFlowGraphBuilder::operator()(Leave const&) +// '_leave' and '__leave' are reserved in VisualStudio +void ControlFlowGraphBuilder::operator()(Leave const& leave_) { - yulAssert(m_currentFunctionExit.has_value(), ""); - m_currentBlock->exit = *m_currentFunctionExit; - m_currentBlock = &m_graph.makeBlock(); + yulAssert(m_currentFunction.has_value(), ""); + m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{debugDataOf(leave_), *m_currentFunction}; + m_currentBlock = &m_graph.makeBlock(debugDataOf(*m_currentBlock)); } void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function) @@ -329,7 +394,7 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function) auto&& [it, inserted] = m_graph.functionInfo.emplace(std::make_pair(&function, CFG::FunctionInfo{ _function.debugData, function, - &m_graph.makeBlock(), + &m_graph.makeBlock(debugDataOf(_function.body)), _function.parameters | ranges::views::transform([&](auto const& _param) { return VariableSlot{ std::get(virtualFunctionScope->identifiers.at(_param.name)), @@ -347,10 +412,10 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function) CFG::FunctionInfo& functionInfo = it->second; ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect}; - builder.m_currentFunctionExit = CFG::BasicBlock::FunctionReturn{&functionInfo}; + builder.m_currentFunction = &functionInfo; builder.m_currentBlock = functionInfo.entry; builder(_function.body); - builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{&functionInfo}; + builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{debugDataOf(_function), &functionInfo}; } @@ -440,18 +505,16 @@ Scope::Variable const& ControlFlowGraphBuilder::lookupVariable(YulString _name) yulAssert(false, "External identifier access unimplemented."); } -std::pair ControlFlowGraphBuilder::makeConditionalJump(StackSlot _condition) -{ - CFG::BasicBlock& nonZero = m_graph.makeBlock(); - CFG::BasicBlock& zero = m_graph.makeBlock(); - makeConditionalJump(move(_condition), nonZero, zero); - return {&nonZero, &zero}; -} - -void ControlFlowGraphBuilder::makeConditionalJump(StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero) +void ControlFlowGraphBuilder::makeConditionalJump( + shared_ptr _debugData, + StackSlot _condition, + CFG::BasicBlock& _nonZero, + CFG::BasicBlock& _zero +) { yulAssert(m_currentBlock, ""); m_currentBlock->exit = CFG::BasicBlock::ConditionalJump{ + move(_debugData), move(_condition), &_nonZero, &_zero @@ -461,10 +524,14 @@ void ControlFlowGraphBuilder::makeConditionalJump(StackSlot _condition, CFG::Bas m_currentBlock = nullptr; } -void ControlFlowGraphBuilder::jump(CFG::BasicBlock& _target, bool backwards) +void ControlFlowGraphBuilder::jump( + shared_ptr _debugData, + CFG::BasicBlock& _target, + bool backwards +) { yulAssert(m_currentBlock, ""); - m_currentBlock->exit = CFG::BasicBlock::Jump{&_target, backwards}; + m_currentBlock->exit = CFG::BasicBlock::Jump{move(_debugData), &_target, backwards}; _target.entries.emplace_back(m_currentBlock); m_currentBlock = &_target; } diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.h b/libyul/backends/evm/ControlFlowGraphBuilder.h index 2a70a9472..f99a879f6 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.h +++ b/libyul/backends/evm/ControlFlowGraphBuilder.h @@ -62,13 +62,18 @@ private: Scope::Function const& lookupFunction(YulString _name) const; Scope::Variable const& lookupVariable(YulString _name) const; - /// @returns a pair of newly created blocks, the first element being the non-zero branch, the second element the - /// zero branch. /// Resets m_currentBlock to enforce a subsequent explicit reassignment. - std::pair makeConditionalJump(StackSlot _condition); - /// Resets m_currentBlock to enforce a subsequent explicit reassignment. - void makeConditionalJump(StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero); - void jump(CFG::BasicBlock& _target, bool _backwards = false); + void makeConditionalJump( + std::shared_ptr _debugData, + StackSlot _condition, + CFG::BasicBlock& _nonZero, + CFG::BasicBlock& _zero + ); + void jump( + std::shared_ptr _debugData, + CFG::BasicBlock& _target, + bool _backwards = false + ); CFG& m_graph; AsmAnalysisInfo const& m_info; Dialect const& m_dialect; @@ -80,7 +85,7 @@ private: std::reference_wrapper post; }; std::optional m_forLoopInfo; - std::optional m_currentFunctionExit; + std::optional m_currentFunction; }; } diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index c837c8b1a..38eecc245 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -44,16 +44,6 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::util; -namespace -{ - -langutil::SourceLocation extractSourceLocationFromDebugData(shared_ptr const& _debugData) -{ - return _debugData ? _debugData->location : langutil::SourceLocation{}; -} - -} - CodeTransform::CodeTransform( AbstractAssembly& _assembly, AsmAnalysisInfo& _analysisInfo, @@ -155,13 +145,13 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) } else { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_varDecl.debugData)); + m_assembly.setSourceLocation(originLocationOf(_varDecl)); size_t variablesLeft = numVariables; while (variablesLeft--) m_assembly.appendConstant(u256(0)); } - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_varDecl.debugData)); + m_assembly.setSourceLocation(originLocationOf(_varDecl)); bool atTopOfStack = true; for (size_t varIndex = 0; varIndex < numVariables; ++varIndex) { @@ -223,13 +213,13 @@ void CodeTransform::operator()(Assignment const& _assignment) std::visit(*this, *_assignment.value); expectDeposit(static_cast(_assignment.variableNames.size()), height); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_assignment.debugData)); + m_assembly.setSourceLocation(originLocationOf(_assignment)); generateMultiAssignment(_assignment.variableNames); } void CodeTransform::operator()(ExpressionStatement const& _statement) { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_statement.debugData)); + m_assembly.setSourceLocation(originLocationOf(_statement)); std::visit(*this, _statement.expression); } @@ -237,11 +227,15 @@ void CodeTransform::operator()(FunctionCall const& _call) { yulAssert(m_scope, ""); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); + m_assembly.setSourceLocation(originLocationOf(_call)); if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name)) - builtin->generateCode(_call, m_assembly, m_builtinContext, [&](Expression const& _expression) { - visitExpression(_expression); - }); + { + for (auto&& [i, arg]: _call.arguments | ranges::views::enumerate | ranges::views::reverse) + if (!builtin->literalArgument(i)) + visitExpression(arg); + m_assembly.setSourceLocation(originLocationOf(_call)); + builtin->generateCode(_call, m_assembly, m_builtinContext); + } else { AbstractAssembly::LabelID returnLabel = m_assembly.newLabelId(); @@ -256,9 +250,9 @@ void CodeTransform::operator()(FunctionCall const& _call) yulAssert(function->arguments.size() == _call.arguments.size(), ""); for (auto const& arg: _call.arguments | ranges::views::reverse) visitExpression(arg); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); + m_assembly.setSourceLocation(originLocationOf(_call)); m_assembly.appendJumpTo( - functionEntryID(_call.functionName.name, *function), + functionEntryID(*function), static_cast(function->returns.size() - function->arguments.size()) - 1, AbstractAssembly::JumpType::IntoFunction ); @@ -268,7 +262,7 @@ void CodeTransform::operator()(FunctionCall const& _call) void CodeTransform::operator()(Identifier const& _identifier) { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_identifier.debugData)); + m_assembly.setSourceLocation(originLocationOf(_identifier)); // First search internals, then externals. yulAssert(m_scope, ""); if (m_scope->lookup(_identifier.name, GenericVisitor{ @@ -300,19 +294,19 @@ void CodeTransform::operator()(Identifier const& _identifier) void CodeTransform::operator()(Literal const& _literal) { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_literal.debugData)); + m_assembly.setSourceLocation(originLocationOf(_literal)); m_assembly.appendConstant(valueOfLiteral(_literal)); } void CodeTransform::operator()(If const& _if) { visitExpression(*_if.condition); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_if.debugData)); + m_assembly.setSourceLocation(originLocationOf(_if)); m_assembly.appendInstruction(evmasm::Instruction::ISZERO); AbstractAssembly::LabelID end = m_assembly.newLabelId(); m_assembly.appendJumpToIf(end); (*this)(_if.body); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_if.debugData)); + m_assembly.setSourceLocation(originLocationOf(_if)); m_assembly.appendLabel(end); } @@ -327,7 +321,7 @@ void CodeTransform::operator()(Switch const& _switch) if (c.value) { (*this)(*c.value); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(c.debugData)); + m_assembly.setSourceLocation(originLocationOf(c)); AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId(); caseBodies[&c] = bodyLabel; yulAssert(m_assembly.stackHeight() == expressionHeight + 1, ""); @@ -339,24 +333,24 @@ void CodeTransform::operator()(Switch const& _switch) // default case (*this)(c.body); } - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_switch.debugData)); + m_assembly.setSourceLocation(originLocationOf(_switch)); m_assembly.appendJumpTo(end); size_t numCases = caseBodies.size(); for (auto const& c: caseBodies) { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(c.first->debugData)); + m_assembly.setSourceLocation(originLocationOf(*c.first)); m_assembly.appendLabel(c.second); (*this)(c.first->body); // Avoid useless "jump to next" for the last case. if (--numCases > 0) { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(c.first->debugData)); + m_assembly.setSourceLocation(originLocationOf(*c.first)); m_assembly.appendJumpTo(end); } } - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_switch.debugData)); + m_assembly.setSourceLocation(originLocationOf(_switch)); m_assembly.appendLabel(end); m_assembly.appendInstruction(evmasm::Instruction::POP); } @@ -377,10 +371,10 @@ void CodeTransform::operator()(FunctionDefinition const& _function) m_context->variableStackHeights[&var] = height++; } - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_function.debugData)); + m_assembly.setSourceLocation(originLocationOf(_function)); int const stackHeightBefore = m_assembly.stackHeight(); - m_assembly.appendLabel(functionEntryID(_function.name, function)); + m_assembly.appendLabel(functionEntryID(function)); m_assembly.setStackHeight(static_cast(height)); @@ -413,7 +407,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) subTransform(_function.body); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_function.debugData)); + m_assembly.setSourceLocation(originLocationOf(_function)); if (!subTransform.m_stackErrors.empty()) { m_assembly.markAsInvalid(); @@ -504,11 +498,11 @@ void CodeTransform::operator()(ForLoop const& _forLoop) AbstractAssembly::LabelID postPart = m_assembly.newLabelId(); AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId(); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); + m_assembly.setSourceLocation(originLocationOf(_forLoop)); m_assembly.appendLabel(loopStart); visitExpression(*_forLoop.condition); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); + m_assembly.setSourceLocation(originLocationOf(_forLoop)); m_assembly.appendInstruction(evmasm::Instruction::ISZERO); m_assembly.appendJumpToIf(loopEnd); @@ -516,12 +510,12 @@ void CodeTransform::operator()(ForLoop const& _forLoop) m_context->forLoopStack.emplace(Context::ForLoopLabels{ {postPart, stackHeightBody}, {loopEnd, stackHeightBody} }); (*this)(_forLoop.body); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); + m_assembly.setSourceLocation(originLocationOf(_forLoop)); m_assembly.appendLabel(postPart); (*this)(_forLoop.post); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); + m_assembly.setSourceLocation(originLocationOf(_forLoop)); m_assembly.appendJumpTo(loopStart); m_assembly.appendLabel(loopEnd); @@ -541,7 +535,7 @@ int CodeTransform::appendPopUntil(int _targetDepth) void CodeTransform::operator()(Break const& _break) { yulAssert(!m_context->forLoopStack.empty(), "Invalid break-statement. Requires surrounding for-loop in code generation."); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_break.debugData)); + m_assembly.setSourceLocation(originLocationOf(_break)); Context::JumpInfo const& jump = m_context->forLoopStack.top().done; m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); @@ -550,7 +544,7 @@ void CodeTransform::operator()(Break const& _break) void CodeTransform::operator()(Continue const& _continue) { yulAssert(!m_context->forLoopStack.empty(), "Invalid continue-statement. Requires surrounding for-loop in code generation."); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_continue.debugData)); + m_assembly.setSourceLocation(originLocationOf(_continue)); Context::JumpInfo const& jump = m_context->forLoopStack.top().post; m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); @@ -560,7 +554,7 @@ void CodeTransform::operator()(Leave const& _leaveStatement) { yulAssert(m_functionExitLabel, "Invalid leave-statement. Requires surrounding function in code generation."); yulAssert(m_functionExitStackHeight, ""); - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_leaveStatement.debugData)); + m_assembly.setSourceLocation(originLocationOf(_leaveStatement)); m_assembly.appendJumpTo(*m_functionExitLabel, appendPopUntil(*m_functionExitStackHeight)); } @@ -569,6 +563,10 @@ void CodeTransform::operator()(Block const& _block) Scope* originalScope = m_scope; m_scope = m_info.scopes.at(&_block).get(); + for (auto const& statement: _block.statements) + if (auto function = get_if(&statement)) + createFunctionEntryID(*function); + int blockStartStackHeight = m_assembly.stackHeight(); visitStatements(_block.statements); @@ -578,17 +576,30 @@ void CodeTransform::operator()(Block const& _block) m_scope = originalScope; } -AbstractAssembly::LabelID CodeTransform::functionEntryID(YulString _name, Scope::Function const& _function) +void CodeTransform::createFunctionEntryID(FunctionDefinition const& _function) { - if (!m_context->functionEntryIDs.count(&_function)) - { - AbstractAssembly::LabelID id = - m_useNamedLabelsForFunctions ? - m_assembly.namedLabel(_name.str(), _function.arguments.size(), _function.returns.size(), {}) : - m_assembly.newLabelId(); - m_context->functionEntryIDs[&_function] = id; - } - return m_context->functionEntryIDs[&_function]; + Scope::Function& scopeFunction = std::get(m_scope->identifiers.at(_function.name)); + yulAssert(!m_context->functionEntryIDs.count(&scopeFunction), ""); + + optional astID; + if (_function.debugData) + astID = _function.debugData->astID; + + m_context->functionEntryIDs[&scopeFunction] = + m_useNamedLabelsForFunctions ? + m_assembly.namedLabel( + _function.name.str(), + _function.parameters.size(), + _function.returnVariables.size(), + astID + ) : + m_assembly.newLabelId(); +} + +AbstractAssembly::LabelID CodeTransform::functionEntryID(Scope::Function const& _scopeFunction) const +{ + yulAssert(m_context->functionEntryIDs.count(&_scopeFunction), ""); + return m_context->functionEntryIDs.at(&_scopeFunction); } void CodeTransform::visitExpression(Expression const& _expression) @@ -672,7 +683,7 @@ void CodeTransform::visitStatements(vector const& _statements) auto const* functionDefinition = std::get_if(&statement); if (functionDefinition && !jumpTarget) { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(functionDefinition->debugData)); + m_assembly.setSourceLocation(originLocationOf(*functionDefinition)); jumpTarget = m_assembly.newLabelId(); m_assembly.appendJumpTo(*jumpTarget, 0); } @@ -693,7 +704,7 @@ void CodeTransform::visitStatements(vector const& _statements) void CodeTransform::finalizeBlock(Block const& _block, optional blockStartStackHeight) { - m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_block.debugData)); + m_assembly.setSourceLocation(originLocationOf(_block)); freeUnusedVariables(); diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index 8bbd3b818..8a605b472 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -142,7 +142,8 @@ public: private: AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier); - AbstractAssembly::LabelID functionEntryID(YulString _name, Scope::Function const& _function); + void createFunctionEntryID(FunctionDefinition const& _function); + AbstractAssembly::LabelID functionEntryID(Scope::Function const& _scopeFunction) const; /// Generates code for an expression that is supposed to return a single value. void visitExpression(Expression const& _expression); diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 86202e378..c90784a03 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -26,8 +26,8 @@ #include #include #include +#include #include - #include #include @@ -46,19 +46,6 @@ using namespace solidity::util; namespace { -void visitArguments( - AbstractAssembly& _assembly, - FunctionCall const& _call, - function _visitExpression -) -{ - for (auto const& arg: _call.arguments | ranges::views::reverse) - _visitExpression(arg); - - _assembly.setSourceLocation(_call.debugData->location); -} - - pair createEVMFunction( string const& _name, evmasm::Instruction _instruction @@ -76,12 +63,10 @@ pair createEVMFunction( f.literalArguments.clear(); f.instruction = _instruction; f.generateCode = [_instruction]( - FunctionCall const& _call, + FunctionCall const&, AbstractAssembly& _assembly, - BuiltinContext&, - std::function _visitExpression + BuiltinContext& ) { - visitArguments(_assembly, _call, _visitExpression); _assembly.appendInstruction(_instruction); }; @@ -94,7 +79,7 @@ pair createFunction( size_t _returns, SideEffects _sideEffects, vector> _literalArguments, - std::function)> _generateCode + std::function _generateCode ) { yulAssert(_literalArguments.size() == _params || _literalArguments.empty(), ""); @@ -166,12 +151,10 @@ map createBuiltins(langutil::EVMVersion _evmVe builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, - BuiltinContext&, - function + BuiltinContext& ) { yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); - _assembly.setSourceLocation(_call.debugData->location); _assembly.appendLinkerSymbol(std::get(arg).value.str()); })); @@ -184,24 +167,24 @@ map createBuiltins(langutil::EVMVersion _evmVe []( FunctionCall const& _call, AbstractAssembly& _assembly, - BuiltinContext&, - function _visitExpression + BuiltinContext& ) { - visitArguments(_assembly, _call, _visitExpression); + yulAssert(_call.arguments.size() == 1, ""); + Literal const* literal = get_if(&_call.arguments.front()); + yulAssert(literal, ""); + _assembly.appendConstant(valueOfLiteral(*literal)); }) ); builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, - BuiltinContext& _context, - std::function const& + BuiltinContext& _context ) { yulAssert(_context.currentObject, "No object available."); yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); YulString dataName = std::get(arg).value; - _assembly.setSourceLocation(_call.debugData->location); if (_context.currentObject->name == dataName) _assembly.appendAssemblySize(); else @@ -217,14 +200,12 @@ map createBuiltins(langutil::EVMVersion _evmVe builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {LiteralKind::String}, []( FunctionCall const& _call, AbstractAssembly& _assembly, - BuiltinContext& _context, - std::function const& + BuiltinContext& _context ) { yulAssert(_context.currentObject, "No object available."); yulAssert(_call.arguments.size() == 1, ""); Expression const& arg = _call.arguments.front(); YulString dataName = std::get(arg).value; - _assembly.setSourceLocation(_call.debugData->location); if (_context.currentObject->name == dataName) _assembly.appendConstant(0); else @@ -244,12 +225,10 @@ map createBuiltins(langutil::EVMVersion _evmVe SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, {}, []( - FunctionCall const& _call, + FunctionCall const&, AbstractAssembly& _assembly, - BuiltinContext&, - std::function _visitExpression + BuiltinContext& ) { - visitArguments(_assembly, _call, _visitExpression); _assembly.appendInstruction(evmasm::Instruction::CODECOPY); } )); @@ -262,15 +241,10 @@ map createBuiltins(langutil::EVMVersion _evmVe []( FunctionCall const& _call, AbstractAssembly& _assembly, - BuiltinContext&, - std::function _visitExpression + BuiltinContext& ) { yulAssert(_call.arguments.size() == 3, ""); - - _visitExpression(_call.arguments[2]); YulString identifier = std::get(_call.arguments[1]).value; - _visitExpression(_call.arguments[0]); - _assembly.setSourceLocation(_call.debugData->location); _assembly.appendImmutableAssignment(identifier.str()); } )); @@ -283,11 +257,9 @@ map createBuiltins(langutil::EVMVersion _evmVe []( FunctionCall const& _call, AbstractAssembly& _assembly, - BuiltinContext&, - std::function + BuiltinContext& ) { yulAssert(_call.arguments.size() == 1, ""); - _assembly.setSourceLocation(_call.debugData->location); _assembly.appendImmutable(std::get(_call.arguments.front()).value.str()); } )); @@ -387,15 +359,11 @@ BuiltinFunctionForEVM const* EVMDialect::verbatimFunction(size_t _arguments, siz [=]( FunctionCall const& _call, AbstractAssembly& _assembly, - BuiltinContext&, - std::function _visitExpression + BuiltinContext& ) { yulAssert(_call.arguments.size() == (1 + _arguments), ""); - for (Expression const& arg: _call.arguments | ranges::views::tail | ranges::views::reverse) - _visitExpression(arg); Expression const& bytecode = _call.arguments.front(); - _assembly.setSourceLocation(_call.debugData->location); _assembly.appendVerbatim( asBytes(std::get(bytecode).value.str()), _arguments, @@ -456,24 +424,19 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA m_functions["popbool"_yulstring].name = "popbool"_yulstring; m_functions["popbool"_yulstring].parameters = {"bool"_yulstring}; m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, {}, []( - FunctionCall const& _call, - AbstractAssembly& _assembly, - BuiltinContext&, - std::function _visitExpression - ) { - visitArguments(_assembly, _call, _visitExpression); - })); + FunctionCall const&, + AbstractAssembly&, + BuiltinContext& + ) {})); m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring}; m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring}; m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, {}, []( - FunctionCall const& _call, + FunctionCall const&, AbstractAssembly& _assembly, - BuiltinContext&, - std::function _visitExpression + BuiltinContext& ) { // TODO this should use a Panic. // A value larger than 1 causes an invalid instruction. - visitArguments(_assembly, _call, _visitExpression); _assembly.appendConstant(2); _assembly.appendInstruction(evmasm::Instruction::DUP2); _assembly.appendInstruction(evmasm::Instruction::LT); diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index b79c558be..28649f3e5 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -52,9 +52,10 @@ struct BuiltinFunctionForEVM: public BuiltinFunction { std::optional instruction; /// Function to generate code for the given function call and append it to the abstract - /// assembly. The fourth parameter is called to visit (and generate code for) the given - /// argument. - std::function)> generateCode; + /// assembly. Expects all non-literal arguments of the call to be on stack in reverse order + /// (i.e. right-most argument pushed first). + /// Expects the caller to set the source location. + std::function generateCode; }; diff --git a/libyul/backends/evm/EVMObjectCompiler.cpp b/libyul/backends/evm/EVMObjectCompiler.cpp index f65f7f1e3..a526bf218 100644 --- a/libyul/backends/evm/EVMObjectCompiler.cpp +++ b/libyul/backends/evm/EVMObjectCompiler.cpp @@ -64,7 +64,16 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize) yulAssert(_object.code, "No code."); // We do not catch and re-throw the stack too deep exception here because it is a YulException, // which should be native to this part of the code. - CodeTransform transform{m_assembly, *_object.analysisInfo, *_object.code, m_dialect, context, _optimize}; + CodeTransform transform{ + m_assembly, + *_object.analysisInfo, + *_object.code, + m_dialect, + context, + _optimize, + {}, + true /* _useNamedLabelsForFunctions */ + }; transform(*_object.code); if (!transform.stackErrors().empty()) BOOST_THROW_EXCEPTION(transform.stackErrors().front()); diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h index e47b79956..c11b375f6 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.h +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -24,7 +24,9 @@ #include #include #include + #include +#include namespace solidity::evmasm { diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index c8016d4a5..62c0ad119 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -26,6 +26,7 @@ #include +#include using namespace std; using namespace solidity; @@ -135,21 +136,11 @@ NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): for (auto& fun: m_functions) { size_t returns = fun.second.returns.size(); - fun.second.generateCode = [=](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&, std::function _visitExpression) + fun.second.generateCode = [=](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&) { - size_t visited = 0; - for (size_t j = 0; j < _call.arguments.size(); j++) - { - size_t const i = _call.arguments.size() - j - 1; + for (size_t i: ranges::views::iota(0u, _call.arguments.size())) if (!fun.second.literalArgument(i)) - { - _visitExpression(_call.arguments[i]); - visited++; - } - } - - for (size_t i = 0; i < visited; i++) - _assembly.appendInstruction(evmasm::Instruction::POP); + _assembly.appendInstruction(evmasm::Instruction::POP); for (size_t i = 0; i < returns; i++) _assembly.appendConstant(u256(0)); diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp new file mode 100644 index 000000000..106e636c8 --- /dev/null +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp @@ -0,0 +1,518 @@ +/* + 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 + +using namespace solidity; +using namespace solidity::yul; +using namespace std; + +vector OptimizedEVMCodeTransform::run( + AbstractAssembly& _assembly, + AsmAnalysisInfo& _analysisInfo, + Block const& _block, + EVMDialect const& _dialect, + BuiltinContext& _builtinContext, + bool _useNamedLabelsForFunctions +) +{ + std::unique_ptr dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block); + StackLayout stackLayout = StackLayoutGenerator::run(*dfg); + OptimizedEVMCodeTransform optimizedCodeTransform( + _assembly, + _builtinContext, + _useNamedLabelsForFunctions, + *dfg, + stackLayout + ); + // Create initial entry layout. + optimizedCodeTransform.createStackLayout(debugDataOf(*dfg->entry), stackLayout.blockInfos.at(dfg->entry).entryLayout); + optimizedCodeTransform(*dfg->entry); + for (Scope::Function const* function: dfg->functions) + optimizedCodeTransform(dfg->functionInfo.at(function)); + return move(optimizedCodeTransform.m_stackErrors); +} + +void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call) +{ + // Validate stack. + { + yulAssert(m_assembly.stackHeight() == static_cast(m_stack.size()), ""); + yulAssert(m_stack.size() >= _call.function.get().arguments.size() + 1, ""); + // Assert that we got the correct arguments on stack for the call. + for (auto&& [arg, slot]: ranges::zip_view( + _call.functionCall.get().arguments | ranges::views::reverse, + m_stack | ranges::views::take_last(_call.functionCall.get().arguments.size()) + )) + validateSlot(slot, arg); + // Assert that we got the correct return label on stack. + auto const* returnLabelSlot = get_if( + &m_stack.at(m_stack.size() - _call.functionCall.get().arguments.size() - 1) + ); + yulAssert(returnLabelSlot && &returnLabelSlot->call.get() == &_call.functionCall.get(), ""); + } + + // Emit code. + { + m_assembly.setSourceLocation(originLocationOf(_call)); + m_assembly.appendJumpTo( + getFunctionLabel(_call.function), + static_cast(_call.function.get().returns.size() - _call.function.get().arguments.size()) - 1, + AbstractAssembly::JumpType::IntoFunction + ); + m_assembly.appendLabel(m_returnLabels.at(&_call.functionCall.get())); + } + + // Update stack. + { + // Remove arguments and return label from m_stack. + for (size_t i = 0; i < _call.function.get().arguments.size() + 1; ++i) + m_stack.pop_back(); + // Push return values to m_stack. + for (size_t index: ranges::views::iota(0u, _call.function.get().returns.size())) + m_stack.emplace_back(TemporarySlot{_call.functionCall, index}); + yulAssert(m_assembly.stackHeight() == static_cast(m_stack.size()), ""); + } +} + +void OptimizedEVMCodeTransform::operator()(CFG::BuiltinCall const& _call) +{ + // Validate stack. + { + yulAssert(m_assembly.stackHeight() == static_cast(m_stack.size()), ""); + yulAssert(m_stack.size() >= _call.arguments, ""); + // Assert that we got a correct stack for the call. + for (auto&& [arg, slot]: ranges::zip_view( + _call.functionCall.get().arguments | + ranges::views::enumerate | + ranges::views::filter(util::mapTuple([&](size_t idx, auto&) -> bool { + return !_call.builtin.get().literalArgument(idx); + })) | + ranges::views::reverse | + ranges::views::values, + m_stack | ranges::views::take_last(_call.arguments) + )) + validateSlot(slot, arg); + } + + // Emit code. + { + m_assembly.setSourceLocation(originLocationOf(_call)); + static_cast(_call.builtin.get()).generateCode( + _call.functionCall, + m_assembly, + m_builtinContext + ); + } + + // Update stack. + { + // Remove arguments from m_stack. + for (size_t i = 0; i < _call.arguments; ++i) + m_stack.pop_back(); + // Push return values to m_stack. + for (size_t index: ranges::views::iota(0u, _call.builtin.get().returns.size())) + m_stack.emplace_back(TemporarySlot{_call.functionCall, index}); + yulAssert(m_assembly.stackHeight() == static_cast(m_stack.size()), ""); + } +} + +void OptimizedEVMCodeTransform::operator()(CFG::Assignment const& _assignment) +{ + yulAssert(m_assembly.stackHeight() == static_cast(m_stack.size()), ""); + + // Invalidate occurrences of the assigned variables. + for (auto& currentSlot: m_stack) + if (VariableSlot const* varSlot = get_if(¤tSlot)) + if (util::contains(_assignment.variables, *varSlot)) + currentSlot = JunkSlot{}; + + // Assign variables to current stack top. + yulAssert(m_stack.size() >= _assignment.variables.size(), ""); + for (auto&& [currentSlot, varSlot]: ranges::zip_view( + m_stack | ranges::views::take_last(_assignment.variables.size()), + _assignment.variables + )) + currentSlot = varSlot; +} + +OptimizedEVMCodeTransform::OptimizedEVMCodeTransform( + AbstractAssembly& _assembly, + BuiltinContext& _builtinContext, + bool _useNamedLabelsForFunctions, + CFG const& _dfg, + StackLayout const& _stackLayout +): + m_assembly(_assembly), + m_builtinContext(_builtinContext), + m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), + m_dfg(_dfg), + m_stackLayout(_stackLayout) +{ +} + +void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentStack, Stack const& _desiredStack) +{ + yulAssert(_currentStack.size() == _desiredStack.size(), ""); + for (auto&& [currentSlot, desiredSlot]: ranges::zip_view(_currentStack, _desiredStack)) + yulAssert(holds_alternative(desiredSlot) || currentSlot == desiredSlot, ""); +} + +AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function) +{ + CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(&_function); + if (!m_functionLabels.count(&functionInfo)) + m_functionLabels[&functionInfo] = m_useNamedLabelsForFunctions ? m_assembly.namedLabel( + functionInfo.function.name.str(), + functionInfo.function.arguments.size(), + functionInfo.function.returns.size(), + {} + ) : m_assembly.newLabelId(); + return m_functionLabels[&functionInfo]; +} + +void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression const& _expression) +{ + std::visit(util::GenericVisitor{ + [&](yul::Literal const& _literal) { + auto* literalSlot = get_if(&_slot); + yulAssert(literalSlot && valueOfLiteral(_literal) == literalSlot->value, ""); + }, + [&](yul::Identifier const& _identifier) { + auto* variableSlot = get_if(&_slot); + yulAssert(variableSlot && variableSlot->variable.get().name == _identifier.name, ""); + }, + [&](yul::FunctionCall const& _call) { + auto* temporarySlot = get_if(&_slot); + yulAssert(temporarySlot && &temporarySlot->call.get() == &_call && temporarySlot->index == 0, ""); + } + }, _expression); +} + +void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr _debugData, Stack _targetStack) +{ + static constexpr auto slotVariableName = [](StackSlot const& _slot) { + return std::visit(util::GenericVisitor{ + [](VariableSlot const& _var) { return _var.variable.get().name; }, + [](auto const&) { return YulString{}; } + }, _slot); + }; + + yulAssert(m_assembly.stackHeight() == static_cast(m_stack.size()), ""); + // ::createStackLayout asserts that it has successfully achieved the target layout. + langutil::SourceLocation sourceLocation = _debugData ? _debugData->originLocation : langutil::SourceLocation{}; + m_assembly.setSourceLocation(sourceLocation); + ::createStackLayout( + m_stack, + _targetStack | ranges::to, + // Swap callback. + [&](unsigned _i) + { + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight(), ""); + yulAssert(_i > 0 && _i < m_stack.size(), ""); + if (_i <= 16) + m_assembly.appendInstruction(evmasm::swapInstruction(_i)); + else + { + int deficit = static_cast(_i) - 16; + StackSlot const& deepSlot = m_stack.at(m_stack.size() - _i - 1); + YulString varNameDeep = slotVariableName(deepSlot); + YulString varNameTop = slotVariableName(m_stack.back()); + string msg = + "Cannot swap " + (varNameDeep.empty() ? "Slot " + stackSlotToString(deepSlot) : "Variable " + varNameDeep.str()) + + " with " + (varNameTop.empty() ? "Slot " + stackSlotToString(m_stack.back()) : "Variable " + varNameTop.str()) + + ": too deep in the stack by " + to_string(deficit) + " slots in " + stackToString(m_stack); + m_stackErrors.emplace_back(StackTooDeepError( + m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{}, + varNameDeep.empty() ? varNameTop : varNameDeep, + deficit, + msg + )); + m_assembly.markAsInvalid(); + } + }, + // Push or dup callback. + [&](StackSlot const& _slot) + { + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight(), ""); + + // Dup the slot, if already on stack and reachable. + if (auto depth = util::findOffset(m_stack | ranges::views::reverse, _slot)) + { + if (*depth < 16) + { + m_assembly.appendInstruction(evmasm::dupInstruction(static_cast(*depth + 1))); + return; + } + else if (!canBeFreelyGenerated(_slot)) + { + int deficit = static_cast(*depth - 15); + YulString varName = slotVariableName(_slot); + string msg = + (varName.empty() ? "Slot " + stackSlotToString(_slot) : "Variable " + varName.str()) + + " is " + to_string(*depth - 15) + " too deep in the stack " + stackToString(m_stack); + m_stackErrors.emplace_back(StackTooDeepError( + m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{}, + varName, + deficit, + msg + )); + m_assembly.markAsInvalid(); + m_assembly.appendConstant(u256(0xCAFFEE)); + return; + } + // else: the slot is too deep in stack, but can be freely generated, we fall through to push it again. + } + + // The slot can be freely generated or is an unassigned return variable. Push it. + std::visit(util::GenericVisitor{ + [&](LiteralSlot const& _literal) + { + m_assembly.setSourceLocation(originLocationOf(_literal)); + m_assembly.appendConstant(_literal.value); + m_assembly.setSourceLocation(sourceLocation); + }, + [&](FunctionReturnLabelSlot const&) + { + yulAssert(false, "Cannot produce function return label."); + }, + [&](FunctionCallReturnLabelSlot const& _returnLabel) + { + if (!m_returnLabels.count(&_returnLabel.call.get())) + m_returnLabels[&_returnLabel.call.get()] = m_assembly.newLabelId(); + m_assembly.setSourceLocation(originLocationOf(_returnLabel.call.get())); + m_assembly.appendLabelReference(m_returnLabels.at(&_returnLabel.call.get())); + m_assembly.setSourceLocation(sourceLocation); + }, + [&](VariableSlot const& _variable) + { + if (m_currentFunctionInfo && util::contains(m_currentFunctionInfo->returnVariables, _variable)) + { + m_assembly.setSourceLocation(originLocationOf(_variable)); + m_assembly.appendConstant(0); + m_assembly.setSourceLocation(sourceLocation); + return; + } + yulAssert(false, "Variable not found on stack."); + }, + [&](TemporarySlot const&) + { + yulAssert(false, "Function call result requested, but not found on stack."); + }, + [&](JunkSlot const&) + { + // Note: this will always be popped, so we can push anything. + m_assembly.appendInstruction(evmasm::Instruction::CODESIZE); + } + }, _slot); + }, + // Pop callback. + [&]() + { + m_assembly.appendInstruction(evmasm::Instruction::POP); + } + ); + yulAssert(m_assembly.stackHeight() == static_cast(m_stack.size()), ""); +} + +void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block) +{ + // Assert that this is the first visit of the block and mark as generated. + yulAssert(m_generated.insert(&_block).second, ""); + + m_assembly.setSourceLocation(originLocationOf(_block)); + auto const& blockInfo = m_stackLayout.blockInfos.at(&_block); + + // Assert that the stack is valid for entering the block. + assertLayoutCompatibility(m_stack, blockInfo.entryLayout); + m_stack = blockInfo.entryLayout; // Might set some slots to junk, if not required by the block. + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight(), ""); + + // Emit jump label, if required. + if (auto label = util::valueOrNullptr(m_blockLabels, &_block)) + m_assembly.appendLabel(*label); + + for (auto const& operation: _block.operations) + { + // Create required layout for entering the operation. + createStackLayout(debugDataOf(operation.operation), m_stackLayout.operationEntryLayout.at(&operation)); + + // Assert that we have the inputs of the operation on stack top. + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight(), ""); + yulAssert(m_stack.size() >= operation.input.size(), ""); + size_t baseHeight = m_stack.size() - operation.input.size(); + assertLayoutCompatibility( + m_stack | ranges::views::take_last(operation.input.size()) | ranges::to, + operation.input + ); + + // Perform the operation. + std::visit(*this, operation.operation); + + // Assert that the operation produced its proclaimed output. + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight(), ""); + yulAssert(m_stack.size() == baseHeight + operation.output.size(), ""); + yulAssert(m_stack.size() >= operation.output.size(), ""); + assertLayoutCompatibility( + m_stack | ranges::views::take_last(operation.output.size()) | ranges::to, + operation.output + ); + } + + // Exit the block. + m_assembly.setSourceLocation(originLocationOf(_block)); + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) + { + m_assembly.appendInstruction(evmasm::Instruction::STOP); + }, + [&](CFG::BasicBlock::Jump const& _jump) + { + // Create the stack expected at the jump target. + createStackLayout(debugDataOf(_jump), m_stackLayout.blockInfos.at(_jump.target).entryLayout); + + // If this is the only jump to the block, we do not need a label and can directly continue with the target block. + if (!m_blockLabels.count(_jump.target) && _jump.target->entries.size() == 1) + { + yulAssert(!_jump.backwards, ""); + (*this)(*_jump.target); + } + else + { + // Generate a jump label for the target, if not already present. + if (!m_blockLabels.count(_jump.target)) + m_blockLabels[_jump.target] = m_assembly.newLabelId(); + + // If we already have generated the target block, jump to it, otherwise generate it in place. + if (m_generated.count(_jump.target)) + m_assembly.appendJumpTo(m_blockLabels[_jump.target]); + else + (*this)(*_jump.target); + } + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + // Create the shared entry layout of the jump targets, which is stored as exit layout of the current block. + createStackLayout(debugDataOf(_conditionalJump), blockInfo.exitLayout); + + // Create labels for the targets, if not already present. + if (!m_blockLabels.count(_conditionalJump.nonZero)) + m_blockLabels[_conditionalJump.nonZero] = m_assembly.newLabelId(); + if (!m_blockLabels.count(_conditionalJump.zero)) + m_blockLabels[_conditionalJump.zero] = m_assembly.newLabelId(); + + // Assert that we have the correct condition on stack. + yulAssert(!m_stack.empty(), ""); + yulAssert(m_stack.back() == _conditionalJump.condition, ""); + + // Emit the conditional jump to the non-zero label and update the stored stack. + m_assembly.appendJumpToIf(m_blockLabels[_conditionalJump.nonZero]); + m_stack.pop_back(); + + // Assert that we have a valid stack for both jump targets. + assertLayoutCompatibility(m_stack, m_stackLayout.blockInfos.at(_conditionalJump.nonZero).entryLayout); + assertLayoutCompatibility(m_stack, m_stackLayout.blockInfos.at(_conditionalJump.zero).entryLayout); + + { + // Restore the stack afterwards for the non-zero case below. + ScopeGuard stackRestore([storedStack = m_stack, this]() { + m_stack = move(storedStack); + m_assembly.setStackHeight(static_cast(m_stack.size())); + }); + + // If we have already generated the zero case, jump to it, otherwise generate it in place. + if (m_generated.count(_conditionalJump.zero)) + m_assembly.appendJumpTo(m_blockLabels[_conditionalJump.zero]); + else + (*this)(*_conditionalJump.zero); + } + // Note that each block visit terminates control flow, so we cannot fall through from the zero case. + + // Generate the non-zero block, if not done already. + if (!m_generated.count(_conditionalJump.nonZero)) + (*this)(*_conditionalJump.nonZero); + }, + [&](CFG::BasicBlock::FunctionReturn const& _functionReturn) + { + yulAssert(m_currentFunctionInfo, ""); + yulAssert(m_currentFunctionInfo == _functionReturn.info, ""); + + // Construct the function return layout, which is fully determined by the function signature. + Stack exitStack = m_currentFunctionInfo->returnVariables | ranges::views::transform([](auto const& _varSlot){ + return StackSlot{_varSlot}; + }) | ranges::to; + exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function}); + + // Create the function return layout and jump. + createStackLayout(debugDataOf(_functionReturn), exitStack); + m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction); + }, + [&](CFG::BasicBlock::Terminated const&) + { + // Assert that the last builtin call was in fact terminating. + yulAssert(!_block.operations.empty(), ""); + CFG::BuiltinCall const* builtinCall = get_if(&_block.operations.back().operation); + yulAssert(builtinCall, ""); + yulAssert(builtinCall->builtin.get().controlFlowSideEffects.terminates, ""); + } + }, _block.exit); + // TODO: We could assert that the last emitted assembly item terminated or was an (unconditional) jump. + // But currently AbstractAssembly does not allow peeking at the last emitted assembly item. + m_stack.clear(); + m_assembly.setStackHeight(0); +} + +void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInfo) +{ + yulAssert(!m_currentFunctionInfo, ""); + ScopedSaveAndRestore currentFunctionInfoRestore(m_currentFunctionInfo, &_functionInfo); + + yulAssert(m_stack.empty() && m_assembly.stackHeight() == 0, ""); + + // Create function entry layout in m_stack. + m_stack.emplace_back(FunctionReturnLabelSlot{_functionInfo.function}); + for (auto const& param: _functionInfo.parameters | ranges::views::reverse) + m_stack.emplace_back(param); + m_assembly.setStackHeight(static_cast(m_stack.size())); + + m_assembly.setSourceLocation(originLocationOf(_functionInfo)); + m_assembly.appendLabel(getFunctionLabel(_functionInfo.function)); + + // Create the entry layout of the function body block and visit. + createStackLayout(debugDataOf(_functionInfo), m_stackLayout.blockInfos.at(_functionInfo.entry).entryLayout); + (*this)(*_functionInfo.entry); + + m_stack.clear(); + m_assembly.setStackHeight(0); +} diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.h b/libyul/backends/evm/OptimizedEVMCodeTransform.h new file mode 100644 index 000000000..48819251a --- /dev/null +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.h @@ -0,0 +1,111 @@ +/* + 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 +/** + * Code generator for translating Yul / inline assembly to EVM. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace solidity::langutil +{ +class ErrorReporter; +} + +namespace solidity::yul +{ +struct AsmAnalysisInfo; +struct StackLayout; + +class OptimizedEVMCodeTransform +{ +public: + [[nodiscard]] static std::vector run( + AbstractAssembly& _assembly, + AsmAnalysisInfo& _analysisInfo, + Block const& _block, + EVMDialect const& _dialect, + BuiltinContext& _builtinContext, + bool _useNamedLabelsForFunctions = false + ); + + /// Generate code for the function call @a _call. Only public for using with std::visit. + void operator()(CFG::FunctionCall const& _call); + /// Generate code for the builtin call @a _call. Only public for using with std::visit. + void operator()(CFG::BuiltinCall const& _call); + /// Generate code for the assignment @a _assignment. Only public for using with std::visit. + void operator()(CFG::Assignment const& _assignment); +private: + OptimizedEVMCodeTransform( + AbstractAssembly& _assembly, + BuiltinContext& _builtinContext, + bool _useNamedLabelsForFunctions, + CFG const& _dfg, + StackLayout const& _stackLayout + ); + + /// Assert that it is valid to transition from @a _currentStack to @a _desiredStack. + /// That is @a _currentStack matches each slot in @a _desiredStack that is not a JunkSlot exactly. + static void assertLayoutCompatibility(Stack const& _currentStack, Stack const& _desiredStack); + /// @returns The label of the entry point of the given @a _function. + /// Creates and stores a new label, if none exists already. + AbstractAssembly::LabelID getFunctionLabel(Scope::Function const& _function); + /// Assert that @a _slot contains the value of @a _expression. + static void validateSlot(StackSlot const& _slot, Expression const& _expression); + + /// Shuffles m_stack to the desired @a _targetStack while emitting the shuffling code to m_assembly. + /// Sets the source locations to the one in @a _debugData. + void createStackLayout(std::shared_ptr _debugData, Stack _targetStack); + + /// Generate code for the given block @a _block. + /// Expects the current stack layout m_stack to be a stack layout that is compatible with the + /// entry layout expected by the block. + /// Recursively generates code for blocks that are jumped to. + /// The last emitted assembly instruction is always an unconditional jump or terminating. + /// Always exits with an empty stack layout. + void operator()(CFG::BasicBlock const& _block); + + /// Generate code for the given function. + /// Resets m_stack. + void operator()(CFG::FunctionInfo const& _functionInfo); + + AbstractAssembly& m_assembly; + BuiltinContext& m_builtinContext; + bool m_useNamedLabelsForFunctions = true; + CFG const& m_dfg; + StackLayout const& m_stackLayout; + Stack m_stack; + std::map m_returnLabels; + std::map m_blockLabels; + std::map m_functionLabels; + /// Set of blocks already generated. If any of the contained blocks is ever jumped to, m_blockLabels should + /// contain a jump label for it. + std::set m_generated; + CFG::FunctionInfo const* m_currentFunctionInfo = nullptr; + std::vector m_stackErrors; +}; + +} diff --git a/libyul/backends/evm/StackHelpers.h b/libyul/backends/evm/StackHelpers.h new file mode 100644 index 000000000..38d0ddf4b --- /dev/null +++ b/libyul/backends/evm/StackHelpers.h @@ -0,0 +1,429 @@ +/* + 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 +#include +#include + +namespace solidity::yul +{ + +inline std::string stackSlotToString(StackSlot const& _slot) +{ + return std::visit(util::GenericVisitor{ + [](FunctionCallReturnLabelSlot const& _ret) -> std::string { return "RET[" + _ret.call.get().functionName.name.str() + "]"; }, + [](FunctionReturnLabelSlot const&) -> std::string { return "RET"; }, + [](VariableSlot const& _var) { return _var.variable.get().name.str(); }, + [](LiteralSlot const& _lit) { return toCompactHexWithPrefix(_lit.value); }, + [](TemporarySlot const& _tmp) -> std::string { return "TMP[" + _tmp.call.get().functionName.name.str() + ", " + std::to_string(_tmp.index) + "]"; }, + [](JunkSlot const&) -> std::string { return "JUNK"; } + }, _slot); +} + +inline std::string stackToString(Stack const& _stack) +{ + std::string result("[ "); + for (auto const& slot: _stack) + result += stackSlotToString(slot) + ' '; + result += ']'; + return result; +} + + +// Abstraction of stack shuffling operations. Can be defined as actual concept once we switch to C++20. +// Used as an interface for the stack shuffler below. +// The shuffle operation class is expected to internally keep track of a current stack layout (the "source layout") +// that the shuffler is supposed to shuffle to a fixed target stack layout. +// The shuffler works iteratively. At each iteration it instantiates an instance of the shuffle operations and +// queries it for various information about the current source stack layout and the target layout, as described +// in the interface below. +// Based on that information the shuffler decides which is the next optimal operation to perform on the stack +// and calls the corresponding entry point in the shuffling operations (swap, pushOrDupTarget or pop). +/* +template +concept ShuffleOperationConcept = requires(ShuffleOperations ops, size_t sourceOffset, size_t targetOffset, size_t depth) { + // Returns true, iff the current slot at sourceOffset in source layout is a suitable slot at targetOffset. + { ops.isCompatible(sourceOffset, targetOffset) } -> std::convertible_to; + // Returns true, iff the slots at the two given source offsets are identical. + { ops.sourceIsSame(sourceOffset, sourceOffset) } -> std::convertible_to; + // Returns a positive integer n, if the slot at the given source offset needs n more copies. + // Returns a negative integer -n, if the slot at the given source offsets occurs n times too many. + // Returns zero if the amount of occurrences, in the current source layout, of the slot at the given source offset + // matches the desired amount of occurrences in the target. + { ops.sourceMultiplicity(sourceOffset) } -> std::convertible_to; + // Returns a positive integer n, if the slot at the given target offset needs n more copies. + // Returns a negative integer -n, if the slot at the given target offsets occurs n times too many. + // Returns zero if the amount of occurrences, in the current source layout, of the slot at the given target offset + // matches the desired amount of occurrences in the target. + { ops.targetMultiplicity(targetOffset) } -> std::convertible_to; + // Returns true, iff any slot is compatible with the given target offset. + { ops.targetIsArbitrary(targetOffset) } -> std::convertible_to; + // Returns the number of slots in the source layout. + { ops.sourceSize() } -> std::convertible_to; + // Returns the number of slots in the target layout. + { ops.targetSize() } -> std::convertible_to; + // Swaps the top most slot in the source with the slot `depth` slots below the top. + // In terms of EVM opcodes this is supposed to be a `SWAP`. + // In terms of vectors this is supposed to be `std::swap(source.at(source.size() - depth - 1, source.top))`. + { ops.swap(depth) }; + // Pops the top most slot in the source, i.e. the slot at offset ops.sourceSize() - 1. + // In terms of EVM opcodes this is `POP`. + // In terms of vectors this is `source.pop();`. + { ops.pop() }; + // Dups or pushes the slot that is supposed to end up at the given target offset. + { ops.pushOrDupTarget(targetOffset) }; +}; +*/ +/// Helper class that can perform shuffling of a source stack layout to a target stack layout via +/// abstracted shuffle operations. +template +class Shuffler +{ +public: + /// Executes the stack shuffling operations. Instantiates an instance of ShuffleOperations + /// in each iteration. Each iteration performs exactly one operation that modifies the stack. + /// After `shuffle`, source and target have the same size and all slots in the source layout are + /// compatible with the slots at the same target offset. + template + static void shuffle(Args&&... args) + { + bool needsMoreShuffling = true; + // The shuffling algorithm should always terminate in polynomial time, but we provide a limit + // in case it does not terminate due to a bug. + size_t iterationCount = 0; + while (iterationCount < 1000 && (needsMoreShuffling = shuffleStep(std::forward(args)...))) + ++iterationCount; + yulAssert(!needsMoreShuffling, "Could not create stack layout after 1000 iterations."); + } +private: + // If dupping an ideal slot causes a slot that will still be required to become unreachable, then dup + // the latter slot first. + // @returns true, if it performed a dup. + static bool dupDeepSlotIfRequired(ShuffleOperations& _ops) + { + // Check if the stack is large enough for anything to potentially become unreachable. + if (_ops.sourceSize() < 15) + return false; + // Check whether any deep slot might still be needed later (i.e. we still need to reach it with a DUP or SWAP). + for (size_t sourceOffset: ranges::views::iota(0u, _ops.sourceSize() - 15)) + { + // This slot needs to be moved. + if (!_ops.isCompatible(sourceOffset, sourceOffset)) + { + // If the current top fixes the slot, swap it down now. + if (_ops.isCompatible(_ops.sourceSize() - 1, sourceOffset)) + { + _ops.swap(_ops.sourceSize() - sourceOffset - 1); + return true; + } + // Bring up a slot to fix this now, if possible. + if (bringUpTargetSlot(_ops, sourceOffset)) + return true; + // Otherwise swap up the slot that will fix the offending slot. + for (auto offset: ranges::views::iota(sourceOffset + 1, _ops.sourceSize())) + if (_ops.isCompatible(offset, sourceOffset)) + { + _ops.swap(_ops.sourceSize() - offset - 1); + return true; + } + // Otherwise give up - we will need stack compression or stack limit evasion. + } + // We need another copy of this slot. + else if (_ops.sourceMultiplicity(sourceOffset) > 0) + { + // If this slot occurs again later, we skip this occurrence. + if (ranges::any_of( + ranges::views::iota(sourceOffset + 1, _ops.sourceSize()), + [&](size_t _offset) { return _ops.sourceIsSame(sourceOffset, _offset); } + )) + continue; + // Bring up the target slot that would otherwise become unreachable. + for (size_t targetOffset: ranges::views::iota(0u, _ops.targetSize())) + if (!_ops.targetIsArbitrary(targetOffset) && _ops.isCompatible(sourceOffset, targetOffset)) + { + _ops.pushOrDupTarget(targetOffset); + return true; + } + } + } + return false; + } + /// Finds a slot to dup or push with the aim of eventually fixing @a _targetOffset in the target. + /// In the simplest case, the slot at @a _targetOffset has a multiplicity > 0, i.e. it can directly be dupped or pushed + /// and the next iteration will fix @a _targetOffset. + /// But, in general, there may already be enough copies of the slot that is supposed to end up at @a _targetOffset + /// on stack, s.t. it cannot be dupped again. In that case there has to be a copy of the desired slot on stack already + /// elsewhere that is not yet in place (`nextOffset` below). The fact that ``nextOffset`` is not in place means that + /// we can (recursively) try bringing up the slot that is supposed to end up at ``nextOffset`` in the *target*. + /// When the target slot at ``nextOffset`` is fixed, the current source slot at ``nextOffset`` will be + /// at the stack top, which is the slot required at @a _targetOffset. + static bool bringUpTargetSlot(ShuffleOperations& _ops, size_t _targetOffset) + { + std::list toVisit{_targetOffset}; + std::set visited; + + while (!toVisit.empty()) + { + auto offset = *toVisit.begin(); + toVisit.erase(toVisit.begin()); + visited.emplace(offset); + if (_ops.targetMultiplicity(offset) > 0) + { + _ops.pushOrDupTarget(offset); + return true; + } + // There must be another slot we can dup/push that will lead to the target slot at ``offset`` to be fixed. + for (auto nextOffset: ranges::views::iota(0u, std::min(_ops.sourceSize(), _ops.targetSize()))) + if ( + !_ops.isCompatible(nextOffset, nextOffset) && + _ops.isCompatible(nextOffset, offset) + ) + if (!visited.count(nextOffset)) + toVisit.emplace_back(nextOffset); + } + return false; + } + /// Performs a single stack operation, transforming the source layout closer to the target layout. + template + static bool shuffleStep(Args&&... args) + { + ShuffleOperations ops{std::forward(args)...}; + + // All source slots are final. + if (ranges::all_of( + ranges::views::iota(0u, ops.sourceSize()), + [&](size_t _index) { return ops.isCompatible(_index, _index); } + )) + { + // Bring up all remaining target slots, if any, or terminate otherwise. + if (ops.sourceSize() < ops.targetSize()) + { + if (!dupDeepSlotIfRequired(ops)) + yulAssert(bringUpTargetSlot(ops, ops.sourceSize()), ""); + return true; + } + return false; + } + + size_t sourceTop = ops.sourceSize() - 1; + // If we no longer need the current stack top, we pop it, unless we need an arbitrary slot at this position + // in the target. + if ( + ops.sourceMultiplicity(sourceTop) < 0 && + !ops.targetIsArbitrary(sourceTop) + ) + { + ops.pop(); + return true; + } + + yulAssert(ops.targetSize() > 0, ""); + + // If the top is not supposed to be exactly what is on top right now, try to find a lower position to swap it to. + if (!ops.isCompatible(sourceTop, sourceTop) || ops.targetIsArbitrary(sourceTop)) + for (size_t offset: ranges::views::iota(0u, std::min(ops.sourceSize(), ops.targetSize()))) + // It makes sense to swap to a lower position, if + if ( + !ops.isCompatible(offset, offset) && // The lower slot is not already in position. + !ops.sourceIsSame(offset, sourceTop) && // We would not just swap identical slots. + ops.isCompatible(sourceTop, offset) // The lower position wants to have this slot. + ) + { + // We cannot swap that deep. + if (ops.sourceSize() - offset - 1 > 16) + { + // If there is a reachable slot to be removed, park the current top there. + for (size_t swapDepth: ranges::views::iota(1u, 17u) | ranges::views::reverse) + if (ops.sourceMultiplicity(ops.sourceSize() - 1 - swapDepth) < 0) + { + ops.swap(swapDepth); + return true; + } + // Otherwise we rely on stack compression or stack-to-memory. + } + ops.swap(ops.sourceSize() - offset - 1); + return true; + } + + // ops.sourceSize() > ops.targetSize() cannot be true anymore, since if the source top is no longer required, + // we already popped it, and if it is required, we already swapped it down to a suitable target position. + yulAssert(ops.sourceSize() <= ops.targetSize(), ""); + + // If a lower slot should be removed, try to bring up the slot that should end up there and bring it up. + // Note that after the cases above, there will always be a target slot to duplicate in this case. + for (size_t offset: ranges::views::iota(0u, ops.sourceSize())) + if ( + !ops.isCompatible(offset, offset) && // The lower slot is not already in position. + ops.sourceMultiplicity(offset) < 0 && // We have too many copies of this slot. + offset <= ops.targetSize() && // There is a target slot at this position. + !ops.targetIsArbitrary(offset) // And that target slot is not arbitrary. + ) + { + if (!dupDeepSlotIfRequired(ops)) + yulAssert(bringUpTargetSlot(ops, offset), ""); + return true; + } + + // At this point we want to keep all slots. + for (size_t i = 0; i < ops.sourceSize(); ++i) + yulAssert(ops.sourceMultiplicity(i) >= 0, ""); + yulAssert(ops.sourceSize() <= ops.targetSize(), ""); + + // If the top is not in position, try to find a slot that wants to be at the top and swap it up. + if (!ops.isCompatible(sourceTop, sourceTop)) + for (size_t sourceOffset: ranges::views::iota(0u, ops.sourceSize())) + if ( + !ops.isCompatible(sourceOffset, sourceOffset) && + ops.isCompatible(sourceOffset, sourceTop) + ) + { + ops.swap(ops.sourceSize() - sourceOffset - 1); + return true; + } + + // If we still need more slots, produce a suitable one. + if (ops.sourceSize() < ops.targetSize()) + { + if (!dupDeepSlotIfRequired(ops)) + yulAssert(bringUpTargetSlot(ops, ops.sourceSize()), ""); + return true; + } + + // The stack has the correct size, each slot has the correct number of copies and the top is in position. + yulAssert(ops.sourceSize() == ops.targetSize(), ""); + size_t size = ops.sourceSize(); + for (size_t i = 0; i < ops.sourceSize(); ++i) + yulAssert(ops.sourceMultiplicity(i) == 0 && (ops.targetIsArbitrary(i) || ops.targetMultiplicity(i) == 0), ""); + yulAssert(ops.isCompatible(sourceTop, sourceTop), ""); + + // If we find a lower slot that is out of position, but also compatible with the top, swap that up. + for (size_t offset: ranges::views::iota(0u, size)) + if (!ops.isCompatible(offset, offset) && ops.isCompatible(sourceTop, offset)) + { + ops.swap(size - offset - 1); + return true; + } + // Swap up any slot that is still out of position. + for (size_t offset: ranges::views::iota(0u, size)) + if (!ops.isCompatible(offset, offset) && !ops.sourceIsSame(offset, sourceTop)) + { + ops.swap(size - offset - 1); + return true; + } + yulAssert(false, ""); + } +}; + + +/// Transforms @a _currentStack to @a _targetStack, invoking the provided shuffling operations. +/// Modifies @a _currentStack itself after each invocation of the shuffling operations. +/// @a _swap is a function with signature void(unsigned) that is called when the top most slot is swapped with +/// the slot `depth` slots below the top. In terms of EVM opcodes this is supposed to be a `SWAP`. +/// @a _pushOrDup is a function with signature void(StackSlot const&) that is called to push or dup the slot given as +/// its argument to the stack top. +/// @a _pop is a function with signature void() that is called when the top most slot is popped. +template +void createStackLayout(Stack& _currentStack, Stack const& _targetStack, Swap _swap, PushOrDup _pushOrDup, Pop _pop) +{ + struct ShuffleOperations + { + Stack& currentStack; + Stack const& targetStack; + Swap swapCallback; + PushOrDup pushOrDupCallback; + Pop popCallback; + std::map multiplicity; + ShuffleOperations( + Stack& _currentStack, + Stack const& _targetStack, + Swap _swap, + PushOrDup _pushOrDup, + Pop _pop + ): + currentStack(_currentStack), + targetStack(_targetStack), + swapCallback(_swap), + pushOrDupCallback(_pushOrDup), + popCallback(_pop) + { + for (auto const& slot: currentStack) + --multiplicity[slot]; + for (auto&& [offset, slot]: targetStack | ranges::views::enumerate) + if (std::holds_alternative(slot) && offset < currentStack.size()) + ++multiplicity[currentStack.at(offset)]; + else + ++multiplicity[slot]; + } + bool isCompatible(size_t _source, size_t _target) + { + return + _source < currentStack.size() && + _target < targetStack.size() && + ( + std::holds_alternative(targetStack.at(_target)) || + currentStack.at(_source) == targetStack.at(_target) + ); + } + bool sourceIsSame(size_t _lhs, size_t _rhs) { return currentStack.at(_lhs) == currentStack.at(_rhs); } + int sourceMultiplicity(size_t _offset) { return multiplicity.at(currentStack.at(_offset)); } + int targetMultiplicity(size_t _offset) { return multiplicity.at(targetStack.at(_offset)); } + bool targetIsArbitrary(size_t offset) + { + return offset < targetStack.size() && std::holds_alternative(targetStack.at(offset)); + } + void swap(size_t _i) + { + swapCallback(static_cast(_i)); + std::swap(currentStack.at(currentStack.size() - _i - 1), currentStack.back()); + } + size_t sourceSize() { return currentStack.size(); } + size_t targetSize() { return targetStack.size(); } + void pop() + { + popCallback(); + currentStack.pop_back(); + } + void pushOrDupTarget(size_t _offset) + { + auto const& targetSlot = targetStack.at(_offset); + pushOrDupCallback(targetSlot); + currentStack.push_back(targetSlot); + } + }; + + Shuffler::shuffle(_currentStack, _targetStack, _swap, _pushOrDup, _pop); + + yulAssert(_currentStack.size() == _targetStack.size(), ""); + for (auto&& [current, target]: ranges::zip_view(_currentStack, _targetStack)) + if (std::holds_alternative(target)) + current = JunkSlot{}; + else + yulAssert(current == target, ""); +} + +} diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp new file mode 100644 index 000000000..43a1d032c --- /dev/null +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -0,0 +1,705 @@ +/* + 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 +/** + * Stack layout generator for Yul to EVM code generation. + */ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace solidity; +using namespace solidity::yul; +using namespace std; + +StackLayout StackLayoutGenerator::run(CFG const& _cfg) +{ + StackLayout stackLayout; + StackLayoutGenerator{stackLayout}.processEntryPoint(*_cfg.entry); + + for (auto& functionInfo: _cfg.functionInfo | ranges::views::values) + StackLayoutGenerator{stackLayout}.processEntryPoint(*functionInfo.entry); + + return stackLayout; +} + +map> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg) +{ + map> stackTooDeepErrors; + stackTooDeepErrors[YulString{}] = reportStackTooDeep(_cfg, YulString{}); + for (auto const& function: _cfg.functions) + if (auto errors = reportStackTooDeep(_cfg, function->name); !errors.empty()) + stackTooDeepErrors[function->name] = move(errors); + return stackTooDeepErrors; +} + +vector StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, YulString _functionName) +{ + StackLayout stackLayout; + CFG::FunctionInfo const* functionInfo = nullptr; + if (!_functionName.empty()) + { + functionInfo = &ranges::find( + _cfg.functionInfo, + _functionName, + util::mapTuple([](auto&&, auto&& info) { return info.function.name; }) + )->second; + yulAssert(functionInfo, "Function not found."); + } + + StackLayoutGenerator generator{stackLayout}; + CFG::BasicBlock const* entry = functionInfo ? functionInfo->entry : _cfg.entry; + generator.processEntryPoint(*entry); + return generator.reportStackTooDeep(*entry); +} + +StackLayoutGenerator::StackLayoutGenerator(StackLayout& _layout): m_layout(_layout) +{ +} + +namespace +{ +/// @returns all stack too deep errors that would occur when shuffling @a _source to @a _target. +vector findStackTooDeep(Stack const& _source, Stack const& _target) +{ + Stack currentStack = _source; + vector stackTooDeepErrors; + auto getVariableChoices = [](auto&& _range) { + vector result; + for (auto const& slot: _range) + if (auto const* variableSlot = get_if(&slot)) + if (!util::contains(result, variableSlot->variable.get().name)) + result.push_back(variableSlot->variable.get().name); + return result; + }; + ::createStackLayout( + currentStack, + _target, + [&](unsigned _i) + { + if (_i > 16) + stackTooDeepErrors.emplace_back(StackLayoutGenerator::StackTooDeep{ + _i - 16, + getVariableChoices(currentStack | ranges::views::take_last(_i + 1)) + }); + }, + [&](StackSlot const& _slot) + { + if (canBeFreelyGenerated(_slot)) + return; + if ( + auto depth = util::findOffset(currentStack | ranges::views::reverse, _slot); + depth && *depth >= 16 + ) + stackTooDeepErrors.emplace_back(StackLayoutGenerator::StackTooDeep{ + *depth - 15, + getVariableChoices(currentStack | ranges::views::take_last(*depth + 1)) + }); + }, + [&]() {} + ); + return stackTooDeepErrors; +} + +/// @returns the ideal stack to have before executing an operation that outputs @a _operationOutput, s.t. +/// shuffling to @a _post is cheap (excluding the input of the operation itself). +/// If @a _generateSlotOnTheFly returns true for a slot, this slot should not occur in the ideal stack, but +/// rather be generated on the fly during shuffling. +template +Stack createIdealLayout(Stack const& _operationOutput, Stack const& _post, Callable _generateSlotOnTheFly) +{ + struct PreviousSlot { size_t slot; }; + + // Determine the number of slots that have to be on stack before executing the operation (excluding + // the inputs of the operation itself). + // That is slots that should not be generated on the fly and are not outputs of the operation. + size_t preOperationLayoutSize = _post.size(); + for (auto const& slot: _post) + if (util::contains(_operationOutput, slot) || _generateSlotOnTheFly(slot)) + --preOperationLayoutSize; + + // The symbolic layout directly after the operation has the form + // PreviousSlot{0}, ..., PreviousSlot{n}, [output<0>], ..., [output] + auto layout = ranges::views::iota(0u, preOperationLayoutSize) | + ranges::views::transform([](size_t _index) { return PreviousSlot{_index}; }) | + ranges::to>>; + layout += _operationOutput; + + // Shortcut for trivial case. + if (layout.empty()) + return Stack{}; + + // Next we will shuffle the layout to the post stack using ShuffleOperations + // that are aware of PreviousSlot's. + struct ShuffleOperations + { + vector>& layout; + Stack const& post; + std::set outputs; + std::map multiplicity; + Callable generateSlotOnTheFly; + ShuffleOperations( + vector>& _layout, + Stack const& _post, + Callable _generateSlotOnTheFly + ): layout(_layout), post(_post), generateSlotOnTheFly(_generateSlotOnTheFly) + { + for (auto const& layoutSlot: layout) + if (StackSlot const* slot = get_if(&layoutSlot)) + outputs.insert(*slot); + + for (auto const& layoutSlot: layout) + if (StackSlot const* slot = get_if(&layoutSlot)) + --multiplicity[*slot]; + for (auto&& slot: post) + if (outputs.count(slot) || generateSlotOnTheFly(slot)) + ++multiplicity[slot]; + } + bool isCompatible(size_t _source, size_t _target) + { + return + _source < layout.size() && + _target < post.size() && + ( + std::holds_alternative(post.at(_target)) || + std::visit(util::GenericVisitor{ + [&](PreviousSlot const&) { + return !outputs.count(post.at(_target)) && !generateSlotOnTheFly(post.at(_target)); + }, + [&](StackSlot const& _s) { return _s == post.at(_target); } + }, layout.at(_source)) + ); + } + bool sourceIsSame(size_t _lhs, size_t _rhs) + { + return std::visit(util::GenericVisitor{ + [&](PreviousSlot const&, PreviousSlot const&) { return true; }, + [&](StackSlot const& _lhs, StackSlot const& _rhs) { return _lhs == _rhs; }, + [&](auto const&, auto const&) { return false; } + }, layout.at(_lhs), layout.at(_rhs)); + } + int sourceMultiplicity(size_t _offset) + { + return std::visit(util::GenericVisitor{ + [&](PreviousSlot const&) { return 0; }, + [&](StackSlot const& _s) { return multiplicity.at(_s); } + }, layout.at(_offset)); + } + int targetMultiplicity(size_t _offset) + { + if (!outputs.count(post.at(_offset)) && !generateSlotOnTheFly(post.at(_offset))) + return 0; + return multiplicity.at(post.at(_offset)); + } + bool targetIsArbitrary(size_t _offset) + { + return _offset < post.size() && std::holds_alternative(post.at(_offset)); + } + void swap(size_t _i) + { + yulAssert(!holds_alternative(layout.at(layout.size() - _i - 1)) || !holds_alternative(layout.back()), ""); + std::swap(layout.at(layout.size() - _i - 1), layout.back()); + } + size_t sourceSize() { return layout.size(); } + size_t targetSize() { return post.size(); } + void pop() { layout.pop_back(); } + void pushOrDupTarget(size_t _offset) { layout.push_back(post.at(_offset)); } + }; + Shuffler::shuffle(layout, _post, _generateSlotOnTheFly); + + // Now we can construct the ideal layout before the operation. + // "layout" has shuffled the PreviousSlot{x} to new places using minimal operations to move the operation + // output in place. The resulting permutation of the PreviousSlot yields the ideal positions of slots + // before the operation, i.e. if PreviousSlot{2} is at a position at which _post contains VariableSlot{"tmp"}, + // then we want the variable tmp in the slot at offset 2 in the layout before the operation. + vector> idealLayout(_post.size(), nullopt); + for (auto const& [slot, idealPosition]: ranges::zip_view(_post, layout)) + if (PreviousSlot* previousSlot = std::get_if(&idealPosition)) + idealLayout.at(previousSlot->slot) = slot; + + // The tail of layout must have contained the operation outputs and will not have been assigned slots in the last loop. + while (!idealLayout.empty() && !idealLayout.back()) + idealLayout.pop_back(); + + yulAssert(idealLayout.size() == preOperationLayoutSize, ""); + + return idealLayout | ranges::views::transform([](optional s) { + yulAssert(s, ""); + return *s; + }) | ranges::to; +} +} + +Stack StackLayoutGenerator::propagateStackThroughOperation(Stack _exitStack, CFG::Operation const& _operation, bool _aggressiveStackCompression) +{ + // Enable aggressive stack compression for recursive calls. + if (auto const* functionCall = get_if(&_operation.operation)) + if (functionCall->recursive) + _aggressiveStackCompression = true; + + // This is a huge tradeoff between code size, gas cost and stack size. + auto generateSlotOnTheFly = [&](StackSlot const& _slot) { + return _aggressiveStackCompression && canBeFreelyGenerated(_slot); + }; + + // Determine the ideal permutation of the slots in _exitLayout that are not operation outputs (and not to be + // generated on the fly), s.t. shuffling the `stack + _operation.output` to _exitLayout is cheap. + Stack stack = createIdealLayout(_operation.output, _exitStack, generateSlotOnTheFly); + + // Make sure the resulting previous slots do not overlap with any assignmed variables. + if (auto const* assignment = get_if(&_operation.operation)) + for (auto& stackSlot: stack) + if (auto const* varSlot = get_if(&stackSlot)) + yulAssert(!util::contains(assignment->variables, *varSlot), ""); + + // Since stack+_operation.output can be easily shuffled to _exitLayout, the desired layout before the operation + // is stack+_operation.input; + stack += _operation.input; + + // Store the exact desired operation entry layout. The stored layout will be recreated by the code transform + // before executing the operation. However, this recreation can produce slots that can be freely generated or + // are duplicated, i.e. we can compress the stack afterwards without causing problems for code generation later. + m_layout.operationEntryLayout[&_operation] = stack; + + // Remove anything from the stack top that can be freely generated or dupped from deeper on the stack. + while (!stack.empty()) + { + if (canBeFreelyGenerated(stack.back())) + stack.pop_back(); + else if (auto offset = util::findOffset(stack | ranges::views::reverse | ranges::views::drop(1), stack.back())) + { + if (*offset + 2 < 16) + stack.pop_back(); + else + break; + } + else + break; + } + + return stack; +} + +Stack StackLayoutGenerator::propagateStackThroughBlock(Stack _exitStack, CFG::BasicBlock const& _block, bool _aggressiveStackCompression) +{ + Stack stack = _exitStack; + for (auto&& [idx, operation]: _block.operations | ranges::views::enumerate | ranges::views::reverse) + { + Stack newStack = propagateStackThroughOperation(stack, operation, _aggressiveStackCompression); + if (!_aggressiveStackCompression && !findStackTooDeep(newStack, stack).empty()) + // If we had stack errors, run again with aggressive stack compression. + return propagateStackThroughBlock(move(_exitStack), _block, true); + stack = move(newStack); + } + + return stack; +} + +void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry) +{ + list toVisit{&_entry}; + set visited; + + // TODO: check whether visiting only a subset of these in the outer iteration below is enough. + list> backwardsJumps = collectBackwardsJumps(_entry); + + while (!toVisit.empty()) + { + // First calculate stack layouts without walking backwards jumps, i.e. assuming the current preliminary + // entry layout of the backwards jump target as the initial exit layout of the backwards-jumping block. + while (!toVisit.empty()) + { + CFG::BasicBlock const *block = *toVisit.begin(); + toVisit.pop_front(); + + if (visited.count(block)) + continue; + + if (std::optional exitLayout = getExitLayoutOrStageDependencies(*block, visited, toVisit)) + { + visited.emplace(block); + auto& info = m_layout.blockInfos[block]; + info.exitLayout = *exitLayout; + info.entryLayout = propagateStackThroughBlock(info.exitLayout, *block); + + for (auto entry: block->entries) + toVisit.emplace_back(entry); + } + else + continue; + } + + // Determine which backwards jumps still require fixing and stage revisits of appropriate nodes. + for (auto [jumpingBlock, target]: backwardsJumps) + // This block jumps backwards, but does not provide all slots required by the jump target on exit. + // Therefore we need to visit the subgraph between ``target`` and ``jumpingBlock`` again. + if (ranges::any_of( + m_layout.blockInfos[target].entryLayout, + [exitLayout = m_layout.blockInfos[jumpingBlock].exitLayout](StackSlot const& _slot) { + return !util::contains(exitLayout, _slot); + } + )) + { + // In particular we can visit backwards starting from ``jumpingBlock`` and mark all entries to-be-visited- + // again until we hit ``target``. + toVisit.emplace_front(jumpingBlock); + // Since we are likely to permute the entry layout of ``target``, we also visit its entries again. + // This is not required for correctness, since the set of stack slots will match, but it may move some + // required stack shuffling from the loop condition to outside the loop. + for (CFG::BasicBlock const* entry: target->entries) + visited.erase(entry); + util::BreadthFirstSearch{{jumpingBlock}}.run( + [&visited, target = target](CFG::BasicBlock const* _block, auto _addChild) { + visited.erase(_block); + if (_block == target) + return; + for (auto const* entry: _block->entries) + _addChild(entry); + } + ); + // While the shuffled layout for ``target`` will be compatible, it can be worthwhile propagating + // it further up once more. + // This would mean not stopping at _block == target above, resp. even doing visited.clear() here, revisiting the entire graph. + // This is a tradeoff between the runtime of this process and the optimality of the result. + // Also note that while visiting the entire graph again *can* be helpful, it can also be detrimental. + } + } + + stitchConditionalJumps(_entry); +} + +optional StackLayoutGenerator::getExitLayoutOrStageDependencies( + CFG::BasicBlock const& _block, + set const& _visited, + list& _toVisit +) const +{ + return std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) -> std::optional + { + // On the exit of the outermost block the stack can be empty. + return Stack{}; + }, + [&](CFG::BasicBlock::Jump const& _jump) -> std::optional + { + if (_jump.backwards) + { + // Choose the best currently known entry layout of the jump target as initial exit. + // Note that this may not yet be the final layout. + if (auto* info = util::valueOrNullptr(m_layout.blockInfos, _jump.target)) + return info->entryLayout; + return Stack{}; + } + // If the current iteration has already visited the jump target, start from its entry layout. + if (_visited.count(_jump.target)) + return m_layout.blockInfos.at(_jump.target).entryLayout; + // Otherwise stage the jump target for visit and defer the current block. + _toVisit.emplace_front(_jump.target); + return nullopt; + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) -> std::optional + { + bool zeroVisited = _visited.count(_conditionalJump.zero); + bool nonZeroVisited = _visited.count(_conditionalJump.nonZero); + if (zeroVisited && nonZeroVisited) + { + // If the current iteration has already visited both jump targets, start from its entry layout. + Stack stack = combineStack( + m_layout.blockInfos.at(_conditionalJump.zero).entryLayout, + m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout + ); + // Additionally, the jump condition has to be at the stack top at exit. + stack.emplace_back(_conditionalJump.condition); + return stack; + } + // If one of the jump targets has not been visited, stage it for visit and defer the current block. + if (!zeroVisited) + _toVisit.emplace_front(_conditionalJump.zero); + if (!nonZeroVisited) + _toVisit.emplace_front(_conditionalJump.nonZero); + return nullopt; + }, + [&](CFG::BasicBlock::FunctionReturn const& _functionReturn) -> std::optional + { + // A function return needs the return variables and the function return label slot on stack. + yulAssert(_functionReturn.info, ""); + Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){ + return StackSlot{_varSlot}; + }) | ranges::to; + stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function}); + return stack; + }, + [&](CFG::BasicBlock::Terminated const&) -> std::optional + { + // A terminating block can have an empty stack on exit. + return Stack{}; + }, + }, _block.exit); +} + +list> StackLayoutGenerator::collectBackwardsJumps(CFG::BasicBlock const& _entry) const +{ + list> backwardsJumps; + util::BreadthFirstSearch{{&_entry}}.run([&](CFG::BasicBlock const* _block, auto _addChild) { + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) {}, + [&](CFG::BasicBlock::Jump const& _jump) + { + if (_jump.backwards) + backwardsJumps.emplace_back(_block, _jump.target); + _addChild(_jump.target); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + _addChild(_conditionalJump.zero); + _addChild(_conditionalJump.nonZero); + }, + [&](CFG::BasicBlock::FunctionReturn const&) {}, + [&](CFG::BasicBlock::Terminated const&) {}, + }, _block->exit); + }); + return backwardsJumps; +} + +void StackLayoutGenerator::stitchConditionalJumps(CFG::BasicBlock const& _block) +{ + util::BreadthFirstSearch breadthFirstSearch{{&_block}}; + breadthFirstSearch.run([&](CFG::BasicBlock const* _block, auto _addChild) { + auto& info = m_layout.blockInfos.at(_block); + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) {}, + [&](CFG::BasicBlock::Jump const& _jump) + { + if (!_jump.backwards) + _addChild(_jump.target); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + auto& zeroTargetInfo = m_layout.blockInfos.at(_conditionalJump.zero); + auto& nonZeroTargetInfo = m_layout.blockInfos.at(_conditionalJump.nonZero); + Stack exitLayout = info.exitLayout; + + // The last block must have produced the condition at the stack top. + yulAssert(!exitLayout.empty(), ""); + yulAssert(exitLayout.back() == _conditionalJump.condition, ""); + // The condition is consumed by the jump. + exitLayout.pop_back(); + + auto fixJumpTargetEntry = [&](Stack const& _originalEntryLayout) -> Stack { + Stack newEntryLayout = exitLayout; + // Whatever the block being jumped to does not actually require, can be marked as junk. + for (auto& slot: newEntryLayout) + if (!util::contains(_originalEntryLayout, slot)) + slot = JunkSlot{}; + // Make sure everything the block being jumped to requires is actually present or can be generated. + for (auto const& slot: _originalEntryLayout) + yulAssert(canBeFreelyGenerated(slot) || util::contains(newEntryLayout, slot), ""); + return newEntryLayout; + }; + zeroTargetInfo.entryLayout = fixJumpTargetEntry(zeroTargetInfo.entryLayout); + nonZeroTargetInfo.entryLayout = fixJumpTargetEntry(nonZeroTargetInfo.entryLayout); + _addChild(_conditionalJump.zero); + _addChild(_conditionalJump.nonZero); + }, + [&](CFG::BasicBlock::FunctionReturn const&) {}, + [&](CFG::BasicBlock::Terminated const&) { }, + }, _block->exit); + }); +} + +Stack StackLayoutGenerator::combineStack(Stack const& _stack1, Stack const& _stack2) +{ + // TODO: it would be nicer to replace this by a constructive algorithm. + // Currently it uses a reduced version of the Heap Algorithm to partly brute-force, which seems + // to work decently well. + + Stack commonPrefix; + for (auto&& [slot1, slot2]: ranges::zip_view(_stack1, _stack2)) + { + if (!(slot1 == slot2)) + break; + commonPrefix.emplace_back(slot1); + } + + Stack stack1Tail = _stack1 | ranges::views::drop(commonPrefix.size()) | ranges::to; + Stack stack2Tail = _stack2 | ranges::views::drop(commonPrefix.size()) | ranges::to; + + if (stack1Tail.empty()) + return commonPrefix + compressStack(stack2Tail); + if (stack2Tail.empty()) + return commonPrefix + compressStack(stack1Tail); + + Stack candidate; + for (auto slot: stack1Tail) + if (!util::contains(candidate, slot)) + candidate.emplace_back(slot); + for (auto slot: stack2Tail) + if (!util::contains(candidate, slot)) + candidate.emplace_back(slot); + cxx20::erase_if(candidate, [](StackSlot const& slot) { + return holds_alternative(slot) || holds_alternative(slot); + }); + + auto evaluate = [&](Stack const& _candidate) -> size_t { + size_t numOps = 0; + Stack testStack = _candidate; + auto swap = [&](unsigned _swapDepth) { ++numOps; if (_swapDepth > 16) numOps += 1000; }; + auto dupOrPush = [&](StackSlot const& _slot) + { + if (canBeFreelyGenerated(_slot)) + return; + auto depth = util::findOffset(ranges::concat_view(commonPrefix, testStack) | ranges::views::reverse, _slot); + if (depth && *depth >= 16) + numOps += 1000; + }; + createStackLayout(testStack, stack1Tail, swap, dupOrPush, [&](){}); + testStack = _candidate; + createStackLayout(testStack, stack2Tail, swap, dupOrPush, [&](){}); + return numOps; + }; + + // See https://en.wikipedia.org/wiki/Heap's_algorithm + size_t n = candidate.size(); + Stack bestCandidate = candidate; + size_t bestCost = evaluate(candidate); + std::vector c(n, 0); + size_t i = 1; + while (i < n) + { + if (c[i] < i) + { + if (i & 1) + std::swap(candidate.front(), candidate[i]); + else + std::swap(candidate[c[i]], candidate[i]); + size_t cost = evaluate(candidate); + if (cost < bestCost) + { + bestCost = cost; + bestCandidate = candidate; + } + ++c[i]; + // Note that for a proper implementation of the Heap algorithm this would need to revert back to ``i = 1.`` + // However, the incorrect implementation produces decent result and the proper version would have n! + // complexity and is thereby not feasible. + ++i; + } + else + { + c[i] = 0; + ++i; + } + } + + return commonPrefix + bestCandidate; +} + +vector StackLayoutGenerator::reportStackTooDeep(CFG::BasicBlock const& _entry) const +{ + vector stackTooDeepErrors; + util::BreadthFirstSearch breadthFirstSearch{{&_entry}}; + breadthFirstSearch.run([&](CFG::BasicBlock const* _block, auto _addChild) { + Stack currentStack = m_layout.blockInfos.at(_block).entryLayout; + + for (auto const& operation: _block->operations) + { + Stack& operationEntry = m_layout.operationEntryLayout.at(&operation); + + stackTooDeepErrors += findStackTooDeep(currentStack, operationEntry); + currentStack = operationEntry; + for (size_t i = 0; i < operation.input.size(); i++) + currentStack.pop_back(); + currentStack += operation.output; + } + // Do not attempt to create the exit layout m_layout.blockInfos.at(_block).exitLayout here, + // since the code generator will directly move to the target entry layout. + + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) {}, + [&](CFG::BasicBlock::Jump const& _jump) + { + Stack const& targetLayout = m_layout.blockInfos.at(_jump.target).entryLayout; + stackTooDeepErrors += findStackTooDeep(currentStack, targetLayout); + + if (!_jump.backwards) + _addChild(_jump.target); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + for (Stack const& targetLayout: { + m_layout.blockInfos.at(_conditionalJump.zero).entryLayout, + m_layout.blockInfos.at(_conditionalJump.nonZero).entryLayout + }) + stackTooDeepErrors += findStackTooDeep(currentStack, targetLayout); + + _addChild(_conditionalJump.zero); + _addChild(_conditionalJump.nonZero); + }, + [&](CFG::BasicBlock::FunctionReturn const&) {}, + [&](CFG::BasicBlock::Terminated const&) {}, + }, _block->exit); + }); + return stackTooDeepErrors; +} + +Stack StackLayoutGenerator::compressStack(Stack _stack) +{ + optional firstDupOffset; + do + { + if (firstDupOffset) + { + std::swap(_stack.at(*firstDupOffset), _stack.back()); + _stack.pop_back(); + firstDupOffset.reset(); + } + for (auto&& [depth, slot]: _stack | ranges::views::reverse | ranges::views::enumerate) + if (canBeFreelyGenerated(slot)) + { + firstDupOffset = _stack.size() - depth - 1; + break; + } + else if (auto dupDepth = util::findOffset(_stack | ranges::views::reverse | ranges::views::drop(depth + 1), slot)) + if (depth + *dupDepth <= 16) + { + firstDupOffset = _stack.size() - depth - 1; + break; + } + } + while (firstDupOffset); + return _stack; +} diff --git a/libyul/backends/evm/StackLayoutGenerator.h b/libyul/backends/evm/StackLayoutGenerator.h new file mode 100644 index 000000000..803e49354 --- /dev/null +++ b/libyul/backends/evm/StackLayoutGenerator.h @@ -0,0 +1,117 @@ +/* + 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 +/** + * Stack layout generator for Yul to EVM code generation. + */ + +#pragma once + +#include + +#include + +namespace solidity::yul +{ + +struct StackLayout +{ + struct BlockInfo + { + /// Complete stack layout that is required for entering a block. + Stack entryLayout; + /// The resulting stack layout after executing the block. + Stack exitLayout; + }; + std::map blockInfos; + /// For each operation the complete stack layout that: + /// - has the slots required for the operation at the stack top. + /// - will have the operation result in a layout that makes it easy to achieve the next desired layout. + std::map operationEntryLayout; +}; + +class StackLayoutGenerator +{ +public: + struct StackTooDeep + { + /// Number of slots that need to be saved. + size_t deficit = 0; + /// Set of variables, eliminating which would decrease the stack deficit. + std::vector variableChoices; + }; + + static StackLayout run(CFG const& _cfg); + /// @returns a map from function names to the stack too deep errors occurring in that function. + /// Requires @a _cfg to be a control flow graph generated from disambiguated Yul. + /// The empty string is mapped to the stack too deep errors of the main entry point. + static std::map> reportStackTooDeep(CFG const& _cfg); + /// @returns all stack too deep errors in the function named @a _functionName. + /// Requires @a _cfg to be a control flow graph generated from disambiguated Yul. + /// If @a _functionName is empty, the stack too deep errors of the main entry point are reported instead. + static std::vector reportStackTooDeep(CFG const& _cfg, YulString _functionName); + +private: + StackLayoutGenerator(StackLayout& _context); + + /// @returns the optimal entry stack layout, s.t. @a _operation can be applied to it and + /// the result can be transformed to @a _exitStack with minimal stack shuffling. + /// Simultaneously stores the entry layout required for executing the operation in m_layout. + Stack propagateStackThroughOperation(Stack _exitStack, CFG::Operation const& _operation, bool _aggressiveStackCompression = false); + + /// @returns the desired stack layout at the entry of @a _block, assuming the layout after + /// executing the block should be @a _exitStack. + Stack propagateStackThroughBlock(Stack _exitStack, CFG::BasicBlock const& _block, bool _aggressiveStackCompression = false); + + /// Main algorithm walking the graph from entry to exit and propagating back the stack layouts to the entries. + /// Iteratively reruns itself along backwards jumps until the layout is stabilized. + void processEntryPoint(CFG::BasicBlock const& _entry); + + /// @returns the best known exit layout of @a _block, if all dependencies are already @a _visited. + /// If not, adds the dependencies to @a _dependencyList and @returns std::nullopt. + std::optional getExitLayoutOrStageDependencies( + CFG::BasicBlock const& _block, + std::set const& _visited, + std::list& _dependencyList + ) const; + + /// @returns a pair of ``{jumpingBlock, targetBlock}`` for each backwards jump in the graph starting at @a _entry. + std::list> collectBackwardsJumps(CFG::BasicBlock const& _entry) const; + + /// After the main algorithms, layouts at conditional jumps are merely compatible, i.e. the exit layout of the + /// jumping block is a superset of the entry layout of the target block. This function modifies the entry layouts + /// of conditional jump targets, s.t. the entry layout of target blocks match the exit layout of the jumping block + /// exactly, except that slots not required after the jump are marked as `JunkSlot`s. + void stitchConditionalJumps(CFG::BasicBlock const& _block); + + /// Calculates the ideal stack layout, s.t. both @a _stack1 and @a _stack2 can be achieved with minimal + /// stack shuffling when starting from the returned layout. + static Stack combineStack(Stack const& _stack1, Stack const& _stack2); + + /// Walks through the CFG and reports any stack too deep errors that would occur when generating code for it + /// without countermeasures. + std::vector reportStackTooDeep(CFG::BasicBlock const& _entry) const; + + /// @returns a copy of @a _stack stripped of all duplicates and slots that can be freely generated. + /// Attempts to create a layout that requires a minimal amount of operations to reconstruct the original + /// stack @a _stack. + static Stack compressStack(Stack _stack); + + StackLayout& m_layout; +}; + +} diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index a5d355ede..eb7a60a32 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -93,6 +93,7 @@ Object EVMToEwasmTranslator::run(Object const& _object) Object ret; ret.name = _object.name; ret.code = make_shared(move(ast)); + ret.debugData = _object.debugData; ret.analysisInfo = make_shared(); ErrorList errors; diff --git a/libyul/backends/wasm/TextTransform.cpp b/libyul/backends/wasm/TextTransform.cpp index b8cae94b7..9880f5ee5 100644 --- a/libyul/backends/wasm/TextTransform.cpp +++ b/libyul/backends/wasm/TextTransform.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -54,7 +55,7 @@ string TextTransform::run(wasm::Module const& _module) " ;; (@custom \"" + name + "\" \"" + - toHex(BinaryTransform::run(module)) + + util::toHex(BinaryTransform::run(module)) + "\")\n"; for (auto const& [name, data]: _module.customSections) ret += @@ -62,7 +63,7 @@ string TextTransform::run(wasm::Module const& _module) " ;; (@custom \"" + name + "\" \"" + - toHex(data) + + util::toHex(data) + "\")\n"; for (wasm::FunctionImport const& imp: _module.imports) { diff --git a/libyul/backends/wasm/WasmCodeTransform.cpp b/libyul/backends/wasm/WasmCodeTransform.cpp index f1254875a..32c774533 100644 --- a/libyul/backends/wasm/WasmCodeTransform.cpp +++ b/libyul/backends/wasm/WasmCodeTransform.cpp @@ -31,6 +31,7 @@ #include #include +#include using namespace std; using namespace solidity; diff --git a/libyul/backends/wasm/WasmCodeTransform.h b/libyul/backends/wasm/WasmCodeTransform.h index 98b761e8d..1026f17c6 100644 --- a/libyul/backends/wasm/WasmCodeTransform.h +++ b/libyul/backends/wasm/WasmCodeTransform.h @@ -28,6 +28,7 @@ #include #include +#include #include #include diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index e796b1f8f..ad723717b 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -27,6 +27,7 @@ #include #include #include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/DeadCodeEliminator.cpp b/libyul/optimiser/DeadCodeEliminator.cpp index d8162397c..6a279487f 100644 --- a/libyul/optimiser/DeadCodeEliminator.cpp +++ b/libyul/optimiser/DeadCodeEliminator.cpp @@ -27,6 +27,7 @@ #include #include +#include using namespace std; using namespace solidity; diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp index 2f3441fa1..9d1df8694 100644 --- a/libyul/optimiser/ExpressionJoiner.cpp +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -31,6 +31,8 @@ #include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; diff --git a/libyul/optimiser/KnowledgeBase.h b/libyul/optimiser/KnowledgeBase.h index 91dbaa58e..75e060eb1 100644 --- a/libyul/optimiser/KnowledgeBase.h +++ b/libyul/optimiser/KnowledgeBase.h @@ -25,6 +25,7 @@ #include #include +#include #include diff --git a/libyul/optimiser/LoadResolver.cpp b/libyul/optimiser/LoadResolver.cpp index b968e1bef..550ca6fb3 100644 --- a/libyul/optimiser/LoadResolver.cpp +++ b/libyul/optimiser/LoadResolver.cpp @@ -97,6 +97,13 @@ void LoadResolver::tryEvaluateKeccak( std::vector const& _arguments ) { + yulAssert(_arguments.size() == 2, ""); + Identifier const* memoryKey = std::get_if(&_arguments.at(0)); + Identifier const* length = std::get_if(&_arguments.at(1)); + + if (!memoryKey || !length) + return; + // The costs are only correct for hashes of 32 bytes or 1 word (when rounded up). GasMeter gasMeter{ dynamic_cast(m_dialect), @@ -122,13 +129,6 @@ void LoadResolver::tryEvaluateKeccak( if (costOfLiteral > costOfKeccak) return; - yulAssert(_arguments.size() == 2, ""); - Identifier const* memoryKey = std::get_if(&_arguments.at(0)); - Identifier const* length = std::get_if(&_arguments.at(1)); - - if (!memoryKey || !length) - return; - auto memoryValue = util::valueOrNullptr(m_memory, memoryKey->name); if (memoryValue && inScope(*memoryValue)) { diff --git a/libyul/optimiser/RedundantStoreBase.cpp b/libyul/optimiser/RedundantStoreBase.cpp index 4e7c21367..6fc0570b7 100644 --- a/libyul/optimiser/RedundantStoreBase.cpp +++ b/libyul/optimiser/RedundantStoreBase.cpp @@ -159,8 +159,15 @@ void RedundantStoreBase::merge(TrackedStores& _target, vector&& _ void StatementRemover::operator()(Block& _block) { - ranges::actions::remove_if(_block.statements, [&](Statement const& _statement) -> bool { - return m_toRemove.count(&_statement); - }); + util::iterateReplacing( + _block.statements, + [&](Statement& _statement) -> std::optional> + { + if (m_toRemove.count(&_statement)) + return {vector{}}; + else + return nullopt; + } + ); ASTModifier::operator()(_block); } diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index e5fec6b62..f1597a7f2 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -32,39 +32,44 @@ using namespace std; using namespace solidity; using namespace solidity::yul; -void Rematerialiser::run(Dialect const& _dialect, Block& _ast, set _varsToAlwaysRematerialize) +void Rematerialiser::run(Dialect const& _dialect, Block& _ast, set _varsToAlwaysRematerialize, bool _onlySelectedVariables) { - Rematerialiser{_dialect, _ast, std::move(_varsToAlwaysRematerialize)}(_ast); + Rematerialiser{_dialect, _ast, std::move(_varsToAlwaysRematerialize), _onlySelectedVariables}(_ast); } void Rematerialiser::run( Dialect const& _dialect, FunctionDefinition& _function, - set _varsToAlwaysRematerialize + set _varsToAlwaysRematerialize, + bool _onlySelectedVariables ) { - Rematerialiser{_dialect, _function, std::move(_varsToAlwaysRematerialize)}(_function); + Rematerialiser{_dialect, _function, std::move(_varsToAlwaysRematerialize), _onlySelectedVariables}(_function); } Rematerialiser::Rematerialiser( Dialect const& _dialect, Block& _ast, - set _varsToAlwaysRematerialize + set _varsToAlwaysRematerialize, + bool _onlySelectedVariables ): DataFlowAnalyzer(_dialect), m_referenceCounts(ReferencesCounter::countReferences(_ast)), - m_varsToAlwaysRematerialize(std::move(_varsToAlwaysRematerialize)) + m_varsToAlwaysRematerialize(std::move(_varsToAlwaysRematerialize)), + m_onlySelectedVariables(_onlySelectedVariables) { } Rematerialiser::Rematerialiser( Dialect const& _dialect, FunctionDefinition& _function, - set _varsToAlwaysRematerialize + set _varsToAlwaysRematerialize, + bool _onlySelectedVariables ): DataFlowAnalyzer(_dialect), m_referenceCounts(ReferencesCounter::countReferences(_function)), - m_varsToAlwaysRematerialize(std::move(_varsToAlwaysRematerialize)) + m_varsToAlwaysRematerialize(std::move(_varsToAlwaysRematerialize)), + m_onlySelectedVariables(_onlySelectedVariables) { } @@ -81,10 +86,13 @@ void Rematerialiser::visit(Expression& _e) size_t refs = m_referenceCounts[name]; size_t cost = CodeCost::codeCost(m_dialect, *value.value); if ( - (refs <= 1 && value.loopDepth == m_loopDepth) || - cost == 0 || - (refs <= 5 && cost <= 1 && m_loopDepth == 0) || - m_varsToAlwaysRematerialize.count(name) + ( + !m_onlySelectedVariables && ( + (refs <= 1 && value.loopDepth == m_loopDepth) || + cost == 0 || + (refs <= 5 && cost <= 1 && m_loopDepth == 0) + ) + ) || m_varsToAlwaysRematerialize.count(name) ) { assertThrow(m_referenceCounts[name] > 0, OptimizerException, ""); diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index b7e98a6b8..d1ebb1595 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -50,24 +50,28 @@ public: static void run( Dialect const& _dialect, Block& _ast, - std::set _varsToAlwaysRematerialize = {} + std::set _varsToAlwaysRematerialize = {}, + bool _onlySelectedVariables = false ); static void run( Dialect const& _dialect, FunctionDefinition& _function, - std::set _varsToAlwaysRematerialize = {} + std::set _varsToAlwaysRematerialize = {}, + bool _onlySelectedVariables = false ); protected: Rematerialiser( Dialect const& _dialect, Block& _ast, - std::set _varsToAlwaysRematerialize = {} + std::set _varsToAlwaysRematerialize = {}, + bool _onlySelectedVariables = false ); Rematerialiser( Dialect const& _dialect, FunctionDefinition& _function, - std::set _varsToAlwaysRematerialize = {} + std::set _varsToAlwaysRematerialize = {}, + bool _onlySelectedVariables = false ); using DataFlowAnalyzer::operator(); @@ -77,6 +81,7 @@ protected: std::map m_referenceCounts; std::set m_varsToAlwaysRematerialize; + bool m_onlySelectedVariables = false; }; /** diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index d51af2de2..bdd08710e 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -31,6 +31,8 @@ #include #include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 4a0e0668c..55c30d2f2 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -241,7 +241,7 @@ Expression Pattern::toExpression(shared_ptr const& _debugData) if (m_kind == PatternKind::Constant) { assertThrow(m_data, OptimizerException, "No match group and no constant value given."); - return Literal{_debugData, LiteralKind::Number, YulString{util::formatNumber(*m_data)}, {}}; + return Literal{_debugData, LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; } else if (m_kind == PatternKind::Operation) { diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index 5e9202530..a57d9738b 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -31,6 +31,8 @@ #include +#include + using namespace std; using namespace solidity; using namespace solidity::yul; @@ -47,16 +49,19 @@ class RematCandidateSelector: public DataFlowAnalyzer public: explicit RematCandidateSelector(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} - /// @returns a set of tuples of rematerialisation costs, variable to rematerialise - /// and variables that occur in its expression. - /// Note that this set is sorted by cost. - set>> candidates() + /// @returns a map from rematerialisation costs to a vector of variables to rematerialise + /// and variables that occur in their expression. + /// While the map is sorted by cost, the contained vectors are sorted by the order of occurrence. + map>>> candidates() { - set>> cand; - for (auto const& codeCost: m_expressionCodeCost) + map>>> cand; + for (auto const& candidate: m_candidates) { - size_t numRef = m_numReferences[codeCost.first]; - cand.emplace(make_tuple(codeCost.second * numRef, codeCost.first, m_references[codeCost.first])); + if (size_t const* cost = util::valueOrNullptr(m_expressionCodeCost, candidate)) + { + size_t numRef = m_numReferences[candidate]; + cand[*cost * numRef].emplace_back(candidate, m_references[candidate]); + } } return cand; } @@ -69,7 +74,11 @@ public: { YulString varName = _varDecl.variables.front().name; if (m_value.count(varName)) + { + yulAssert(!m_expressionCodeCost.count(varName), ""); + m_candidates.emplace_back(varName); m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *m_value[varName].value); + } } } @@ -105,12 +114,40 @@ public: m_expressionCodeCost.erase(_variable); } + /// All candidate variables in order of occurrence. + vector m_candidates; /// Candidate variables and the code cost of their value. map m_expressionCodeCost; /// Number of references to each candidate variable. map m_numReferences; }; +/// Selects at most @a _numVariables among @a _candidates. +set chooseVarsToEliminate( + map>>> const& _candidates, + size_t _numVariables +) +{ + set varsToEliminate; + for (auto&& [cost, candidates]: _candidates) + for (auto&& [candidate, references]: candidates) + { + if (varsToEliminate.size() >= _numVariables) + return varsToEliminate; + // If a variable we would like to eliminate references another one + // we already selected for elimination, then stop selecting + // candidates. If we would add that variable, then the cost calculation + // for the previous variable would be off. Furthermore, we + // do not skip the variable because it would be better to properly re-compute + // the costs of all other variables instead. + for (YulString const& referencedVar: references) + if (varsToEliminate.count(referencedVar)) + return varsToEliminate; + varsToEliminate.insert(candidate); + } + return varsToEliminate; +} + template void eliminateVariables( Dialect const& _dialect, @@ -121,32 +158,7 @@ void eliminateVariables( { RematCandidateSelector selector{_dialect}; selector(_node); - - // Select at most _numVariables - set varsToEliminate; - for (auto const& costs: selector.candidates()) - { - if (varsToEliminate.size() >= _numVariables) - break; - // If a variable we would like to eliminate references another one - // we already selected for elimination, then stop selecting - // candidates. If we would add that variable, then the cost calculation - // for the previous variable would be off. Furthermore, we - // do not skip the variable because it would be better to properly re-compute - // the costs of all other variables instead. - bool referencesVarToEliminate = false; - for (YulString const& referencedVar: get<2>(costs)) - if (varsToEliminate.count(referencedVar)) - { - referencesVarToEliminate = true; - break; - } - if (referencesVarToEliminate) - break; - varsToEliminate.insert(get<1>(costs)); - } - - Rematerialiser::run(_dialect, _node, std::move(varsToEliminate)); + Rematerialiser::run(_dialect, _node, chooseVarsToEliminate(selector.candidates(), _numVariables)); UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization); } diff --git a/libyul/optimiser/StackLimitEvader.cpp b/libyul/optimiser/StackLimitEvader.cpp index 8a632af53..1c7903b1f 100644 --- a/libyul/optimiser/StackLimitEvader.cpp +++ b/libyul/optimiser/StackLimitEvader.cpp @@ -163,6 +163,6 @@ void StackLimitEvader::run( { Literal* literal = std::get_if(&memoryGuardCall->arguments.front()); yulAssert(literal && literal->kind == LiteralKind::Number, ""); - literal->value = YulString{util::toCompactHexWithPrefix(reservedMemory)}; + literal->value = YulString{toCompactHexWithPrefix(reservedMemory)}; } } diff --git a/libyul/optimiser/StackToMemoryMover.cpp b/libyul/optimiser/StackToMemoryMover.cpp index 87976e116..dcc5f45f6 100644 --- a/libyul/optimiser/StackToMemoryMover.cpp +++ b/libyul/optimiser/StackToMemoryMover.cpp @@ -315,7 +315,7 @@ optional StackToMemoryMover::VariableMemoryOffsetTracker::operator()( { uint64_t slot = m_memorySlots.at(_variable); yulAssert(slot < m_numRequiredSlots, ""); - return YulString{util::toCompactHexWithPrefix(m_reservedMemory + 32 * (m_numRequiredSlots - slot - 1))}; + return YulString{toCompactHexWithPrefix(m_reservedMemory + 32 * (m_numRequiredSlots - slot - 1))}; } else return nullopt; diff --git a/libyul/optimiser/StackToMemoryMover.h b/libyul/optimiser/StackToMemoryMover.h index 0f92a4d56..64c661c0b 100644 --- a/libyul/optimiser/StackToMemoryMover.h +++ b/libyul/optimiser/StackToMemoryMover.h @@ -26,6 +26,7 @@ #include #include +#include #include diff --git a/libyul/optimiser/VarNameCleaner.cpp b/libyul/optimiser/VarNameCleaner.cpp index 55fc71fd6..09093f5aa 100644 --- a/libyul/optimiser/VarNameCleaner.cpp +++ b/libyul/optimiser/VarNameCleaner.cpp @@ -27,6 +27,7 @@ #include #include #include +#include using namespace std; using namespace solidity::yul; diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 212e547f6..daf5a6163 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -42,15 +42,24 @@ function testImportExportEquivalence { if $SOLC "$nth_input_file" "${all_input_files[@]}" > /dev/null 2>&1 then + ! [[ -e stderr.txt ]] || { echo "stderr.txt already exists. Refusing to overwrite."; exit 1; } + # save exported json as expected result (silently) - $SOLC --combined-json ast,compact-format --pretty-json "$nth_input_file" "${all_input_files[@]}" > expected.json 2> /dev/null + $SOLC --combined-json ast --pretty-json "$nth_input_file" "${all_input_files[@]}" > expected.json 2> /dev/null # import it, and export it again as obtained result (silently) - if ! $SOLC --import-ast --combined-json ast,compact-format --pretty-json expected.json > obtained.json 2> /dev/null + if ! $SOLC --import-ast --combined-json ast --pretty-json expected.json > obtained.json 2> stderr.txt then # For investigating, use exit 1 here so the script stops at the # first failing test # exit 1 FAILED=$((FAILED + 1)) + echo -e "ERROR: AST reimport failed for input file $nth_input_file" + echo + echo "Compiler stderr:" + cat ./stderr.txt + echo + echo "Compiler stdout:" + cat ./obtained.json return 1 fi DIFF="$(diff expected.json obtained.json)" @@ -72,6 +81,7 @@ function testImportExportEquivalence { fi TESTED=$((TESTED + 1)) rm expected.json obtained.json + rm -f stderr.txt else # echo "contract $solfile could not be compiled " UNCOMPILABLE=$((UNCOMPILABLE + 1)) diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index 7c8ce9b0d..33463f198 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -101,12 +101,13 @@ class Statistics: self.missing_metadata_count += sum(1 for c in contract_reports if c.metadata is None) def __str__(self) -> str: - return "test cases: {}, contracts: {}, errors: {}, missing bytecode: {}, missing metadata: {}".format( - self.file_count, - str(self.contract_count) + ('+' if self.error_count > 0 else ''), - self.error_count, - self.missing_bytecode_count, - self.missing_metadata_count, + contract_count = str(self.contract_count) + ('+' if self.error_count > 0 else '') + return ( + f"test cases: {self.file_count}, " + f"contracts: {contract_count}, " + f"errors: {self.error_count}, " + f"missing bytecode: {self.missing_bytecode_count}, " + f"missing metadata: {self.missing_metadata_count}" ) diff --git a/scripts/common.sh b/scripts/common.sh index 7df8b85db..b3a5f82e4 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -33,6 +33,80 @@ else function printLog() { echo "$(tput setaf 3)$1$(tput sgr0)"; } fi +function fail() +{ + printError "$@" + return 1 +} + +function msg_on_error() +{ + local error_message + local no_stdout=false + local no_stderr=false + + while [[ $1 =~ ^-- ]] + do + case "$1" in + --msg) + error_message="$2" + shift + shift + ;; + --no-stdout) + no_stdout=true + shift + ;; + --no-stderr) + no_stderr=true + shift + ;; + --silent) + no_stdout=true + no_stderr=true + shift + ;; + *) + fail "Invalid option for msg_on_error: $1" + ;; + esac + done + + local command=("$@") + + local stdout_file stderr_file + stdout_file="$(mktemp -t cmdline_test_command_stdout_XXXXXX.txt)" + stderr_file="$(mktemp -t cmdline_test_command_stderr_XXXXXX.txt)" + + if "${command[@]}" > "$stdout_file" 2> "$stderr_file" + then + [[ $no_stdout == "true" ]] || cat "$stdout_file" + [[ $no_stderr == "true" ]] || >&2 cat "$stderr_file" + rm "$stdout_file" "$stderr_file" + return 0 + else + printError "Command failed: $SOLC ${command[*]}" + if [[ -s "$stdout_file" ]] + then + printError "stdout:" + >&2 cat "$stdout_file" + else + printError "stdout: " + fi + if [[ -s "$stderr_file" ]] + then + printError "stderr:" + >&2 cat "$stderr_file" + else + printError "stderr: " + fi + + printError "$error_message" + rm "$stdout_file" "$stderr_file" + return 1 + fi +} + safe_kill() { local PID=${1} diff --git a/scripts/common_cmdline.sh b/scripts/common_cmdline.sh index 59c334a4b..6718ca5b5 100644 --- a/scripts/common_cmdline.sh +++ b/scripts/common_cmdline.sh @@ -20,7 +20,7 @@ # ------------------------------------------------------------------------------ YULARGS=(--strict-assembly) -FULLARGS=(--optimize --ignore-missing --combined-json "abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc") +FULLARGS=(--optimize --ignore-missing --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,hashes,metadata,opcodes,srcmap,srcmap-runtime,userdoc") OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc") function compileFull() { diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index df5c2967e..1ea8c0b57 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -31,7 +31,15 @@ REPO_ROOT="$(dirname "$0")"/.. fi # Add dependencies mkdir -p "$SOLDIR/deps/downloads/" 2>/dev/null || true - wget -O "$SOLDIR/deps/downloads/jsoncpp-1.9.3.tar.gz" https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz + jsoncpp_version="1.9.3" + jsoncpp_package_path="$SOLDIR/deps/downloads/jsoncpp-${jsoncpp_version}.tar.gz" + jsoncpp_sha256=8593c1d69e703563d94d8c12244e2e18893eeb9a8a9f8aa3d09a327aa45c8f7d + wget -O "$jsoncpp_package_path" "https://github.com/open-source-parsers/jsoncpp/archive/${jsoncpp_version}.tar.gz" + if ! [ "$(sha256sum "$jsoncpp_package_path")" = "${jsoncpp_sha256} ${jsoncpp_package_path}" ] + then + >&2 echo "ERROR: Downloaded jsoncpp source package has wrong checksum." + exit 1 + fi mkdir -p "$REPO_ROOT/upload" tar --owner 0 --group 0 -czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring" rm -r "$TEMPDIR" diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index 5e32ef9bf..48e9839dd 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -19,10 +19,10 @@ # You should have received a copy of the GNU General Public License # along with solidity. If not, see # -# (c) 2016-2019 solidity contributors. +# (c) 2016-2021 solidity contributors. #------------------------------------------------------------------------------ -FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="11" +FROM gcr.io/oss-fuzz-base/base-clang:latest as base +LABEL version="13" ARG DEBIAN_FRONTEND=noninteractive @@ -33,12 +33,13 @@ RUN apt-get update; \ ninja-build git wget \ 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; + apt-get install -qy python3-pip; -# Install cmake 3.14 (minimum requirement is cmake 3.10) -RUN wget https://github.com/Kitware/CMake/releases/download/v3.14.5/cmake-3.14.5-Linux-x86_64.sh; \ - chmod +x cmake-3.14.5-Linux-x86_64.sh; \ - ./cmake-3.14.5-Linux-x86_64.sh --skip-license --prefix="/usr" +# Install cmake 3.21.2 (minimum requirement is cmake 3.10) +RUN wget https://github.com/Kitware/CMake/releases/download/v3.21.2/cmake-3.21.2-Linux-x86_64.sh; \ + test "$(sha256sum cmake-3.21.2-Linux-x86_64.sh)" = "3310362c6fe4d4b2dc00823835f3d4a7171bbd73deb7d059738494761f1c908c cmake-3.21.2-Linux-x86_64.sh"; \ + chmod +x cmake-3.21.2-Linux-x86_64.sh; \ + ./cmake-3.21.2-Linux-x86_64.sh --skip-license --prefix="/usr" FROM base AS libraries @@ -92,7 +93,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.8.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.8.2" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 320074ff1..8d919e41b 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -192,8 +192,11 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): # white list of ids which are not covered by tests white_ids = { "9804", # Tested in test/libyul/ObjectParser.cpp. + "1544", + "1749", "2674", "6367", + "8387", "3805", # "This is a pre-release compiler version, please do not use it in production." # The warning may or may not exist in a compiler build. "4591", # "There are more than 256 warnings. Ignoring the rest." @@ -225,7 +228,7 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): old_source_only_ids = { "1584", "1823", - "1988", "2066", "3356", + "1988", "2066", "2833", "3356", "3893", "3996", "4010", "4802", "5272", "5622", "7128", "7400", "7589", "7593", "7649", "7710", diff --git a/scripts/extract_test_cases.py b/scripts/extract_test_cases.py index 4277271cf..c43cabce2 100755 --- a/scripts/extract_test_cases.py +++ b/scripts/extract_test_cases.py @@ -23,7 +23,7 @@ def extract_test_cases(_path): for l in lines: if inside: if l.strip().endswith(')' + delimiter + '";'): - with open('%03d_%s.sol' % (ctr, test_name), mode='wb', encoding='utf8') as f: + with open(f'{ctr:03d}_{test_name}.sol', mode='wb', encoding='utf8') as f: f.write(test) ctr += 1 inside = False diff --git a/scripts/install_deps.ps1 b/scripts/install_deps.ps1 index 02f0419c9..e7726993c 100644 --- a/scripts/install_deps.ps1 +++ b/scripts/install_deps.ps1 @@ -7,14 +7,20 @@ 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 + if ((Get-FileHash cmake.zip).Hash -ne "5f4ec834fbd9b62fbf73bc48ed459fa2ea6a86c403106c90fedc2ac76d51612d") { + throw 'Downloaded CMake source package has wrong checksum.' + } tar -xf cmake.zip mv cmake-3.18.2-win64-x64 "$PSScriptRoot\..\deps\cmake" # FIXME: The default user agent results in Artifactory treating Invoke-WebRequest as a browser # and serving it a page that requires JavaScript. - Invoke-WebRequest -URI "https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.zip" -OutFile boost.zip -UserAgent "" + Invoke-WebRequest -URI "https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.zip" -OutFile boost.zip -UserAgent "" + if ((Get-FileHash boost.zip).Hash -ne "d2886ceff60c35fc6dc9120e8faa960c1e9535f2d7ce447469eae9836110ea77") { + throw 'Downloaded Boost source package has wrong checksum.' + } tar -xf boost.zip - cd boost_1_74_0 + cd boost_1_77_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." } diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index 8fa65667a..ddea2e8b7 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -93,8 +93,8 @@ case $(uname -s) in 10.15) echo "Installing solidity dependencies on macOS 10.15 Catalina." ;; - 11.0 | 11.1 | 11.2 | 11.3 | 11.4) - echo "Installing solidity dependencies on macOS 11.0 / 11.1 / 11.2 / 11.3 / 11.4 Big Sur." + 11.0 | 11.1 | 11.2 | 11.3 | 11.4 | 11.5) + echo "Installing solidity dependencies on macOS 11.0 / 11.1 / 11.2 / 11.3 / 11.4 / 11.5 Big Sur." ;; *) echo "Unsupported macOS version." diff --git a/scripts/install_obsolete_jsoncpp_1_7_4.sh b/scripts/install_obsolete_jsoncpp_1_7_4.sh index eb617e651..825d1a58a 100755 --- a/scripts/install_obsolete_jsoncpp_1_7_4.sh +++ b/scripts/install_obsolete_jsoncpp_1_7_4.sh @@ -4,13 +4,21 @@ set -eu TEMPDIR=$(mktemp -d) ( cd "$TEMPDIR" - wget https://github.com/open-source-parsers/jsoncpp/archive/1.7.4.tar.gz - tar xvzf "1.7.4.tar.gz" - cd "jsoncpp-1.7.4" + jsoncpp_version="1.7.4" + jsoncpp_package="jsoncpp-${jsoncpp_version}.tar.gz" + jsoncpp_sha256=10dcd0677e80727e572a1e462193e51a5fde3e023b99e144b2ee1a469835f769 + wget -O "$jsoncpp_package" https://github.com/open-source-parsers/jsoncpp/archive/${jsoncpp_version}.tar.gz + if ! [ "$(sha256sum "$jsoncpp_package")" = "${jsoncpp_sha256} ${jsoncpp_package}" ] + then + >&2 echo "ERROR: Downloaded jsoncpp source package has wrong checksum." + exit 1 + fi + tar xvzf "$jsoncpp_package" + cd "jsoncpp-${jsoncpp_version}" mkdir -p build cd build cmake -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" .. make make install ) -rm -rf "$TEMPDIR" +rm -r "$TEMPDIR" diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index f3a121f3d..93cf2755c 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -58,7 +58,7 @@ def extract_yul_docs_cases(path): if line.startswith("//"): continue if not line.startswith("object") and not line.startswith("{"): - return indent("{{\n{}\n}}\n\n".format(code.rstrip()), " ") + return indent(f"{{\n{code.rstrip()}\n}}\n\n", " ") break return code @@ -107,7 +107,8 @@ def write_cases(f, solidityTests, yulTests): # When code examples are extracted they are indented by 8 spaces, which violates the style guide, # so before checking remove 4 spaces from each line. remainder = dedent(test) - sol_filename = 'test_%s_%s.%s' % (hashlib.sha256(test.encode("utf-8")).hexdigest(), cleaned_filename, language) + hash = hashlib.sha256(test.encode("utf-8")).hexdigest() + sol_filename = f'test_{hash}_{cleaned_filename}.{language}' with open(sol_filename, mode='w', encoding='utf8', newline='') as fi: fi.write(remainder) diff --git a/scripts/regressions.py b/scripts/regressions.py index 185d7bbdf..1efbb48b5 100755 --- a/scripts/regressions.py +++ b/scripts/regressions.py @@ -101,22 +101,20 @@ class regressor(): """ testStatus = [] - for fuzzer in glob.iglob("{}/*_ossfuzz".format(self._fuzzer_path)): + for fuzzer in glob.iglob(f"{self._fuzzer_path}/*_ossfuzz"): basename = os.path.basename(fuzzer) - logfile = os.path.join(self._logpath, "{}.log".format(basename)) - corpus_dir = "/tmp/solidity-fuzzing-corpus/{0}_seed_corpus" \ - .format(basename) - cmd = "find {0} -type f | xargs -n1 sh -c '{1} $0 || exit 255'".format(corpus_dir, fuzzer) + logfile = os.path.join(self._logpath, f"{basename}.log") + corpus_dir = f"/tmp/solidity-fuzzing-corpus/{basename}_seed_corpus" + cmd = f"find {corpus_dir} -type f | xargs -n1 sh -c '{fuzzer} $0 || exit 255'" self.run_cmd(cmd, logfile=logfile) ret = self.process_log(logfile) if not ret: print( - "\t[-] libFuzzer reported failure for {0}. " - "Failure logged to test_results".format( - basename)) + f"\t[-] libFuzzer reported failure for {basename}. " + "Failure logged to test_results") testStatus.append(False) else: - print("\t[+] {0} passed regression tests.".format(basename)) + print(f"\t[+] {basename} passed regression tests.") testStatus.append(True) return all(testStatus) diff --git a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh index 006ae475e..3401c127f 100755 --- a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh +++ b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh @@ -109,7 +109,6 @@ cd "$tmp_dir" git clone https://github.com/ethereum/solc-js.git "$solcjs_dir" cd "$solcjs_dir" npm install -rm soljson.js cd "${solc_bin_dir}/${platform}/" echo "Commit range: ${base_ref}..${top_ref}" diff --git a/scripts/splitSources.py b/scripts/splitSources.py index bdd53bf0a..3d1c8ef45 100755 --- a/scripts/splitSources.py +++ b/scripts/splitSources.py @@ -65,7 +65,7 @@ if __name__ == '__main__': # decide if file has multiple sources with open(filePath, mode='r', encoding='utf8', newline='') as f: lines = f.read().splitlines() - if lines[0][:12] == "==== Source:": + if len(lines) >= 1 and lines[0][:12] == "==== Source:": hasMultipleSources = True writeSourceToFile(lines) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 333cea0df..97aaa3b93 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -120,7 +120,13 @@ done < <( # a variable declaration. grep -v -E 'revertStatement/non_called.sol' | # Skipping a test with "let basefee := ..." - grep -v -E 'inlineAssembly/basefee_berlin_function.sol' + grep -v -E 'inlineAssembly/basefee_berlin_function.sol' | + # Skipping license error, unrelated to the grammar + grep -v -E 'license/license_double5.sol' | + grep -v -E 'license/license_hidden_unicode.sol' | + grep -v -E 'license/license_unicode.sol' | + # Skipping tests with 'something.address' as 'address' as the grammar fails on those + grep -v -E 'inlineAssembly/external_function_pointer_address.*.sol' ) YUL_FILES=() diff --git a/scripts/tests.sh b/scripts/tests.sh index 1dcf2ae01..964d00668 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -52,17 +52,32 @@ cleanup() { } trap cleanup INT TERM -if [ "$1" = --junit_report ] -then - if [ -z "$2" ] - then - echo "Usage: $0 [--junit_report ]" - exit 1 - fi - log_directory="$2" -else - log_directory="" -fi +log_directory="" +no_smt="" +while [[ $# -gt 0 ]] +do + case "$1" in + --junit_report) + if [ -z "$2" ] + then + echo "Usage: $0 [--junit_report ] [--no-smt]" + exit 1 + else + log_directory="$2" + fi + shift + shift + ;; + --no-smt) + no_smt="--no-smt" + SMT_FLAGS+=(--no-smt) + shift + ;; + *) + echo "Usage: $0 [--junit_report ] [--no-smt]" + exit 1 + esac +done printTask "Testing Python scripts..." "$REPO_ROOT/test/pyscriptTests.py" @@ -74,7 +89,7 @@ then "$REPO_ROOT/test/cmdlineTests.sh" & CMDLINE_PID=$! else - if ! "$REPO_ROOT/test/cmdlineTests.sh" + if ! "$REPO_ROOT/test/cmdlineTests.sh" "$no_smt" then printError "Commandline tests FAILED" exit 1 diff --git a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py index 53a78a990..635ebcc31 100755 --- a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py +++ b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py @@ -46,7 +46,8 @@ def write_cases(f, tests): cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower() for test in tests: remainder = re.sub(r'^ {4}', '', test, 0, re.MULTILINE) - with open('test_%s_%s.sol' % (hashlib.sha256(test).hexdigest(), cleaned_filename), 'w', encoding='utf8') as _f: + hash = hashlib.sha256(test).hexdigest() + with open(f'test_{hash}_{cleaned_filename}.sol', 'w', encoding='utf8') as _f: _f.write(remainder) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index d732b9077..0826749ee 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -13,12 +13,13 @@ description: | It is possible to create contracts for voting, crowdfunding, blind auctions, multi-signature wallets and more. +base: core20 grade: stable confinement: strict apps: solc: - command: solc + command: usr/local/bin/solc plugs: [home] parts: @@ -27,7 +28,7 @@ parts: source-type: git plugin: cmake build-packages: [build-essential, libboost-all-dev] - stage-packages: [libicu60] + stage-packages: [libicu66] override-build: | if git describe --exact-match --tags 2> /dev/null then @@ -39,9 +40,8 @@ parts: source: https://github.com/Z3Prover/z3.git source-tag: z3-4.8.4 plugin: make - build-packages: [python] + build-packages: [python3] stage-packages: [libstdc++6] - makefile: build/Makefile override-build: | python scripts/mk_make.py cd build @@ -49,14 +49,13 @@ parts: make install DESTDIR=$SNAPCRAFT_PART_INSTALL cvc4: source: https://github.com/CVC4/CVC4.git - source-tag: "1.7" + source-tag: "1.8" plugin: nil - build-packages: [python, cmake, openjdk-11-jre, libgmp-dev, wget] + build-packages: [python3, python3-toml, cmake, openjdk-11-jre, libgmp-dev, wget, antlr3, libantlr3c-3.4-0, libantlr3c-dev] override-build: | - ./contrib/get-antlr-3.4 - ./configure.sh --prefix=$SNAPCRAFT_STAGE/usr + ./configure.sh --python3 --prefix=$SNAPCRAFT_STAGE/usr cd build make -j -l $(grep -c "^processor" /proc/cpuinfo) make install mkdir -p $SNAPCRAFT_PART_INSTALL/usr/lib/ - cp $SNAPCRAFT_STAGE/usr/lib/libcvc4.so.6 $SNAPCRAFT_PART_INSTALL/usr/lib/ + cp $SNAPCRAFT_STAGE/usr/lib/libcvc4.so.7 $SNAPCRAFT_PART_INSTALL/usr/lib/ diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 549991d61..dfe5cfc5c 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -154,6 +154,8 @@ static bool coloredOutput(CommandLineOptions const& _options) void CommandLineInterface::handleBinary(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (m_options.compiler.outputs.binary) { if (!m_options.output.dir.empty()) @@ -178,6 +180,8 @@ void CommandLineInterface::handleBinary(string const& _contract) void CommandLineInterface::handleOpcode(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.output.dir.empty()) createFile(m_compiler->filesystemFriendlyName(_contract) + ".opcode", evmasm::disassemble(m_compiler->object(_contract).bytecode)); else @@ -190,6 +194,8 @@ void CommandLineInterface::handleOpcode(string const& _contract) void CommandLineInterface::handleIR(string const& _contractName) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.ir) return; @@ -204,6 +210,8 @@ void CommandLineInterface::handleIR(string const& _contractName) void CommandLineInterface::handleIROptimized(string const& _contractName) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.irOptimized) return; @@ -218,6 +226,8 @@ void CommandLineInterface::handleIROptimized(string const& _contractName) void CommandLineInterface::handleEwasm(string const& _contractName) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.ewasm) return; @@ -239,6 +249,8 @@ void CommandLineInterface::handleEwasm(string const& _contractName) void CommandLineInterface::handleBytecode(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (m_options.compiler.outputs.opcodes) handleOpcode(_contract); if (m_options.compiler.outputs.binary || m_options.compiler.outputs.binaryRuntime) @@ -247,6 +259,8 @@ void CommandLineInterface::handleBytecode(string const& _contract) void CommandLineInterface::handleSignatureHashes(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.signatureHashes) return; @@ -263,6 +277,8 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract) void CommandLineInterface::handleMetadata(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.metadata) return; @@ -275,6 +291,8 @@ void CommandLineInterface::handleMetadata(string const& _contract) void CommandLineInterface::handleABI(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.abi) return; @@ -287,6 +305,8 @@ void CommandLineInterface::handleABI(string const& _contract) void CommandLineInterface::handleStorageLayout(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.storageLayout) return; @@ -299,6 +319,8 @@ void CommandLineInterface::handleStorageLayout(string const& _contract) void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + bool enabled = false; std::string suffix; std::string title; @@ -339,6 +361,8 @@ void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contra void CommandLineInterface::handleGasEstimation(string const& _contract) { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + Json::Value estimates = m_compiler->gasEstimates(_contract); sout() << "Gas estimation:" << endl; @@ -398,9 +422,30 @@ bool CommandLineInterface::readInputFiles() } } + for (boost::filesystem::path const& includePath: m_options.input.includePaths) + m_fileReader.addIncludePath(includePath); + for (boost::filesystem::path const& allowedDirectory: m_options.input.allowedDirectories) m_fileReader.allowDirectory(allowedDirectory); + map> collisions = + m_fileReader.detectSourceUnitNameCollisions(m_options.input.paths); + if (!collisions.empty()) + { + auto pathToQuotedString = [](boost::filesystem::path const& _path){ return "\"" + _path.string() + "\""; }; + + serr() << "Source unit name collision detected. "; + serr() << "The specified values of base path and/or include paths would result in multiple "; + serr() << "input files being assigned the same source unit name:" << endl; + for (auto const& [sourceUnitName, normalizedInputPaths]: collisions) + { + serr() << sourceUnitName << " matches: "; + serr() << joinHumanReadable(normalizedInputPaths | ranges::views::transform(pathToQuotedString)) << endl; + } + + return false; + } + for (boost::filesystem::path const& infile: m_options.input.paths) { if (!boost::filesystem::exists(infile)) @@ -465,6 +510,8 @@ bool CommandLineInterface::readInputFiles() map CommandLineInterface::parseAstFromInput() { + solAssert(m_options.input.mode == InputMode::CompilerWithASTImport, ""); + map sourceJsons; map tmpSources; @@ -551,13 +598,7 @@ bool CommandLineInterface::processInput() } case InputMode::Assembler: { - return assemble( - m_options.assembly.inputLanguage, - m_options.assembly.targetMachine, - m_options.optimizer.enabled, - m_options.optimizer.expectedExecutionsPerDeployment, - m_options.optimizer.yulSteps - ); + return assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine); } case InputMode::Linker: return link(); @@ -594,17 +635,28 @@ bool CommandLineInterface::compile() m_compiler->enableIRGeneration(m_options.compiler.outputs.ir || m_options.compiler.outputs.irOptimized); m_compiler->enableEwasmGeneration(m_options.compiler.outputs.ewasm); + m_compiler->enableEvmBytecodeGeneration( + m_options.compiler.estimateGas || + m_options.compiler.outputs.asm_ || + m_options.compiler.outputs.asmJson || + m_options.compiler.outputs.opcodes || + m_options.compiler.outputs.binary || + m_options.compiler.outputs.binaryRuntime || + (m_options.compiler.combinedJsonRequests && ( + m_options.compiler.combinedJsonRequests->binary || + m_options.compiler.combinedJsonRequests->binaryRuntime || + m_options.compiler.combinedJsonRequests->opcodes || + m_options.compiler.combinedJsonRequests->asm_ || + m_options.compiler.combinedJsonRequests->generatedSources || + m_options.compiler.combinedJsonRequests->generatedSourcesRuntime || + m_options.compiler.combinedJsonRequests->srcMap || + m_options.compiler.combinedJsonRequests->srcMapRuntime || + m_options.compiler.combinedJsonRequests->funDebug || + m_options.compiler.combinedJsonRequests->funDebugRuntime + )) + ); - OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); - if (m_options.optimizer.expectedExecutionsPerDeployment.has_value()) - settings.expectedExecutionsPerDeployment = m_options.optimizer.expectedExecutionsPerDeployment.value(); - if (m_options.optimizer.noOptimizeYul) - settings.runYulOptimiser = false; - - if (m_options.optimizer.yulSteps.has_value()) - settings.yulOptimiserSteps = m_options.optimizer.yulSteps.value(); - settings.optimizeStackAllocation = settings.runYulOptimiser; - m_compiler->setOptimiserSettings(settings); + m_compiler->setOptimiserSettings(m_options.optimiserSettings()); if (m_options.input.mode == InputMode::CompilerWithASTImport) { @@ -706,6 +758,8 @@ bool CommandLineInterface::compile() void CommandLineInterface::handleCombinedJSON() { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.combinedJsonRequests.has_value()) return; @@ -796,6 +850,8 @@ void CommandLineInterface::handleCombinedJSON() void CommandLineInterface::handleAst() { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + if (!m_options.compiler.outputs.astCompactJson) return; @@ -843,6 +899,8 @@ bool CommandLineInterface::actOnInput() bool CommandLineInterface::link() { + solAssert(m_options.input.mode == InputMode::Linker, ""); + // Map from how the libraries will be named inside the bytecode to their addresses. map librariesReplacements; int const placeholderSize = 40; // 20 bytes or 40 hex characters @@ -906,6 +964,8 @@ bool CommandLineInterface::link() void CommandLineInterface::writeLinkedFiles() { + solAssert(m_options.input.mode == InputMode::Linker, ""); + for (auto const& src: m_fileReader.sourceCodes()) if (src.first == g_stdinFileName) sout() << src.second << endl; @@ -939,27 +999,23 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _ return out; } -bool CommandLineInterface::assemble( - yul::AssemblyStack::Language _language, - yul::AssemblyStack::Machine _targetMachine, - bool _optimize, - optional _expectedExecutionsPerDeployment, - optional _yulOptimiserSteps -) +bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine) { - solAssert(_optimize || !_yulOptimiserSteps.has_value(), ""); + solAssert(m_options.input.mode == InputMode::Assembler, ""); bool successful = true; map assemblyStacks; for (auto const& src: m_fileReader.sourceCodes()) { - OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal(); - if (_expectedExecutionsPerDeployment.has_value()) - settings.expectedExecutionsPerDeployment = _expectedExecutionsPerDeployment.value(); - if (_yulOptimiserSteps.has_value()) - settings.yulOptimiserSteps = _yulOptimiserSteps.value(); + // --no-optimize-yul option is not accepted in assembly mode. + solAssert(!m_options.optimizer.noOptimizeYul, ""); + + auto& stack = assemblyStacks[src.first] = yul::AssemblyStack( + m_options.output.evmVersion, + _language, + m_options.optimiserSettings() + ); - auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_options.output.evmVersion, _language, settings); try { if (!stack.parseAndAnalyze(src.first, src.second)) @@ -997,7 +1053,7 @@ bool CommandLineInterface::assemble( m_hasOutput = true; formatter.printErrorInformation(*error); } - if (!Error::containsOnlyWarnings(stack.errors())) + if (Error::containsErrors(stack.errors())) successful = false; } @@ -1089,6 +1145,8 @@ bool CommandLineInterface::assemble( void CommandLineInterface::outputCompilationResults() { + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + handleCombinedJSON(); // do we need AST output? diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 488ec1d71..5504e51bb 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -74,13 +74,7 @@ private: /// @returns the full object with library placeholder hints in hex. static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj); - bool assemble( - yul::AssemblyStack::Language _language, - yul::AssemblyStack::Machine _targetMachine, - bool _optimize, - std::optional _expectedExecutionsPerDeployment = std::nullopt, - std::optional _yulOptimiserSteps = std::nullopt - ); + bool assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine); void outputCompilationResults(); @@ -101,7 +95,7 @@ private: /// Tries to read @ m_sourceCodes as a JSONs holding ASTs /// such that they can be imported into the compiler (importASTs()) - /// (produced by --combined-json ast,compact-format + /// (produced by --combined-json ast /// or standard-json output std::map parseAstFromInput(); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 8ce0dfcfe..26b8dd255 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -51,40 +51,27 @@ ostream& CommandLineParser::serr() #define cout #define cerr -static string const g_strAbi = "abi"; static string const g_strAllowPaths = "allow-paths"; static string const g_strBasePath = "base-path"; -static string const g_strAsm = "asm"; -static string const g_strAsmJson = "asm-json"; +static string const g_strIncludePath = "include-path"; static string const g_strAssemble = "assemble"; -static string const g_strAst = "ast"; -static string const g_strAstCompactJson = "ast-compact-json"; -static string const g_strBinary = "bin"; -static string const g_strBinaryRuntime = "bin-runtime"; static string const g_strCombinedJson = "combined-json"; -static string const g_strCompactJSON = "compact-format"; static string const g_strErrorRecovery = "error-recovery"; static string const g_strEVM = "evm"; 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"; static string const g_strInputFile = "input-file"; -static string const g_strInterface = "interface"; static string const g_strYul = "yul"; static string const g_strYulDialect = "yul-dialect"; -static string const g_strIR = "ir"; -static string const g_strIROptimized = "ir-optimized"; static string const g_strIPFS = "ipfs"; static string const g_strLicense = "license"; static string const g_strLibraries = "libraries"; static string const g_strLink = "link"; 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_strModelCheckerContracts = "model-checker-contracts"; @@ -94,11 +81,8 @@ static string const g_strModelCheckerShowUnproved = "model-checker-show-unproved static string const g_strModelCheckerSolvers = "model-checker-solvers"; static string const g_strModelCheckerTargets = "model-checker-targets"; 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"; static string const g_strNoOptimizeYul = "no-optimize-yul"; -static string const g_strOpcodes = "opcodes"; static string const g_strOptimize = "optimize"; static string const g_strOptimizeRuns = "optimize-runs"; static string const g_strOptimizeYul = "optimize-yul"; @@ -106,7 +90,6 @@ static string const g_strYulOptimizations = "yul-optimizations"; 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"; @@ -119,13 +102,8 @@ static set const g_revertStringsArgs revertStringsToString(RevertStrings::VerboseDebug) }; -static string const g_strSignatureHashes = "hashes"; static string const g_strSources = "sources"; static string const g_strSourceList = "sourceList"; -static string const g_strSrcMap = "srcmap"; -static string const g_strSrcMapRuntime = "srcmap-runtime"; -static string const g_strFunDebug = "function-debug"; -static string const g_strFunDebugRuntime = "function-debug-runtime"; static string const g_strStandardJSON = "standard-json"; static string const g_strStrictAssembly = "strict-assembly"; static string const g_strSwarm = "swarm"; @@ -137,30 +115,6 @@ static string const g_strColor = "color"; static string const g_strNoColor = "no-color"; static string const g_strErrorIds = "error-codes"; -/// Possible arguments to for --combined-json -static set const g_combinedJsonArgs -{ - g_strAbi, - g_strAsm, - g_strAst, - g_strBinary, - g_strBinaryRuntime, - g_strCompactJSON, - g_strFunDebug, - g_strFunDebugRuntime, - g_strGeneratedSources, - g_strGeneratedSourcesRuntime, - g_strInterface, - g_strMetadata, - g_strNatspecUser, - g_strNatspecDev, - g_strOpcodes, - g_strSignatureHashes, - g_strSrcMap, - g_strSrcMapRuntime, - g_strStorageLayout -}; - /// Possible arguments to for --machine static set const g_machineArgs { @@ -216,54 +170,62 @@ bool CommandLineParser::checkMutuallyExclusive(vector const& _optionName bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept { - static_assert( - sizeof(*this) == 15 * sizeof(bool), - "Remember to update code below if you add/remove fields." - ); + for (bool CompilerOutputs::* member: componentMap() | ranges::views::values) + if (this->*member != _other.*member) + return false; + return true; +} - return - astCompactJson == _other.astCompactJson && - asm_ == _other.asm_ && - asmJson == _other.asmJson && - opcodes == _other.opcodes && - binary == _other.binary && - binaryRuntime == _other.binaryRuntime && - abi == _other.abi && - ir == _other.ir && - irOptimized == _other.irOptimized && - ewasm == _other.ewasm && - signatureHashes == _other.signatureHashes && - natspecUser == _other.natspecUser && - natspecDev == _other.natspecDev && - metadata == _other.metadata && - storageLayout == _other.storageLayout; +ostream& operator<<(ostream& _out, CompilerOutputs const& _selection) +{ + vector serializedSelection; + for (auto&& [componentName, component]: CompilerOutputs::componentMap()) + if (_selection.*component) + serializedSelection.push_back(CompilerOutputs::componentName(component)); + + return _out << joinHumanReadable(serializedSelection, ","); +} + +string const& CompilerOutputs::componentName(bool CompilerOutputs::* _component) +{ + solAssert(_component, ""); + + // NOTE: Linear search is not optimal but it's simpler than getting pointers-to-members to work as map keys. + for (auto const& [componentName, component]: CompilerOutputs::componentMap()) + if (component == _component) + return componentName; + + solAssert(false, ""); } bool CombinedJsonRequests::operator==(CombinedJsonRequests const& _other) const noexcept { - static_assert( - sizeof(*this) == 17 * sizeof(bool), - "Remember to update code below if you add/remove fields." - ); + for (bool CombinedJsonRequests::* member: componentMap() | ranges::views::values) + if (this->*member != _other.*member) + return false; + return true; +} - return - abi == _other.abi && - metadata == _other.metadata && - binary == _other.binary && - binaryRuntime == _other.binaryRuntime && - opcodes == _other.opcodes && - asm_ == _other.asm_ && - storageLayout == _other.storageLayout && - generatedSources == _other.generatedSources && - generatedSourcesRuntime == _other.generatedSourcesRuntime && - srcMap == _other.srcMap && - srcMapRuntime == _other.srcMapRuntime && - funDebug == _other.funDebug && - funDebugRuntime == _other.funDebugRuntime && - signatureHashes == _other.signatureHashes && - natspecDev == _other.natspecDev && - natspecUser == _other.natspecUser && - ast == _other.ast; + +ostream& operator<<(ostream& _out, CombinedJsonRequests const& _requests) +{ + vector serializedRequests; + for (auto&& [componentName, component]: CombinedJsonRequests::componentMap()) + if (_requests.*component) + serializedRequests.push_back(CombinedJsonRequests::componentName(component)); + + return _out << joinHumanReadable(serializedRequests, ","); +} + +string const& CombinedJsonRequests::componentName(bool CombinedJsonRequests::* _component) +{ + solAssert(_component, ""); + + for (auto const& [componentName, component]: CombinedJsonRequests::componentMap()) + if (component == _component) + return componentName; + + solAssert(false, ""); } bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noexcept @@ -273,6 +235,7 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex input.remappings == _other.input.remappings && input.addStdin == _other.input.addStdin && input.basePath == _other.input.basePath && + input.includePaths == _other.input.includePaths && input.allowedDirectories == _other.input.allowedDirectories && input.ignoreMissingFiles == _other.input.ignoreMissingFiles && input.errorRecovery == _other.input.errorRecovery && @@ -302,16 +265,53 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex modelChecker.settings == _other.modelChecker.settings; } +OptimiserSettings CommandLineOptions::optimiserSettings() const +{ + OptimiserSettings settings; + + if (optimizer.enabled) + settings = OptimiserSettings::standard(); + else + settings = OptimiserSettings::minimal(); + + if (optimizer.noOptimizeYul) + settings.runYulOptimiser = false; + + if (optimizer.expectedExecutionsPerDeployment.has_value()) + settings.expectedExecutionsPerDeployment = optimizer.expectedExecutionsPerDeployment.value(); + + if (optimizer.yulSteps.has_value()) + settings.yulOptimiserSteps = optimizer.yulSteps.value(); + + return settings; +} + +bool CommandLineParser::parse(int _argc, char const* const* _argv, bool _interactiveTerminal) +{ + m_hasOutput = false; + + if (!parseArgs(_argc, _argv, _interactiveTerminal)) + return false; + + return processArgs(); +} + bool CommandLineParser::parseInputPathsAndRemappings() { m_options.input.ignoreMissingFiles = (m_args.count(g_strIgnoreMissingFiles) > 0); if (m_args.count(g_strInputFile)) - for (string path: m_args[g_strInputFile].as>()) + for (string const& positionalArg: m_args[g_strInputFile].as>()) { - auto eq = find(path.begin(), path.end(), '='); - if (eq != path.end()) + if (ImportRemapper::isRemapping(positionalArg)) { + optional remapping = ImportRemapper::parseRemapping(positionalArg); + if (!remapping.has_value()) + { + serr() << "Invalid remapping: \"" << positionalArg << "\"." << endl; + return false; + } + if (m_options.input.mode == InputMode::StandardJson) { serr() << "Import remappings are not accepted on the command line in Standard JSON mode." << endl; @@ -319,21 +319,24 @@ bool CommandLineParser::parseInputPathsAndRemappings() return false; } - if (auto r = ImportRemapper::parseRemapping(path)) - m_options.input.remappings.emplace_back(std::move(*r)); - else + if (!remapping->target.empty()) { - serr() << "Invalid remapping: \"" << path << "\"." << endl; - return false; + // If the target is a directory, whitelist it. Otherwise whitelist containing dir. + // NOTE: /a/b/c/ is a directory while /a/b/c is not. + boost::filesystem::path remappingDir = remapping->target; + if (remappingDir.filename() != "..") + // As an exception we'll treat /a/b/c/.. as a directory too. It would be + // unintuitive to whitelist /a/b/c when the target is equivalent to /a/b/. + remappingDir.remove_filename(); + m_options.input.allowedDirectories.insert(remappingDir.empty() ? "." : remappingDir); } - string remappingTarget(eq + 1, path.end()); - m_options.input.allowedDirectories.insert(boost::filesystem::path(remappingTarget).remove_filename()); + m_options.input.remappings.emplace_back(move(remapping.value())); } - else if (path == "-") + else if (positionalArg == "-") m_options.input.addStdin = true; else - m_options.input.paths.insert(path); + m_options.input.paths.insert(positionalArg); } if (m_options.input.mode == InputMode::StandardJson) @@ -457,10 +460,8 @@ bool CommandLineParser::parseLibraryOption(string const& _input) return true; } -bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal) +po::options_description CommandLineParser::optionsDescription() { - m_hasOutput = false; - // Declare the supported options. po::options_description desc((R"(solc, the Solidity commandline compiler. @@ -475,7 +476,7 @@ at standard output or in files in the output directory, if specified. Imports are automatically read from the filesystem, but it is also possible to remap paths using the context:prefix=path syntax. Example: -solc --)" + g_strBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol +solc --)" + CompilerOutputs::componentName(&CompilerOutputs::binary) + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol General Information)").c_str(), po::options_description::m_default_line_length, @@ -494,6 +495,15 @@ General Information)").c_str(), po::value()->value_name("path"), "Use the given path as the root of the source tree instead of the root of the filesystem." ) + ( + g_strIncludePath.c_str(), + po::value>()->value_name("path"), + "Make an additional source directory available to the default import callback. " + "Use this option if you want to import contracts whose location is not fixed in relation " + "to your main source tree, e.g. third-party libraries installed using a package manager. " + "Can be used multiple times. " + "Can only be used if base path has a non-empty value." + ) ( g_strAllowPaths.c_str(), po::value()->value_name("path(s)"), @@ -533,7 +543,7 @@ General Information)").c_str(), ) ( g_strRevertStrings.c_str(), - po::value()->value_name(boost::join(g_revertStringsArgs, ",")), + po::value()->value_name(joinHumanReadable(g_revertStringsArgs, ",")), "Strip revert (and require) reason strings or add additional debugging information." ) ( @@ -578,7 +588,7 @@ General Information)").c_str(), g_strImportAst.c_str(), ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " "Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by " - "--" + g_strCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str() + "--" + g_strCombinedJson + " " + CombinedJsonRequests::componentName(&CombinedJsonRequests::ast)).c_str() ) ; desc.add(alternativeInputModes); @@ -587,12 +597,12 @@ General Information)").c_str(), assemblyModeOptions.add_options() ( g_strMachine.c_str(), - po::value()->value_name(boost::join(g_machineArgs, ",")), + po::value()->value_name(joinHumanReadable(g_machineArgs, ",")), "Target machine in assembly or Yul mode." ) ( g_strYulDialect.c_str(), - po::value()->value_name(boost::join(g_yulDialectArgs, ",")), + po::value()->value_name(joinHumanReadable(g_yulDialectArgs, ",")), "Input dialect to use in assembly or yul mode." ) ; @@ -638,21 +648,21 @@ General Information)").c_str(), po::options_description outputComponents("Output Components"); outputComponents.add_options() - (g_strAstCompactJson.c_str(), "AST of all source files in a compact JSON format.") - (g_strAsm.c_str(), "EVM assembly of the contracts.") - (g_strAsmJson.c_str(), "EVM assembly of the contracts in JSON format.") - (g_strOpcodes.c_str(), "Opcodes of the contracts.") - (g_strBinary.c_str(), "Binary of the contracts in hex.") - (g_strBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") - (g_strAbi.c_str(), "ABI specification of the contracts.") - (g_strIR.c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_strIROptimized.c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") - (g_strEwasm.c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") - (g_strSignatureHashes.c_str(), "Function signature hashes of the contracts.") - (g_strNatspecUser.c_str(), "Natspec user documentation of all contracts.") - (g_strNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_strMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") - (g_strStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables.") + (CompilerOutputs::componentName(&CompilerOutputs::astCompactJson).c_str(), "AST of all source files in a compact JSON format.") + (CompilerOutputs::componentName(&CompilerOutputs::asm_).c_str(), "EVM assembly of the contracts.") + (CompilerOutputs::componentName(&CompilerOutputs::asmJson).c_str(), "EVM assembly of the contracts in JSON format.") + (CompilerOutputs::componentName(&CompilerOutputs::opcodes).c_str(), "Opcodes of the contracts.") + (CompilerOutputs::componentName(&CompilerOutputs::binary).c_str(), "Binary of the contracts in hex.") + (CompilerOutputs::componentName(&CompilerOutputs::binaryRuntime).c_str(), "Binary of the runtime part of the contracts in hex.") + (CompilerOutputs::componentName(&CompilerOutputs::abi).c_str(), "ABI specification of the contracts.") + (CompilerOutputs::componentName(&CompilerOutputs::ir).c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (CompilerOutputs::componentName(&CompilerOutputs::irOptimized).c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).") + (CompilerOutputs::componentName(&CompilerOutputs::ewasm).c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).") + (CompilerOutputs::componentName(&CompilerOutputs::signatureHashes).c_str(), "Function signature hashes of the contracts.") + (CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all contracts.") + (CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.") + (CompilerOutputs::componentName(&CompilerOutputs::metadata).c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") + (CompilerOutputs::componentName(&CompilerOutputs::storageLayout).c_str(), "Slots, offsets and types of the contract's state variables.") ; desc.add(outputComponents); @@ -664,7 +674,7 @@ General Information)").c_str(), ) ( g_strCombinedJson.c_str(), - po::value()->value_name(boost::join(g_combinedJsonArgs, ",")), + po::value()->value_name(joinHumanReadable(CombinedJsonRequests::componentMap() | ranges::views::keys, ",")), "Output a single json document containing the specified information." ) ; @@ -674,7 +684,7 @@ General Information)").c_str(), metadataOptions.add_options() ( g_strMetadataHash.c_str(), - po::value()->value_name(boost::join(g_metadataHashArgs, ",")), + po::value()->value_name(joinHumanReadable(g_metadataHashArgs, ",")), "Choose hash method for the bytecode metadata or disable it." ) ( @@ -695,7 +705,7 @@ General Information)").c_str(), // TODO: The type in OptimiserSettings is size_t but we only accept values up to 2**32-1 // on the CLI and in Standard JSON. We should just switch to uint32_t everywhere. po::value()->value_name("n")->default_value(static_cast(OptimiserSettings{}.expectedExecutionsPerDeployment)), - "Set for how many contract runs to optimize. " + "The number of runs specifies roughly how often each opcode of the deployed code will be executed across the lifetime of the contract. " "Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage." ) ( @@ -759,12 +769,22 @@ General Information)").c_str(), ; desc.add(smtCheckerOptions); - po::options_description allOptions = desc; - allOptions.add_options()(g_strInputFile.c_str(), po::value>(), "input file"); + desc.add_options()(g_strInputFile.c_str(), po::value>(), "input file"); + return desc; +} +po::positional_options_description CommandLineParser::positionalOptionsDescription() +{ // All positional options should be interpreted as input files po::positional_options_description filesPositions; filesPositions.add(g_strInputFile.c_str(), -1); + return filesPositions; +} + +bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal) +{ + po::options_description allOptions = optionsDescription(); + po::positional_options_description filesPositions = positionalOptionsDescription(); // parse the compiler arguments try @@ -780,34 +800,9 @@ General Information)").c_str(), return false; } - if (!checkMutuallyExclusive({g_strColor, g_strNoColor})) - return false; - - array const conflictingWithStopAfter{ - g_strBinary, - g_strIR, - g_strIROptimized, - g_strEwasm, - g_strGas, - g_strAsm, - g_strAsmJson, - g_strOpcodes - }; - - for (auto& option: conflictingWithStopAfter) - if (!checkMutuallyExclusive({g_strStopAfter, option})) - return false; - - if (m_args.count(g_strColor) > 0) - m_options.formatting.coloredOutput = true; - else if (m_args.count(g_strNoColor) > 0) - m_options.formatting.coloredOutput = false; - - m_options.formatting.withErrorIds = m_args.count(g_strErrorIds); - - if (m_args.count(g_strHelp) || (interactiveTerminal && _argc == 1)) + if (m_args.count(g_strHelp) || (_interactiveTerminal && _argc == 1)) { - sout() << desc; + sout() << allOptions; return false; } @@ -817,6 +812,89 @@ General Information)").c_str(), if (m_args.count(g_strLicense)) printLicenseAndExit(); + po::notify(m_args); + + return true; +} + +bool CommandLineParser::processArgs() +{ + if (!checkMutuallyExclusive({ + g_strStandardJSON, + g_strLink, + g_strAssemble, + g_strStrictAssembly, + g_strYul, + g_strImportAst, + })) + return false; + + if (m_args.count(g_strStandardJSON) > 0) + m_options.input.mode = InputMode::StandardJson; + else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0) + m_options.input.mode = InputMode::Assembler; + else if (m_args.count(g_strLink) > 0) + m_options.input.mode = InputMode::Linker; + else if (m_args.count(g_strImportAst) > 0) + m_options.input.mode = InputMode::CompilerWithASTImport; + else + m_options.input.mode = InputMode::Compiler; + + if ( + m_args.count(g_strExperimentalViaIR) > 0 && + m_options.input.mode != InputMode::Compiler && + m_options.input.mode != InputMode::CompilerWithASTImport + ) + { + serr() << "The option --" << g_strExperimentalViaIR << " is only supported in the compiler mode." << endl; + return false; + } + + if (!checkMutuallyExclusive({g_strColor, g_strNoColor})) + return false; + + array const conflictingWithStopAfter{ + CompilerOutputs::componentName(&CompilerOutputs::binary), + CompilerOutputs::componentName(&CompilerOutputs::ir), + CompilerOutputs::componentName(&CompilerOutputs::irOptimized), + CompilerOutputs::componentName(&CompilerOutputs::ewasm), + g_strGas, + CompilerOutputs::componentName(&CompilerOutputs::asm_), + CompilerOutputs::componentName(&CompilerOutputs::asmJson), + CompilerOutputs::componentName(&CompilerOutputs::opcodes), + }; + + for (auto& option: conflictingWithStopAfter) + if (!checkMutuallyExclusive({g_strStopAfter, option})) + return false; + + if ( + m_options.input.mode != InputMode::Compiler && + m_options.input.mode != InputMode::CompilerWithASTImport && + m_options.input.mode != InputMode::Assembler + ) + { + if (!m_args[g_strOptimizeRuns].defaulted()) + { + serr() << "Option --" << g_strOptimizeRuns << " is only valid in compiler and assembler modes." << endl; + return false; + } + + for (string const& option: {g_strOptimize, g_strNoOptimizeYul, g_strOptimizeYul, g_strYulOptimizations}) + if (m_args.count(option) > 0) + { + serr() << "Option --" << option << " is only valid in compiler and assembler modes." << endl; + return false; + } + } + + if (m_args.count(g_strColor) > 0) + m_options.formatting.coloredOutput = true; + else if (m_args.count(g_strNoColor) > 0) + m_options.formatting.coloredOutput = false; + + m_options.formatting.withErrorIds = m_args.count(g_strErrorIds); + if (m_args.count(g_strRevertStrings)) { string revertStringsString = m_args[g_strRevertStrings].as(); @@ -852,47 +930,39 @@ General Information)").c_str(), m_options.formatting.json.indent = m_args[g_strJsonIndent].as(); } - static_assert( - sizeof(m_options.compiler.outputs) == 15 * sizeof(bool), - "Remember to update code below if you add/remove fields." - ); - m_options.compiler.outputs.astCompactJson = (m_args.count(g_strAstCompactJson) > 0); - m_options.compiler.outputs.asm_ = (m_args.count(g_strAsm) > 0); - m_options.compiler.outputs.asmJson = (m_args.count(g_strAsmJson) > 0); - m_options.compiler.outputs.opcodes = (m_args.count(g_strOpcodes) > 0); - m_options.compiler.outputs.binary = (m_args.count(g_strBinary) > 0); - m_options.compiler.outputs.binaryRuntime = (m_args.count(g_strBinaryRuntime) > 0); - m_options.compiler.outputs.abi = (m_args.count(g_strAbi) > 0); - m_options.compiler.outputs.ir = (m_args.count(g_strIR) > 0); - m_options.compiler.outputs.irOptimized = (m_args.count(g_strIROptimized) > 0); - m_options.compiler.outputs.ewasm = (m_args.count(g_strEwasm) > 0); - m_options.compiler.outputs.signatureHashes = (m_args.count(g_strSignatureHashes) > 0); - m_options.compiler.outputs.natspecUser = (m_args.count(g_strNatspecUser) > 0); - m_options.compiler.outputs.natspecDev = (m_args.count(g_strNatspecDev) > 0); - m_options.compiler.outputs.metadata = (m_args.count(g_strMetadata) > 0); - m_options.compiler.outputs.storageLayout = (m_args.count(g_strStorageLayout) > 0); + for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap()) + m_options.compiler.outputs.*outputComponent = (m_args.count(optionName) > 0); m_options.compiler.estimateGas = (m_args.count(g_strGas) > 0); - po::notify(m_args); - if (m_args.count(g_strBasePath)) m_options.input.basePath = m_args[g_strBasePath].as(); + if (m_args.count(g_strIncludePath) > 0) + { + if (m_options.input.basePath.empty()) + { + serr() << "--" << g_strIncludePath << " option requires a non-empty base path." << endl; + return false; + } + + for (string const& includePath: m_args[g_strIncludePath].as>()) + { + if (includePath.empty()) + { + serr() << "Empty values are not allowed in --" << g_strIncludePath << "." << endl; + return false; + } + m_options.input.includePaths.push_back(includePath); + } + } + if (m_args.count(g_strAllowPaths)) { vector paths; - for (string const& path: boost::split(paths, m_args[g_strAllowPaths].as(), boost::is_any_of(","))) - { - auto filesystem_path = boost::filesystem::path(path); - // If the given path had a trailing slash, the Boost filesystem - // path will have it's last component set to '.'. This breaks - // path comparison in later parts of the code, so we need to strip - // it. - if (filesystem_path.filename() == ".") - filesystem_path.remove_filename(); - m_options.input.allowedDirectories.insert(filesystem_path); - } + for (string const& allowedPath: boost::split(paths, m_args[g_strAllowPaths].as(), boost::is_any_of(","))) + if (!allowedPath.empty()) + m_options.input.allowedDirectories.insert(allowedPath); } if (m_args.count(g_strStopAfter)) @@ -906,37 +976,6 @@ General Information)").c_str(), m_options.output.stopAfter = CompilerStack::State::Parsed; } - if (!checkMutuallyExclusive({ - g_strStandardJSON, - g_strLink, - g_strAssemble, - g_strStrictAssembly, - g_strYul, - g_strImportAst, - })) - return false; - - if (m_args.count(g_strStandardJSON) > 0) - m_options.input.mode = InputMode::StandardJson; - else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0) - m_options.input.mode = InputMode::Assembler; - else if (m_args.count(g_strLink) > 0) - m_options.input.mode = InputMode::Linker; - else if (m_args.count(g_strImportAst) > 0) - m_options.input.mode = InputMode::CompilerWithASTImport; - else - m_options.input.mode = InputMode::Compiler; - - if ( - m_args.count(g_strExperimentalViaIR) > 0 && - m_options.input.mode != InputMode::Compiler && - m_options.input.mode != InputMode::CompilerWithASTImport - ) - { - serr() << "The option --" << g_strExperimentalViaIR << " is only supported in the compiler mode." << endl; - return false; - } - if (!parseInputPathsAndRemappings()) return false; @@ -948,6 +987,9 @@ General Information)").c_str(), if (!parseLibraryOption(library)) return false; + if (m_options.input.mode == InputMode::Linker) + return true; + if (m_args.count(g_strEVMVersion)) { string versionOptionStr = m_args[g_strEVMVersion].as(); @@ -960,6 +1002,33 @@ General Information)").c_str(), m_options.output.evmVersion = *versionOption; } + m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); + m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); + if (!m_args[g_strOptimizeRuns].defaulted()) + m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); + + if (m_args.count(g_strYulOptimizations)) + { + OptimiserSettings optimiserSettings = m_options.optimiserSettings(); + if (!optimiserSettings.runYulOptimiser) + { + serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; + return false; + } + + try + { + yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); + } + catch (yul::OptimizerException const& _exception) + { + serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; + return false; + } + + m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); + } + if (m_options.input.mode == InputMode::Assembler) { vector const nonAssemblyModeOptions = { @@ -987,32 +1056,6 @@ General Information)").c_str(), using Input = yul::AssemblyStack::Language; using Machine = yul::AssemblyStack::Machine; m_options.assembly.inputLanguage = m_args.count(g_strYul) ? Input::Yul : (m_args.count(g_strStrictAssembly) ? Input::StrictAssembly : Input::Assembly); - m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); - m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); - - if (!m_args[g_strOptimizeRuns].defaulted()) - m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); - - if (m_args.count(g_strYulOptimizations)) - { - if (!m_options.optimizer.enabled) - { - serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; - return false; - } - - try - { - yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); - } - catch (yul::OptimizerException const& _exception) - { - serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; - return false; - } - - m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); - } if (m_args.count(g_strMachine)) { @@ -1078,9 +1121,6 @@ General Information)").c_str(), return false; } - if (m_options.input.mode == InputMode::Linker) - return true; - if (m_args.count(g_strMetadataHash)) { string hashStr = m_args[g_strMetadataHash].as(); @@ -1164,40 +1204,10 @@ General Information)").c_str(), m_args.count(g_strModelCheckerTargets) || m_args.count(g_strModelCheckerTimeout); m_options.output.experimentalViaIR = (m_args.count(g_strExperimentalViaIR) > 0); - if (!m_args[g_strOptimizeRuns].defaulted()) - m_options.optimizer.expectedExecutionsPerDeployment = m_args.at(g_strOptimizeRuns).as(); - - m_options.optimizer.enabled = (m_args.count(g_strOptimize) > 0); - m_options.optimizer.noOptimizeYul = (m_args.count(g_strNoOptimizeYul) > 0); - - OptimiserSettings settings = m_options.optimizer.enabled ? OptimiserSettings::standard() : OptimiserSettings::minimal(); - if (m_options.optimizer.noOptimizeYul) - settings.runYulOptimiser = false; - if (m_args.count(g_strYulOptimizations)) - { - if (!settings.runYulOptimiser) - { - serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl; - return false; - } - - try - { - yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as()); - } - catch (yul::OptimizerException const& _exception) - { - serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl; - return false; - } - - m_options.optimizer.yulSteps = m_args[g_strYulOptimizations].as(); - } - if (m_options.input.mode == InputMode::Compiler) m_options.input.errorRecovery = (m_args.count(g_strErrorRecovery) > 0); - solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, ""); + solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport); return true; } @@ -1208,30 +1218,15 @@ bool CommandLineParser::parseCombinedJsonOption() set requests; for (string const& item: boost::split(requests, m_args[g_strCombinedJson].as(), boost::is_any_of(","))) - if (!g_combinedJsonArgs.count(item)) + if (CombinedJsonRequests::componentMap().count(item) == 0) { serr() << "Invalid option to --" << g_strCombinedJson << ": " << item << endl; return false; } m_options.compiler.combinedJsonRequests = CombinedJsonRequests{}; - m_options.compiler.combinedJsonRequests->abi = (requests.count(g_strAbi) > 0); - m_options.compiler.combinedJsonRequests->metadata = (requests.count("metadata") > 0); - m_options.compiler.combinedJsonRequests->binary = (requests.count(g_strBinary) > 0); - m_options.compiler.combinedJsonRequests->binaryRuntime = (requests.count(g_strBinaryRuntime) > 0); - m_options.compiler.combinedJsonRequests->opcodes = (requests.count(g_strOpcodes) > 0); - m_options.compiler.combinedJsonRequests->asm_ = (requests.count(g_strAsm) > 0); - m_options.compiler.combinedJsonRequests->storageLayout = (requests.count(g_strStorageLayout) > 0); - m_options.compiler.combinedJsonRequests->generatedSources = (requests.count(g_strGeneratedSources) > 0); - m_options.compiler.combinedJsonRequests->generatedSourcesRuntime = (requests.count(g_strGeneratedSourcesRuntime) > 0); - m_options.compiler.combinedJsonRequests->srcMap = (requests.count(g_strSrcMap) > 0); - m_options.compiler.combinedJsonRequests->srcMapRuntime = (requests.count(g_strSrcMapRuntime) > 0); - m_options.compiler.combinedJsonRequests->funDebug = (requests.count(g_strFunDebug) > 0); - m_options.compiler.combinedJsonRequests->funDebugRuntime = (requests.count(g_strFunDebugRuntime) > 0); - m_options.compiler.combinedJsonRequests->signatureHashes = (requests.count(g_strSignatureHashes) > 0); - m_options.compiler.combinedJsonRequests->natspecDev = (requests.count(g_strNatspecDev) > 0); - m_options.compiler.combinedJsonRequests->natspecUser = (requests.count(g_strNatspecUser) > 0); - m_options.compiler.combinedJsonRequests->ast = (requests.count(g_strAst) > 0); + for (auto&& [componentName, component]: CombinedJsonRequests::componentMap()) + m_options.compiler.combinedJsonRequests.value().*component = (requests.count(componentName) > 0); return true; } diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 423dacb5f..7e1266653 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -55,6 +55,30 @@ struct CompilerOutputs { bool operator!=(CompilerOutputs const& _other) const noexcept { return !(*this == _other); } bool operator==(CompilerOutputs const& _other) const noexcept; + friend std::ostream& operator<<(std::ostream& _out, CompilerOutputs const& _requests); + + static std::string const& componentName(bool CompilerOutputs::* _component); + static auto const& componentMap() + { + static std::map const components = { + {"ast-compact-json", &CompilerOutputs::astCompactJson}, + {"asm", &CompilerOutputs::asm_}, + {"asm-json", &CompilerOutputs::asmJson}, + {"opcodes", &CompilerOutputs::opcodes}, + {"bin", &CompilerOutputs::binary}, + {"bin-runtime", &CompilerOutputs::binaryRuntime}, + {"abi", &CompilerOutputs::abi}, + {"ir", &CompilerOutputs::ir}, + {"ir-optimized", &CompilerOutputs::irOptimized}, + {"ewasm", &CompilerOutputs::ewasm}, + {"hashes", &CompilerOutputs::signatureHashes}, + {"userdoc", &CompilerOutputs::natspecUser}, + {"devdoc", &CompilerOutputs::natspecDev}, + {"metadata", &CompilerOutputs::metadata}, + {"storage-layout", &CompilerOutputs::storageLayout}, + }; + return components; + } bool astCompactJson = false; bool asm_ = false; @@ -77,6 +101,32 @@ struct CombinedJsonRequests { bool operator!=(CombinedJsonRequests const& _other) const noexcept { return !(*this == _other); } bool operator==(CombinedJsonRequests const& _other) const noexcept; + friend std::ostream& operator<<(std::ostream& _out, CombinedJsonRequests const& _requests); + + static std::string const& componentName(bool CombinedJsonRequests::* _component); + static auto const& componentMap() + { + static std::map const components = { + {"abi", &CombinedJsonRequests::abi}, + {"metadata", &CombinedJsonRequests::metadata}, + {"bin", &CombinedJsonRequests::binary}, + {"bin-runtime", &CombinedJsonRequests::binaryRuntime}, + {"opcodes", &CombinedJsonRequests::opcodes}, + {"asm", &CombinedJsonRequests::asm_}, + {"storage-layout", &CombinedJsonRequests::storageLayout}, + {"generated-sources", &CombinedJsonRequests::generatedSources}, + {"generated-sources-runtime", &CombinedJsonRequests::generatedSourcesRuntime}, + {"srcmap", &CombinedJsonRequests::srcMap}, + {"srcmap-runtime", &CombinedJsonRequests::srcMapRuntime}, + {"function-debug", &CombinedJsonRequests::funDebug}, + {"function-debug-runtime", &CombinedJsonRequests::funDebugRuntime}, + {"hashes", &CombinedJsonRequests::signatureHashes}, + {"devdoc", &CombinedJsonRequests::natspecDev}, + {"userdoc", &CombinedJsonRequests::natspecUser}, + {"ast", &CombinedJsonRequests::ast}, + }; + return components; + } bool abi = false; bool metadata = false; @@ -102,6 +152,7 @@ struct CommandLineOptions bool operator==(CommandLineOptions const& _other) const noexcept; bool operator!=(CommandLineOptions const& _other) const noexcept { return !(*this == _other); } + OptimiserSettings optimiserSettings() const; struct { @@ -110,6 +161,7 @@ struct CommandLineOptions std::vector remappings; bool addStdin = false; boost::filesystem::path basePath = ""; + std::vector includePaths; FileReader::FileSystemPathSet allowedDirectories; bool ignoreMissingFiles = false; bool errorRecovery = false; @@ -169,7 +221,6 @@ struct CommandLineOptions bool initialize = false; ModelCheckerSettings settings; } modelChecker; - }; /// Parses the command-line arguments and produces a filled-out CommandLineOptions structure. @@ -195,7 +246,7 @@ public: /// CommandLineOptions structure has been fully initialized. false if there were errors - in /// this case CommandLineOptions may be only partially filled out. May also return false if /// there is not further processing necessary and the program should just exit. - bool parse(int _argc, char const* const* _argv, bool interactiveTerminal); + bool parse(int _argc, char const* const* _argv, bool _interactiveTerminal); CommandLineOptions const& options() const { return m_options; } @@ -203,6 +254,24 @@ public: bool hasOutput() const { return m_hasOutput; } private: + /// @returns a specification of all named command-line options accepted by the compiler. + /// The object can be used to parse command-line arguments or to generate the help screen. + static boost::program_options::options_description optionsDescription(); + + /// @returns a specification of all positional command-line arguments accepted by the compiler. + /// The object can be used to parse command-line arguments or to generate the help screen. + static boost::program_options::positional_options_description positionalOptionsDescription(); + + /// Uses boost::program_options to parse the command-line arguments and leaves the result in @a m_args. + /// Also handles the arguments that result in information being printed followed by immediate exit. + /// @returns false if parsing fails due to syntactical errors or the arguments not matching the description. + bool parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal); + + /// Validates parsed arguments stored in @a m_args and fills out the internal CommandLineOptions + /// structure. + /// @return false if there are any validation errors, true otherwise. + bool processArgs(); + /// Parses the value supplied to --combined-json. /// @return false if there are any validation errors, true otherwise. bool parseCombinedJsonOption(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fa55d76a2..3135b53d5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -143,6 +143,8 @@ set(libyul_sources libyul/ObjectCompilerTest.h libyul/ObjectParser.cpp libyul/Parser.cpp + libyul/StackLayoutGeneratorTest.cpp + libyul/StackLayoutGeneratorTest.h libyul/SyntaxTest.h libyul/SyntaxTest.cpp libyul/YulInterpreterTest.cpp @@ -158,6 +160,7 @@ set(solcli_sources solc/Common.cpp solc/Common.h solc/CommandLineInterface.cpp + solc/CommandLineInterfaceAllowPaths.cpp solc/CommandLineParser.cpp ) detect_stray_source_files("${solcli_sources}" "solc/") diff --git a/test/Common.cpp b/test/Common.cpp index 9b2cfbb96..2a663aa1b 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -91,22 +91,27 @@ CommonOptions::CommonOptions(std::string _caption): po::options_description::m_default_line_length, po::options_description::m_default_line_length - 23 ) +{ + +} + +void CommonOptions::addOptions() { 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") ("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-semantic-tests", po::bool_switch(&disableSemanticTests), "disable semantic tests") - ("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.") - ("enforce-compile-to-ewasm", po::bool_switch(&enforceCompileToEwasm), "Enforce compiling all tests to Ewasm to see if additional tests can be activated.") - ("enforce-gas-cost", po::bool_switch(&enforceGasTest), "Enforce checking gas cost in semantic tests.") - ("enforce-gas-cost-min-value", po::value(&enforceGasTestMinValue), "Threshold value to enforce adding gas checks to a test.") - ("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"); + ("ewasm", po::bool_switch(&ewasm)->default_value(ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.") + ("no-semantic-tests", po::bool_switch(&disableSemanticTests)->default_value(disableSemanticTests), "disable semantic tests") + ("no-smt", po::bool_switch(&disableSMT)->default_value(disableSMT), "disable SMT checker") + ("optimize", po::bool_switch(&optimize)->default_value(optimize), "enables optimization") + ("enforce-via-yul", po::value(&enforceViaYul)->default_value(enforceViaYul)->implicit_value(true), "Enforce compiling all tests via yul to see if additional tests can be activated.") + ("enforce-compile-to-ewasm", po::bool_switch(&enforceCompileToEwasm)->default_value(enforceCompileToEwasm), "Enforce compiling all tests to Ewasm to see if additional tests can be activated.") + ("enforce-gas-cost", po::value(&enforceGasTest)->default_value(enforceGasTest)->implicit_value(true), "Enforce checking gas cost in semantic tests.") + ("enforce-gas-cost-min-value", po::value(&enforceGasTestMinValue)->default_value(enforceGasTestMinValue), "Threshold value to enforce adding gas checks to a test.") + ("abiencoderv1", po::bool_switch(&useABIEncoderV1)->default_value(useABIEncoderV1), "enables abi encoder v1") + ("show-messages", po::bool_switch(&showMessages)->default_value(showMessages), "enables message output") + ("show-metadata", po::bool_switch(&showMetadata)->default_value(showMetadata), "enables metadata output"); } void CommonOptions::validate() const @@ -139,6 +144,7 @@ void CommonOptions::validate() const bool CommonOptions::parse(int argc, char const* const* argv) { po::variables_map arguments; + addOptions(); po::command_line_parser cmdLineParser(argc, argv); cmdLineParser.options(options); diff --git a/test/Common.h b/test/Common.h index c8a863d8c..f7c8fa733 100644 --- a/test/Common.h +++ b/test/Common.h @@ -70,6 +70,7 @@ struct CommonOptions langutil::EVMVersion evmVersion() const; + virtual void addOptions(); virtual bool parse(int argc, char const* const* argv); // Throws a ConfigException on error virtual void validate() const; diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 6d925f4d2..6c5cb6912 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -856,7 +856,7 @@ void EVMHostPrinter::selfdestructRecords() void EVMHostPrinter::callRecords() { - static const auto callKind = [](evmc_call_kind _kind) -> string + static auto constexpr callKind = [](evmc_call_kind _kind) -> string { switch (_kind) { diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index b65a94f52..c153cd471 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -40,6 +40,7 @@ #include #include +#include using namespace std; using namespace solidity; @@ -102,8 +103,8 @@ std::pair ExecutionFramework::compareAndCreateMessage( std::string message = "Invalid encoded data\n" " Result Expectation\n"; - auto resultHex = boost::replace_all_copy(toHex(_result), "0", "."); - auto expectedHex = boost::replace_all_copy(toHex(_expectation), "0", "."); + auto resultHex = boost::replace_all_copy(util::toHex(_result), "0", "."); + auto expectedHex = boost::replace_all_copy(util::toHex(_expectation), "0", "."); for (size_t i = 0; i < std::max(resultHex.size(), expectedHex.size()); i += 0x40) { std::string result{i >= resultHex.size() ? string{} : resultHex.substr(i, 0x40)}; @@ -163,7 +164,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 cout << "CALL " << m_sender.hex() << " -> " << m_contractAddress.hex() << ":" << endl; if (_value > 0) cout << " value: " << _value << endl; - cout << " in: " << toHex(_data) << endl; + cout << " in: " << util::toHex(_data) << endl; } evmc_message message = {}; message.input_data = _data.data(); @@ -194,7 +195,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 if (m_showMessages) { - cout << " out: " << toHex(m_output) << endl; + cout << " out: " << util::toHex(m_output) << endl; cout << " result: " << static_cast(result.status_code) << endl; cout << " gas used: " << m_gasUsed.str() << endl; } diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index d6912c5d0..0ee0214d4 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -49,9 +49,8 @@ namespace solidity::test using rational = boost::rational; // The ether and gwei denominations; here for ease of use where needed within code. -static const u256 gwei = u256(1) << 9; -static const u256 ether = u256(1) << 18; - +static u256 const gwei = u256(1) << 9; +static u256 const ether = u256(1) << 18; class ExecutionFramework { @@ -170,7 +169,7 @@ public: static bytes encode(size_t _value) { return encode(u256(_value)); } static bytes encode(char const* _value) { return encode(std::string(_value)); } static bytes encode(uint8_t _value) { return bytes(31, 0) + bytes{_value}; } - static bytes encode(u256 const& _value) { return util::toBigEndian(_value); } + static bytes encode(u256 const& _value) { return toBigEndian(_value); } /// @returns the fixed-point encoding of a rational number with a given /// number of fractional bits. static bytes encode(std::pair const& _valueAndPrecision) diff --git a/test/FilesystemUtils.cpp b/test/FilesystemUtils.cpp index c016f7fd1..0235b1147 100644 --- a/test/FilesystemUtils.cpp +++ b/test/FilesystemUtils.cpp @@ -31,7 +31,8 @@ void solidity::test::createFilesWithParentDirs(set cons if (!path.parent_path().empty()) boost::filesystem::create_directories(path.parent_path()); - ofstream newFile(path.string()); + // Use binary mode to avoid line ending conversion on Windows. + ofstream newFile(path.string(), std::ofstream::binary); newFile << _content; if (newFile.fail() || !boost::filesystem::exists(path)) @@ -53,13 +54,17 @@ void solidity::test::createFileWithContent(boost::filesystem::path const& _path, } bool solidity::test::createSymlinkIfSupportedByFilesystem( - boost::filesystem::path const& _targetPath, + boost::filesystem::path _targetPath, boost::filesystem::path const& _linkName, bool _directorySymlink ) { boost::system::error_code symlinkCreationError; + // NOTE: On Windows / works as a separator in a symlink target only if the target is absolute. + // Convert path separators to native ones to avoid this problem. + _targetPath.make_preferred(); + if (_directorySymlink) boost::filesystem::create_directory_symlink(_targetPath, _linkName, symlinkCreationError); else diff --git a/test/FilesystemUtils.h b/test/FilesystemUtils.h index f993ce03d..731260505 100644 --- a/test/FilesystemUtils.h +++ b/test/FilesystemUtils.h @@ -46,7 +46,7 @@ void createFileWithContent(boost::filesystem::path const& _path, std::string con /// support symlinks. /// Throws an exception of the operation fails for a different reason. bool createSymlinkIfSupportedByFilesystem( - boost::filesystem::path const& _targetPath, + boost::filesystem::path _targetPath, boost::filesystem::path const& _linkName, bool _directorySymlink ); diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 4ffdf5a74..e27c00b84 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ Testsuite const g_interactiveTestsuites[] = { {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, + {"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create}, {"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, diff --git a/test/Metadata.cpp b/test/Metadata.cpp index 61654ae00..687b9d73e 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include using namespace std; @@ -170,30 +169,35 @@ std::optional> parseCBORMetadata(bytes const& _metadata) } } -bool isValidMetadata(string const& _metadata) +bool isValidMetadata(string const& _serialisedMetadata) { Json::Value metadata; - if (!util::jsonParseStrict(_metadata, metadata)) + if (!util::jsonParseStrict(_serialisedMetadata, metadata)) return false; + return isValidMetadata(metadata); +} + +bool isValidMetadata(Json::Value const& _metadata) +{ if ( - !metadata.isObject() || - !metadata.isMember("version") || - !metadata.isMember("language") || - !metadata.isMember("compiler") || - !metadata.isMember("settings") || - !metadata.isMember("sources") || - !metadata.isMember("output") || - !metadata["settings"].isMember("evmVersion") || - !metadata["settings"].isMember("metadata") || - !metadata["settings"]["metadata"].isMember("bytecodeHash") + !_metadata.isObject() || + !_metadata.isMember("version") || + !_metadata.isMember("language") || + !_metadata.isMember("compiler") || + !_metadata.isMember("settings") || + !_metadata.isMember("sources") || + !_metadata.isMember("output") || + !_metadata["settings"].isMember("evmVersion") || + !_metadata["settings"].isMember("metadata") || + !_metadata["settings"]["metadata"].isMember("bytecodeHash") ) return false; - if (!metadata["version"].isNumeric() || metadata["version"] != 1) + if (!_metadata["version"].isNumeric() || _metadata["version"] != 1) return false; - if (!metadata["language"].isString() || metadata["language"].asString() != "Solidity") + if (!_metadata["language"].isString() || _metadata["language"].asString() != "Solidity") return false; /// @TODO add more strict checks diff --git a/test/Metadata.h b/test/Metadata.h index c91ceb559..86b43e245 100644 --- a/test/Metadata.h +++ b/test/Metadata.h @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -48,8 +49,10 @@ std::string bytecodeSansMetadata(std::string const& _bytecode); /// - everything else is invalid std::optional> parseCBORMetadata(bytes const& _metadata); -/// Expects a serialised metadata JSON and returns true if the -/// content is valid metadata. -bool isValidMetadata(std::string const& _metadata); +/// Expects a serialised metadata JSON and returns true if the content is valid metadata. +bool isValidMetadata(std::string const& _serialisedMetadata); + +/// Expects a deserialised metadata JSON and returns true if the content is valid metadata. +bool isValidMetadata(Json::Value const& _metadata); } // end namespaces diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index b5742e3bb..c413eef0f 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -26,7 +26,7 @@ # (c) 2016 solidity contributors. #------------------------------------------------------------------------------ -set -e +set -eo pipefail ## GLOBAL VARIABLES @@ -37,8 +37,44 @@ source "${REPO_ROOT}/scripts/common.sh" # shellcheck source=scripts/common_cmdline.sh source "${REPO_ROOT}/scripts/common_cmdline.sh" -AUTOUPDATE=false -[[ $1 == --update ]] && AUTOUPDATE=true && shift +pushd "${REPO_ROOT}/test/cmdlineTests" > /dev/null +autoupdate=false +no_smt=false +declare -a selected_tests +declare -a patterns_with_no_matches +while [[ $# -gt 0 ]] +do + case "$1" in + --update) + autoupdate=true + shift + ;; + --no-smt) + no_smt=true + shift + ;; + *) + matching_tests=$(find . -mindepth 1 -maxdepth 1 -type d -name "$1" | cut --characters 3- | sort) + + if [[ $matching_tests == "" ]] + then + patterns_with_no_matches+=("$1") + printWarning "No tests matching pattern '$1' found." + else + # shellcheck disable=SC2206 # We do not support test names containing spaces. + selected_tests+=($matching_tests) + fi + + shift + ;; + esac +done + +if (( ${#selected_tests[@]} == 0 && ${#patterns_with_no_matches[@]} == 0 )) +then + selected_tests=(*) +fi +popd > /dev/null case "$OSTYPE" in msys) @@ -51,7 +87,7 @@ case "$OSTYPE" in SOLC="${SOLIDITY_BUILD_DIR}/solc/solc" ;; esac -echo "${SOLC}" +echo "Using solc binary at ${SOLC}" INTERACTIVE=true if ! tty -s || [ "$CI" ] @@ -60,7 +96,8 @@ then fi # extend stack size in case we run via ASAN -if [[ -n "${CIRCLECI}" ]] || [[ -n "$CI" ]]; then +if [[ -n "${CIRCLECI}" ]] || [[ -n "$CI" ]] +then ulimit -s 16384 ulimit -a fi @@ -71,6 +108,15 @@ function update_expectation { local newExpectation="${1}" local expectationFile="${2}" + if [[ $newExpectation == "" || $newExpectation -eq 0 && $expectationFile == */exit ]] + then + if [[ -f $expectationFile ]] + then + rm "$expectationFile" + fi + return + fi + echo "$newExpectation" > "$expectationFile" printLog "File $expectationFile updated to match the expectation." } @@ -82,7 +128,7 @@ function ask_expectation_update local newExpectation="${1}" local expectationFile="${2}" - if [[ $AUTOUPDATE == true ]] + if [[ $autoupdate == true ]] then update_expectation "$newExpectation" "$expectationFile" else @@ -126,7 +172,10 @@ function test_solc_behaviour() # shellcheck disable=SC2064 trap "rm -f $stdout_path $stderr_path" EXIT - if [[ "$exit_code_expected" = "" ]]; then exit_code_expected="0"; fi + if [[ "$exit_code_expected" = "" ]] + then + exit_code_expected="0" + fi [[ $filename == "" ]] || solc_args+=("$filename") @@ -234,7 +283,7 @@ EOF [[ $stderr_expectation_file == "" ]] && exit 1 fi - rm -f "$stdout_path" "$stderr_path" + rm "$stdout_path" "$stderr_path" } @@ -246,7 +295,7 @@ function test_solc_assembly_output() local expected_object="object \"object\" { code ${expected} }" - output=$(echo "${input}" | "$SOLC" - "${solc_args[@]}" 2>/dev/null) + output=$(echo "${input}" | msg_on_error --no-stderr "$SOLC" - "${solc_args[@]}") empty=$(echo "$output" | tr '\n' ' ' | tr -s ' ' | sed -ne "/${expected_object}/p") if [ -z "$empty" ] then @@ -274,8 +323,7 @@ printTask "Testing unknown options..." then echo "Passed" else - printError "Incorrect response to unknown options: $output" - exit 1 + fail "Incorrect response to unknown options: $output" fi ) @@ -293,25 +341,31 @@ test_solc_behaviour "${0}" "ctx:=/some/remapping/target" "" "" 1 "" "Invalid rem printTask "Running general commandline tests..." ( cd "$REPO_ROOT"/test/cmdlineTests/ - for tdir in ${*:-*/} + for tdir in "${selected_tests[@]}" do - if ! [[ -d $tdir ]]; then - if [[ $tdir =~ ^--.*$ ]]; then - if [[ $tdir == "--update" ]]; then - printError "The --update option must be given before any positional arguments." - else - printError "Invalid option: $tdir." - fi - else - printError "Test directory not found: $tdir" - fi - exit 1 + if ! [[ -d $tdir ]] + then + fail "Test directory not found: $tdir" fi printTask " - ${tdir}" + if [[ $(ls -A "$tdir") == "" ]] + then + printWarning " ---> skipped (test dir empty)" + continue + fi + # Strip trailing slash from $tdir. tdir=$(basename "${tdir}") + if [[ $no_smt == true ]] + then + if [[ $tdir =~ .*model_checker_.* ]] + then + printWarning " ---> skipped (SMT test)" + continue + fi + fi inputFiles="$(ls -1 "${tdir}/input."* 2> /dev/null || true)" inputCount="$(echo "${inputFiles}" | wc -w)" @@ -327,7 +381,7 @@ printTask "Running general commandline tests..." if [ "${inputFile}" = "${tdir}/input.json" ] then - ! [ -e "${tdir}/stdin" ] || { printError "Found a file called 'stdin' but redirecting standard input in JSON mode is not allowed."; exit 1; } + ! [ -e "${tdir}/stdin" ] || fail "Found a file called 'stdin' but redirecting standard input in JSON mode is not allowed." stdin="${inputFile}" inputFile="" @@ -338,7 +392,7 @@ printTask "Running general commandline tests..." if [ -e "${tdir}/stdin" ] then stdin="${tdir}/stdin" - [ -f "${tdir}/stdin" ] || { printError "'stdin' is not a regular file."; exit 1; } + [ -f "${tdir}/stdin" ] || fail "'stdin' is not a regular file." else stdin="" fi @@ -415,52 +469,50 @@ SOLTMPDIR=$(mktemp -d) compileFull "${opts[@]}" "$SOLTMPDIR/$f" done ) -rm -rf "$SOLTMPDIR" +rm -r "$SOLTMPDIR" echo "Done." printTask "Testing library checksum..." -echo '' | "$SOLC" - --link --libraries a=0x90f20564390eAe531E810af625A22f51385Cd222 >/dev/null +echo '' | msg_on_error --no-stdout "$SOLC" - --link --libraries a=0x90f20564390eAe531E810af625A22f51385Cd222 echo '' | "$SOLC" - --link --libraries a=0x80f20564390eAe531E810af625A22f51385Cd222 &>/dev/null && exit 1 printTask "Testing long library names..." -echo '' | "$SOLC" - --link --libraries aveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylonglibraryname=0x90f20564390eAe531E810af625A22f51385Cd222 >/dev/null +echo '' | msg_on_error --no-stdout "$SOLC" - --link --libraries aveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylonglibraryname=0x90f20564390eAe531E810af625A22f51385Cd222 printTask "Testing linking itself..." SOLTMPDIR=$(mktemp -d) ( cd "$SOLTMPDIR" - set -e echo 'library L { function f() public pure {} } contract C { function f() public pure { L.f(); } }' > x.sol - "$SOLC" --bin -o . x.sol 2>/dev/null + msg_on_error --no-stderr "$SOLC" --bin -o . x.sol # Explanation and placeholder should be there grep -q '//' C.bin && grep -q '__' C.bin # But not in library file. grep -q -v '[/_]' L.bin # Now link - "$SOLC" --link --libraries x.sol:L=0x90f20564390eAe531E810af625A22f51385Cd222 C.bin + msg_on_error "$SOLC" --link --libraries x.sol:L=0x90f20564390eAe531E810af625A22f51385Cd222 C.bin # Now the placeholder and explanation should be gone. grep -q -v '[/_]' C.bin ) -rm -rf "$SOLTMPDIR" +rm -r "$SOLTMPDIR" printTask "Testing overwriting files..." SOLTMPDIR=$(mktemp -d) ( - set -e # First time it works - echo 'contract C {} ' | "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null + echo 'contract C {}' | msg_on_error --no-stderr "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" # Second time it fails - echo 'contract C {} ' | "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null && exit 1 + echo 'contract C {}' | "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null && exit 1 # Unless we force - echo 'contract C {} ' | "$SOLC" - --overwrite --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null + echo 'contract C {}' | msg_on_error --no-stderr "$SOLC" - --overwrite --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" ) -rm -rf "$SOLTMPDIR" +rm -r "$SOLTMPDIR" printTask "Testing assemble, yul, strict-assembly and optimize..." ( - echo '{}' | "$SOLC" - --assemble &>/dev/null - echo '{}' | "$SOLC" - --yul &>/dev/null - echo '{}' | "$SOLC" - --strict-assembly &>/dev/null + echo '{}' | msg_on_error --silent "$SOLC" - --assemble + echo '{}' | msg_on_error --silent "$SOLC" - --yul + echo '{}' | msg_on_error --silent "$SOLC" - --strict-assembly # Test options above in conjunction with --optimize. # Using both, --assemble and --optimize should fail. @@ -482,31 +534,27 @@ printTask "Testing standard input..." SOLTMPDIR=$(mktemp -d) ( set +e - output=$("$SOLC" --bin 2>&1) + output=$("$SOLC" --bin 2>&1) result=$? set -e # This should fail if [[ ! ("$output" =~ "No input files given") || ($result == 0) ]] then - printError "Incorrect response to empty input arg list: $output" - exit 1 + fail "Incorrect response to empty input arg list: $output" fi # The contract should be compiled - if ! output=$(echo 'contract C {} ' | "$SOLC" - --bin 2>/dev/null | grep -q ":C") + if ! echo 'contract C {}' | msg_on_error --no-stderr "$SOLC" - --bin | grep -q ":C" then - printError "Failed to compile a simple contract from standard input" - exit 1 + fail "Failed to compile a simple contract from standard input" fi # This should not fail - if ! output=$(echo '' | "$SOLC" --ast-compact-json - 2>/dev/null) - then - printError "Incorrect response to --ast-compact-json option with empty stdin" - exit 1 - fi + echo '' | msg_on_error --silent --msg "Incorrect response to --ast-compact-json option with empty stdin" \ + "$SOLC" --ast-compact-json - ) +rm -r "$SOLTMPDIR" printTask "Testing AST import..." SOLTMPDIR=$(mktemp -d) @@ -518,7 +566,7 @@ SOLTMPDIR=$(mktemp -d) exit 1 fi ) -rm -rf "$SOLTMPDIR" +rm -r "$SOLTMPDIR" printTask "Testing AST export with stop-after=parsing..." "$REPO_ROOT/test/stopAfterParseTests.sh" @@ -534,6 +582,6 @@ SOLTMPDIR=$(mktemp -d) echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files ) -rm -rf "$SOLTMPDIR" +rm -r "$SOLTMPDIR" echo "Commandline tests successful." diff --git a/test/cmdlineTests/asm_json/args b/test/cmdlineTests/asm_json/args new file mode 100644 index 000000000..8f4713692 --- /dev/null +++ b/test/cmdlineTests/asm_json/args @@ -0,0 +1 @@ +--asm-json diff --git a/test/cmdlineTests/asm_json/input.sol b/test/cmdlineTests/asm_json/input.sol new file mode 100644 index 000000000..da3a08d7f --- /dev/null +++ b/test/cmdlineTests/asm_json/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f(uint x) public pure { + x += 42; + require(x > 100); + } +} diff --git a/test/cmdlineTests/asm_json/output b/test/cmdlineTests/asm_json/output new file mode 100644 index 000000000..26ab87147 --- /dev/null +++ b/test/cmdlineTests/asm_json/output @@ -0,0 +1,1586 @@ + +======= asm_json/input.sol:C ======= +EVM assembly: +{ + ".code": + [ + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "80" + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "40" + }, + { + "begin": 60, + "end": 160, + "name": "MSTORE", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "CALLVALUE", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "DUP1", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "ISZERO", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH [tag]", + "source": 0, + "value": "1" + }, + { + "begin": 60, + "end": 160, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 60, + "end": 160, + "name": "DUP1", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "REVERT", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "tag", + "source": 0, + "value": "1" + }, + { + "begin": 60, + "end": 160, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "POP", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH #[$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 60, + "end": 160, + "name": "DUP1", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH [$]", + "source": 0, + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 60, + "end": 160, + "name": "CODECOPY", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 60, + "end": 160, + "name": "RETURN", + "source": 0 + } + ], + ".data": + { + "0": + { + ".auxdata": "", + ".code": + [ + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "80" + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "40" + }, + { + "begin": 60, + "end": 160, + "name": "MSTORE", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "CALLVALUE", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "DUP1", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "ISZERO", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH [tag]", + "source": 0, + "value": "1" + }, + { + "begin": 60, + "end": 160, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 60, + "end": 160, + "name": "DUP1", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "REVERT", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "tag", + "source": 0, + "value": "1" + }, + { + "begin": 60, + "end": 160, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "POP", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "4" + }, + { + "begin": 60, + "end": 160, + "name": "CALLDATASIZE", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "LT", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH [tag]", + "source": 0, + "value": "2" + }, + { + "begin": 60, + "end": 160, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 60, + "end": 160, + "name": "CALLDATALOAD", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "E0" + }, + { + "begin": 60, + "end": 160, + "name": "SHR", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "DUP1", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "B3DE648B" + }, + { + "begin": 60, + "end": 160, + "name": "EQ", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH [tag]", + "source": 0, + "value": "3" + }, + { + "begin": 60, + "end": 160, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "tag", + "source": 0, + "value": "2" + }, + { + "begin": 60, + "end": 160, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 60, + "end": 160, + "name": "DUP1", + "source": 0 + }, + { + "begin": 60, + "end": 160, + "name": "REVERT", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "tag", + "source": 0, + "value": "3" + }, + { + "begin": 77, + "end": 158, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "PUSH [tag]", + "source": 0, + "value": "4" + }, + { + "begin": 77, + "end": 158, + "name": "PUSH", + "source": 0, + "value": "4" + }, + { + "begin": 77, + "end": 158, + "name": "DUP1", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "CALLDATASIZE", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "SUB", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "DUP2", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "ADD", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "SWAP1", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "PUSH [tag]", + "source": 0, + "value": "5" + }, + { + "begin": 77, + "end": 158, + "name": "SWAP2", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "SWAP1", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "PUSH [tag]", + "source": 0, + "value": "6" + }, + { + "begin": 77, + "end": 158, + "name": "JUMP", + "source": 0, + "value": "[in]" + }, + { + "begin": 77, + "end": 158, + "name": "tag", + "source": 0, + "value": "5" + }, + { + "begin": 77, + "end": 158, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "PUSH [tag]", + "source": 0, + "value": "7" + }, + { + "begin": 77, + "end": 158, + "name": "JUMP", + "source": 0, + "value": "[in]" + }, + { + "begin": 77, + "end": 158, + "name": "tag", + "source": 0, + "value": "4" + }, + { + "begin": 77, + "end": 158, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "STOP", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "tag", + "source": 0, + "value": "7" + }, + { + "begin": 77, + "end": 158, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 123, + "end": 125, + "name": "PUSH", + "source": 0, + "value": "2A" + }, + { + "begin": 118, + "end": 125, + "name": "DUP2", + "source": 0 + }, + { + "begin": 118, + "end": 125, + "name": "PUSH [tag]", + "source": 0, + "value": "9" + }, + { + "begin": 118, + "end": 125, + "name": "SWAP2", + "source": 0 + }, + { + "begin": 118, + "end": 125, + "name": "SWAP1", + "source": 0 + }, + { + "begin": 118, + "end": 125, + "name": "PUSH [tag]", + "source": 0, + "value": "10" + }, + { + "begin": 118, + "end": 125, + "name": "JUMP", + "source": 0, + "value": "[in]" + }, + { + "begin": 118, + "end": 125, + "name": "tag", + "source": 0, + "value": "9" + }, + { + "begin": 118, + "end": 125, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 118, + "end": 125, + "name": "SWAP1", + "source": 0 + }, + { + "begin": 118, + "end": 125, + "name": "POP", + "source": 0 + }, + { + "begin": 147, + "end": 150, + "name": "PUSH", + "source": 0, + "value": "64" + }, + { + "begin": 143, + "end": 144, + "name": "DUP2", + "source": 0 + }, + { + "begin": 143, + "end": 150, + "name": "GT", + "source": 0 + }, + { + "begin": 135, + "end": 151, + "name": "PUSH [tag]", + "source": 0, + "value": "11" + }, + { + "begin": 135, + "end": 151, + "name": "JUMPI", + "source": 0 + }, + { + "begin": 135, + "end": 151, + "name": "PUSH", + "source": 0, + "value": "0" + }, + { + "begin": 135, + "end": 151, + "name": "DUP1", + "source": 0 + }, + { + "begin": 135, + "end": 151, + "name": "REVERT", + "source": 0 + }, + { + "begin": 135, + "end": 151, + "name": "tag", + "source": 0, + "value": "11" + }, + { + "begin": 135, + "end": 151, + "name": "JUMPDEST", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "POP", + "source": 0 + }, + { + "begin": 77, + "end": 158, + "name": "JUMP", + "source": 0, + "value": "[out]" + }, + { + "begin": 88, + "end": 205, + "name": "tag", + "source": 1, + "value": "13" + }, + { + "begin": 88, + "end": 205, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 197, + "end": 198, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 194, + "end": 195, + "name": "DUP1", + "source": 1 + }, + { + "begin": 187, + "end": 199, + "name": "REVERT", + "source": 1 + }, + { + "begin": 334, + "end": 411, + "name": "tag", + "source": 1, + "value": "15" + }, + { + "begin": 334, + "end": 411, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 371, + "end": 378, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 400, + "end": 405, + "name": "DUP2", + "source": 1 + }, + { + "begin": 389, + "end": 405, + "name": "SWAP1", + "source": 1 + }, + { + "begin": 389, + "end": 405, + "name": "POP", + "source": 1 + }, + { + "begin": 334, + "end": 411, + "name": "SWAP2", + "source": 1 + }, + { + "begin": 334, + "end": 411, + "name": "SWAP1", + "source": 1 + }, + { + "begin": 334, + "end": 411, + "name": "POP", + "source": 1 + }, + { + "begin": 334, + "end": 411, + "name": "JUMP", + "source": 1, + "value": "[out]" + }, + { + "begin": 417, + "end": 539, + "name": "tag", + "source": 1, + "value": "16" + }, + { + "begin": 417, + "end": 539, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 490, + "end": 514, + "name": "PUSH [tag]", + "source": 1, + "value": "25" + }, + { + "begin": 508, + "end": 513, + "name": "DUP2", + "source": 1 + }, + { + "begin": 490, + "end": 514, + "name": "PUSH [tag]", + "source": 1, + "value": "15" + }, + { + "begin": 490, + "end": 514, + "name": "JUMP", + "source": 1, + "value": "[in]" + }, + { + "begin": 490, + "end": 514, + "name": "tag", + "source": 1, + "value": "25" + }, + { + "begin": 490, + "end": 514, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 483, + "end": 488, + "name": "DUP2", + "source": 1 + }, + { + "begin": 480, + "end": 515, + "name": "EQ", + "source": 1 + }, + { + "begin": 470, + "end": 533, + "name": "PUSH [tag]", + "source": 1, + "value": "26" + }, + { + "begin": 470, + "end": 533, + "name": "JUMPI", + "source": 1 + }, + { + "begin": 529, + "end": 530, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 526, + "end": 527, + "name": "DUP1", + "source": 1 + }, + { + "begin": 519, + "end": 531, + "name": "REVERT", + "source": 1 + }, + { + "begin": 470, + "end": 533, + "name": "tag", + "source": 1, + "value": "26" + }, + { + "begin": 470, + "end": 533, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 417, + "end": 539, + "name": "POP", + "source": 1 + }, + { + "begin": 417, + "end": 539, + "name": "JUMP", + "source": 1, + "value": "[out]" + }, + { + "begin": 545, + "end": 684, + "name": "tag", + "source": 1, + "value": "17" + }, + { + "begin": 545, + "end": 684, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 591, + "end": 596, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 629, + "end": 635, + "name": "DUP2", + "source": 1 + }, + { + "begin": 616, + "end": 636, + "name": "CALLDATALOAD", + "source": 1 + }, + { + "begin": 607, + "end": 636, + "name": "SWAP1", + "source": 1 + }, + { + "begin": 607, + "end": 636, + "name": "POP", + "source": 1 + }, + { + "begin": 645, + "end": 678, + "name": "PUSH [tag]", + "source": 1, + "value": "28" + }, + { + "begin": 672, + "end": 677, + "name": "DUP2", + "source": 1 + }, + { + "begin": 645, + "end": 678, + "name": "PUSH [tag]", + "source": 1, + "value": "16" + }, + { + "begin": 645, + "end": 678, + "name": "JUMP", + "source": 1, + "value": "[in]" + }, + { + "begin": 645, + "end": 678, + "name": "tag", + "source": 1, + "value": "28" + }, + { + "begin": 645, + "end": 678, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 545, + "end": 684, + "name": "SWAP3", + "source": 1 + }, + { + "begin": 545, + "end": 684, + "name": "SWAP2", + "source": 1 + }, + { + "begin": 545, + "end": 684, + "name": "POP", + "source": 1 + }, + { + "begin": 545, + "end": 684, + "name": "POP", + "source": 1 + }, + { + "begin": 545, + "end": 684, + "name": "JUMP", + "source": 1, + "value": "[out]" + }, + { + "begin": 690, + "end": 1019, + "name": "tag", + "source": 1, + "value": "6" + }, + { + "begin": 690, + "end": 1019, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 749, + "end": 755, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 798, + "end": 800, + "name": "PUSH", + "source": 1, + "value": "20" + }, + { + "begin": 786, + "end": 795, + "name": "DUP3", + "source": 1 + }, + { + "begin": 777, + "end": 784, + "name": "DUP5", + "source": 1 + }, + { + "begin": 773, + "end": 796, + "name": "SUB", + "source": 1 + }, + { + "begin": 769, + "end": 801, + "name": "SLT", + "source": 1 + }, + { + "begin": 766, + "end": 885, + "name": "ISZERO", + "source": 1 + }, + { + "begin": 766, + "end": 885, + "name": "PUSH [tag]", + "source": 1, + "value": "30" + }, + { + "begin": 766, + "end": 885, + "name": "JUMPI", + "source": 1 + }, + { + "begin": 804, + "end": 883, + "name": "PUSH [tag]", + "source": 1, + "value": "31" + }, + { + "begin": 804, + "end": 883, + "name": "PUSH [tag]", + "source": 1, + "value": "13" + }, + { + "begin": 804, + "end": 883, + "name": "JUMP", + "source": 1, + "value": "[in]" + }, + { + "begin": 804, + "end": 883, + "name": "tag", + "source": 1, + "value": "31" + }, + { + "begin": 804, + "end": 883, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 766, + "end": 885, + "name": "tag", + "source": 1, + "value": "30" + }, + { + "begin": 766, + "end": 885, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 924, + "end": 925, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 949, + "end": 1002, + "name": "PUSH [tag]", + "source": 1, + "value": "32" + }, + { + "begin": 994, + "end": 1001, + "name": "DUP5", + "source": 1 + }, + { + "begin": 985, + "end": 991, + "name": "DUP3", + "source": 1 + }, + { + "begin": 974, + "end": 983, + "name": "DUP6", + "source": 1 + }, + { + "begin": 970, + "end": 992, + "name": "ADD", + "source": 1 + }, + { + "begin": 949, + "end": 1002, + "name": "PUSH [tag]", + "source": 1, + "value": "17" + }, + { + "begin": 949, + "end": 1002, + "name": "JUMP", + "source": 1, + "value": "[in]" + }, + { + "begin": 949, + "end": 1002, + "name": "tag", + "source": 1, + "value": "32" + }, + { + "begin": 949, + "end": 1002, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 939, + "end": 1002, + "name": "SWAP2", + "source": 1 + }, + { + "begin": 939, + "end": 1002, + "name": "POP", + "source": 1 + }, + { + "begin": 895, + "end": 1012, + "name": "POP", + "source": 1 + }, + { + "begin": 690, + "end": 1019, + "name": "SWAP3", + "source": 1 + }, + { + "begin": 690, + "end": 1019, + "name": "SWAP2", + "source": 1 + }, + { + "begin": 690, + "end": 1019, + "name": "POP", + "source": 1 + }, + { + "begin": 690, + "end": 1019, + "name": "POP", + "source": 1 + }, + { + "begin": 690, + "end": 1019, + "name": "JUMP", + "source": 1, + "value": "[out]" + }, + { + "begin": 1025, + "end": 1205, + "name": "tag", + "source": 1, + "value": "18" + }, + { + "begin": 1025, + "end": 1205, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 1073, + "end": 1150, + "name": "PUSH", + "source": 1, + "value": "4E487B7100000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 1070, + "end": 1071, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 1063, + "end": 1151, + "name": "MSTORE", + "source": 1 + }, + { + "begin": 1170, + "end": 1174, + "name": "PUSH", + "source": 1, + "value": "11" + }, + { + "begin": 1167, + "end": 1168, + "name": "PUSH", + "source": 1, + "value": "4" + }, + { + "begin": 1160, + "end": 1175, + "name": "MSTORE", + "source": 1 + }, + { + "begin": 1194, + "end": 1198, + "name": "PUSH", + "source": 1, + "value": "24" + }, + { + "begin": 1191, + "end": 1192, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 1184, + "end": 1199, + "name": "REVERT", + "source": 1 + }, + { + "begin": 1211, + "end": 1516, + "name": "tag", + "source": 1, + "value": "10" + }, + { + "begin": 1211, + "end": 1516, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 1251, + "end": 1254, + "name": "PUSH", + "source": 1, + "value": "0" + }, + { + "begin": 1270, + "end": 1290, + "name": "PUSH [tag]", + "source": 1, + "value": "35" + }, + { + "begin": 1288, + "end": 1289, + "name": "DUP3", + "source": 1 + }, + { + "begin": 1270, + "end": 1290, + "name": "PUSH [tag]", + "source": 1, + "value": "15" + }, + { + "begin": 1270, + "end": 1290, + "name": "JUMP", + "source": 1, + "value": "[in]" + }, + { + "begin": 1270, + "end": 1290, + "name": "tag", + "source": 1, + "value": "35" + }, + { + "begin": 1270, + "end": 1290, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 1265, + "end": 1290, + "name": "SWAP2", + "source": 1 + }, + { + "begin": 1265, + "end": 1290, + "name": "POP", + "source": 1 + }, + { + "begin": 1304, + "end": 1324, + "name": "PUSH [tag]", + "source": 1, + "value": "36" + }, + { + "begin": 1322, + "end": 1323, + "name": "DUP4", + "source": 1 + }, + { + "begin": 1304, + "end": 1324, + "name": "PUSH [tag]", + "source": 1, + "value": "15" + }, + { + "begin": 1304, + "end": 1324, + "name": "JUMP", + "source": 1, + "value": "[in]" + }, + { + "begin": 1304, + "end": 1324, + "name": "tag", + "source": 1, + "value": "36" + }, + { + "begin": 1304, + "end": 1324, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 1299, + "end": 1324, + "name": "SWAP3", + "source": 1 + }, + { + "begin": 1299, + "end": 1324, + "name": "POP", + "source": 1 + }, + { + "begin": 1458, + "end": 1459, + "name": "DUP3", + "source": 1 + }, + { + "begin": 1390, + "end": 1456, + "name": "PUSH", + "source": 1, + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 1386, + "end": 1460, + "name": "SUB", + "source": 1 + }, + { + "begin": 1383, + "end": 1384, + "name": "DUP3", + "source": 1 + }, + { + "begin": 1380, + "end": 1461, + "name": "GT", + "source": 1 + }, + { + "begin": 1377, + "end": 1484, + "name": "ISZERO", + "source": 1 + }, + { + "begin": 1377, + "end": 1484, + "name": "PUSH [tag]", + "source": 1, + "value": "37" + }, + { + "begin": 1377, + "end": 1484, + "name": "JUMPI", + "source": 1 + }, + { + "begin": 1464, + "end": 1482, + "name": "PUSH [tag]", + "source": 1, + "value": "38" + }, + { + "begin": 1464, + "end": 1482, + "name": "PUSH [tag]", + "source": 1, + "value": "18" + }, + { + "begin": 1464, + "end": 1482, + "name": "JUMP", + "source": 1, + "value": "[in]" + }, + { + "begin": 1464, + "end": 1482, + "name": "tag", + "source": 1, + "value": "38" + }, + { + "begin": 1464, + "end": 1482, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 1377, + "end": 1484, + "name": "tag", + "source": 1, + "value": "37" + }, + { + "begin": 1377, + "end": 1484, + "name": "JUMPDEST", + "source": 1 + }, + { + "begin": 1508, + "end": 1509, + "name": "DUP3", + "source": 1 + }, + { + "begin": 1505, + "end": 1506, + "name": "DUP3", + "source": 1 + }, + { + "begin": 1501, + "end": 1510, + "name": "ADD", + "source": 1 + }, + { + "begin": 1494, + "end": 1510, + "name": "SWAP1", + "source": 1 + }, + { + "begin": 1494, + "end": 1510, + "name": "POP", + "source": 1 + }, + { + "begin": 1211, + "end": 1516, + "name": "SWAP3", + "source": 1 + }, + { + "begin": 1211, + "end": 1516, + "name": "SWAP2", + "source": 1 + }, + { + "begin": 1211, + "end": 1516, + "name": "POP", + "source": 1 + }, + { + "begin": 1211, + "end": 1516, + "name": "POP", + "source": 1 + }, + { + "begin": 1211, + "end": 1516, + "name": "JUMP", + "source": 1, + "value": "[out]" + } + ] + } + } +} diff --git a/test/cmdlineTests/ast_compact_json_with_base_path/output b/test/cmdlineTests/ast_compact_json_with_base_path/output index ce8c4d274..6404ef3bd 100644 --- a/test/cmdlineTests/ast_compact_json_with_base_path/output +++ b/test/cmdlineTests/ast_compact_json_with_base_path/output @@ -30,6 +30,7 @@ JSON AST (compact format): { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/cmdlineTests/ast_json_import_wrong_evmVersion/args b/test/cmdlineTests/ast_json_import_wrong_evmVersion/args index 87444708c..d8fddfec6 100644 --- a/test/cmdlineTests/ast_json_import_wrong_evmVersion/args +++ b/test/cmdlineTests/ast_json_import_wrong_evmVersion/args @@ -1 +1 @@ ---evm-version=homestead --import-ast --combined-json ast,compact-format --pretty-json +--evm-version=homestead --import-ast --combined-json ast --pretty-json diff --git a/test/cmdlineTests/combined_json_generated_sources/output b/test/cmdlineTests/combined_json_generated_sources/output index 3cf5cfb68..0bd9395fe 100644 --- a/test/cmdlineTests/combined_json_generated_sources/output +++ b/test/cmdlineTests/combined_json_generated_sources/output @@ -17,14 +17,294 @@ "body": { "nodeType": "YulBlock", - "src": "114:478:1", + "src": "47:35:1", + "statements": + [ + { + "nodeType": "YulAssignment", + "src": "57:19:1", + "value": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "73:2:1", + "type": "", + "value": "64" + } + ], + "functionName": + { + "name": "mload", + "nodeType": "YulIdentifier", + "src": "67:5:1" + }, + "nodeType": "YulFunctionCall", + "src": "67:9:1" + }, + "variableNames": + [ + { + "name": "memPtr", + "nodeType": "YulIdentifier", + "src": "57:6:1" + } + ] + } + ] + }, + "name": "allocate_unbounded", + "nodeType": "YulFunctionDefinition", + "returnVariables": + [ + { + "name": "memPtr", + "nodeType": "YulTypedName", + "src": "40:6:1", + "type": "" + } + ], + "src": "7:75:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "177:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "194:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "197:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "187:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "187:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "187:12:1" + } + ] + }, + "name": "revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b", + "nodeType": "YulFunctionDefinition", + "src": "88:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "300:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "317:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "320:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "310:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "310:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "310:12:1" + } + ] + }, + "name": "revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db", + "nodeType": "YulFunctionDefinition", + "src": "211:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "423:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "440:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "443:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "433:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "433:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "433:12:1" + } + ] + }, + "name": "revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d", + "nodeType": "YulFunctionDefinition", + "src": "334:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "546:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "563:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "566:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "556:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "556:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "556:12:1" + } + ] + }, + "name": "revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490", + "nodeType": "YulFunctionDefinition", + "src": "457:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "669:28: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" + } + ] + }, + "name": "revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef", + "nodeType": "YulFunctionDefinition", + "src": "580:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "810:478:1", "statements": [ { "body": { "nodeType": "YulBlock", - "src": "163:83:1", + "src": "859:83:1", "statements": [ { @@ -35,13 +315,13 @@ { "name": "revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d", "nodeType": "YulIdentifier", - "src": "165:77:1" + "src": "861:77:1" }, "nodeType": "YulFunctionCall", - "src": "165:79:1" + "src": "861:79:1" }, "nodeType": "YulExpressionStatement", - "src": "165:79:1" + "src": "861:79:1" } ] }, @@ -58,12 +338,12 @@ { "name": "offset", "nodeType": "YulIdentifier", - "src": "142:6:1" + "src": "838:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "150:4:1", + "src": "846:4:1", "type": "", "value": "0x1f" } @@ -72,42 +352,42 @@ { "name": "add", "nodeType": "YulIdentifier", - "src": "138:3:1" + "src": "834:3:1" }, "nodeType": "YulFunctionCall", - "src": "138:17:1" + "src": "834:17:1" }, { "name": "end", "nodeType": "YulIdentifier", - "src": "157:3:1" + "src": "853:3:1" } ], "functionName": { "name": "slt", "nodeType": "YulIdentifier", - "src": "134:3:1" + "src": "830:3:1" }, "nodeType": "YulFunctionCall", - "src": "134:27:1" + "src": "830:27:1" } ], "functionName": { "name": "iszero", "nodeType": "YulIdentifier", - "src": "127:6:1" + "src": "823:6:1" }, "nodeType": "YulFunctionCall", - "src": "127:35:1" + "src": "823:35:1" }, "nodeType": "YulIf", - "src": "124:122:1" + "src": "820:122:1" }, { "nodeType": "YulAssignment", - "src": "255:30:1", + "src": "951:30:1", "value": { "arguments": @@ -115,24 +395,24 @@ { "name": "offset", "nodeType": "YulIdentifier", - "src": "278:6:1" + "src": "974:6:1" } ], "functionName": { "name": "calldataload", "nodeType": "YulIdentifier", - "src": "265:12:1" + "src": "961:12:1" }, "nodeType": "YulFunctionCall", - "src": "265:20:1" + "src": "961:20:1" }, "variableNames": [ { "name": "length", "nodeType": "YulIdentifier", - "src": "255:6:1" + "src": "951:6:1" } ] }, @@ -140,7 +420,7 @@ "body": { "nodeType": "YulBlock", - "src": "328:83:1", + "src": "1024:83:1", "statements": [ { @@ -151,13 +431,13 @@ { "name": "revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490", "nodeType": "YulIdentifier", - "src": "330:77:1" + "src": "1026:77:1" }, "nodeType": "YulFunctionCall", - "src": "330:79:1" + "src": "1026:79:1" }, "nodeType": "YulExpressionStatement", - "src": "330:79:1" + "src": "1026:79:1" } ] }, @@ -168,12 +448,12 @@ { "name": "length", "nodeType": "YulIdentifier", - "src": "300:6:1" + "src": "996:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "308:18:1", + "src": "1004:18:1", "type": "", "value": "0xffffffffffffffff" } @@ -182,17 +462,17 @@ { "name": "gt", "nodeType": "YulIdentifier", - "src": "297:2:1" + "src": "993:2:1" }, "nodeType": "YulFunctionCall", - "src": "297:30:1" + "src": "993:30:1" }, "nodeType": "YulIf", - "src": "294:117:1" + "src": "990:117:1" }, { "nodeType": "YulAssignment", - "src": "420:29:1", + "src": "1116:29:1", "value": { "arguments": @@ -200,12 +480,12 @@ { "name": "offset", "nodeType": "YulIdentifier", - "src": "436:6:1" + "src": "1132:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "444:4:1", + "src": "1140:4:1", "type": "", "value": "0x20" } @@ -214,17 +494,17 @@ { "name": "add", "nodeType": "YulIdentifier", - "src": "432:3:1" + "src": "1128:3:1" }, "nodeType": "YulFunctionCall", - "src": "432:17:1" + "src": "1128:17:1" }, "variableNames": [ { "name": "arrayPos", "nodeType": "YulIdentifier", - "src": "420:8:1" + "src": "1116:8:1" } ] }, @@ -232,7 +512,7 @@ "body": { "nodeType": "YulBlock", - "src": "503:83:1", + "src": "1199:83:1", "statements": [ { @@ -243,13 +523,13 @@ { "name": "revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef", "nodeType": "YulIdentifier", - "src": "505:77:1" + "src": "1201:77:1" }, "nodeType": "YulFunctionCall", - "src": "505:79:1" + "src": "1201:79:1" }, "nodeType": "YulExpressionStatement", - "src": "505:79:1" + "src": "1201:79:1" } ] }, @@ -263,7 +543,7 @@ { "name": "arrayPos", "nodeType": "YulIdentifier", - "src": "468:8:1" + "src": "1164:8:1" }, { "arguments": @@ -271,12 +551,12 @@ { "name": "length", "nodeType": "YulIdentifier", - "src": "482:6:1" + "src": "1178:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "490:4:1", + "src": "1186:4:1", "type": "", "value": "0x20" } @@ -285,38 +565,38 @@ { "name": "mul", "nodeType": "YulIdentifier", - "src": "478:3:1" + "src": "1174:3:1" }, "nodeType": "YulFunctionCall", - "src": "478:17:1" + "src": "1174:17:1" } ], "functionName": { "name": "add", "nodeType": "YulIdentifier", - "src": "464:3:1" + "src": "1160:3:1" }, "nodeType": "YulFunctionCall", - "src": "464:32:1" + "src": "1160:32:1" }, { "name": "end", "nodeType": "YulIdentifier", - "src": "498:3:1" + "src": "1194:3:1" } ], "functionName": { "name": "gt", "nodeType": "YulIdentifier", - "src": "461:2:1" + "src": "1157:2:1" }, "nodeType": "YulFunctionCall", - "src": "461:41:1" + "src": "1157:41:1" }, "nodeType": "YulIf", - "src": "458:128:1" + "src": "1154:128:1" } ] }, @@ -327,13 +607,13 @@ { "name": "offset", "nodeType": "YulTypedName", - "src": "81:6:1", + "src": "777:6:1", "type": "" }, { "name": "end", "nodeType": "YulTypedName", - "src": "89:3:1", + "src": "785:3:1", "type": "" } ], @@ -342,30 +622,30 @@ { "name": "arrayPos", "nodeType": "YulTypedName", - "src": "97:8:1", + "src": "793:8:1", "type": "" }, { "name": "length", "nodeType": "YulTypedName", - "src": "107:6:1", + "src": "803:6:1", "type": "" } ], - "src": "24:568:1" + "src": "720:568:1" }, { "body": { "nodeType": "YulBlock", - "src": "699:458:1", + "src": "1395:458:1", "statements": [ { "body": { "nodeType": "YulBlock", - "src": "745:83:1", + "src": "1441:83:1", "statements": [ { @@ -376,13 +656,13 @@ { "name": "revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b", "nodeType": "YulIdentifier", - "src": "747:77:1" + "src": "1443:77:1" }, "nodeType": "YulFunctionCall", - "src": "747:79:1" + "src": "1443:79:1" }, "nodeType": "YulExpressionStatement", - "src": "747:79:1" + "src": "1443:79:1" } ] }, @@ -396,27 +676,27 @@ { "name": "dataEnd", "nodeType": "YulIdentifier", - "src": "720:7:1" + "src": "1416:7:1" }, { "name": "headStart", "nodeType": "YulIdentifier", - "src": "729:9:1" + "src": "1425:9:1" } ], "functionName": { "name": "sub", "nodeType": "YulIdentifier", - "src": "716:3:1" + "src": "1412:3:1" }, "nodeType": "YulFunctionCall", - "src": "716:23:1" + "src": "1412:23:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "741:2:1", + "src": "1437:2:1", "type": "", "value": "32" } @@ -425,22 +705,22 @@ { "name": "slt", "nodeType": "YulIdentifier", - "src": "712:3:1" + "src": "1408:3:1" }, "nodeType": "YulFunctionCall", - "src": "712:32:1" + "src": "1408:32:1" }, "nodeType": "YulIf", - "src": "709:119:1" + "src": "1405:119:1" }, { "nodeType": "YulBlock", - "src": "838:312:1", + "src": "1534:312:1", "statements": [ { "nodeType": "YulVariableDeclaration", - "src": "853:45:1", + "src": "1549:45:1", "value": { "arguments": @@ -451,12 +731,12 @@ { "name": "headStart", "nodeType": "YulIdentifier", - "src": "884:9:1" + "src": "1580:9:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "895:1:1", + "src": "1591:1:1", "type": "", "value": "0" } @@ -465,27 +745,27 @@ { "name": "add", "nodeType": "YulIdentifier", - "src": "880:3:1" + "src": "1576:3:1" }, "nodeType": "YulFunctionCall", - "src": "880:17:1" + "src": "1576:17:1" } ], "functionName": { "name": "calldataload", "nodeType": "YulIdentifier", - "src": "867:12:1" + "src": "1563:12:1" }, "nodeType": "YulFunctionCall", - "src": "867:31:1" + "src": "1563:31:1" }, "variables": [ { "name": "offset", "nodeType": "YulTypedName", - "src": "857:6:1", + "src": "1553:6:1", "type": "" } ] @@ -494,7 +774,7 @@ "body": { "nodeType": "YulBlock", - "src": "945:83:1", + "src": "1641:83:1", "statements": [ { @@ -505,13 +785,13 @@ { "name": "revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db", "nodeType": "YulIdentifier", - "src": "947:77:1" + "src": "1643:77:1" }, "nodeType": "YulFunctionCall", - "src": "947:79:1" + "src": "1643:79:1" }, "nodeType": "YulExpressionStatement", - "src": "947:79:1" + "src": "1643:79:1" } ] }, @@ -522,12 +802,12 @@ { "name": "offset", "nodeType": "YulIdentifier", - "src": "917:6:1" + "src": "1613:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "925:18:1", + "src": "1621:18:1", "type": "", "value": "0xffffffffffffffff" } @@ -536,17 +816,17 @@ { "name": "gt", "nodeType": "YulIdentifier", - "src": "914:2:1" + "src": "1610:2:1" }, "nodeType": "YulFunctionCall", - "src": "914:30:1" + "src": "1610:30:1" }, "nodeType": "YulIf", - "src": "911:117:1" + "src": "1607:117:1" }, { "nodeType": "YulAssignment", - "src": "1042:98:1", + "src": "1738:98:1", "value": { "arguments": @@ -557,49 +837,49 @@ { "name": "headStart", "nodeType": "YulIdentifier", - "src": "1112:9:1" + "src": "1808:9:1" }, { "name": "offset", "nodeType": "YulIdentifier", - "src": "1123:6:1" + "src": "1819:6:1" } ], "functionName": { "name": "add", "nodeType": "YulIdentifier", - "src": "1108:3:1" + "src": "1804:3:1" }, "nodeType": "YulFunctionCall", - "src": "1108:22:1" + "src": "1804:22:1" }, { "name": "dataEnd", "nodeType": "YulIdentifier", - "src": "1132:7:1" + "src": "1828:7:1" } ], "functionName": { "name": "abi_decode_t_array$_t_uint256_$dyn_calldata_ptr", "nodeType": "YulIdentifier", - "src": "1060:47:1" + "src": "1756:47:1" }, "nodeType": "YulFunctionCall", - "src": "1060:80:1" + "src": "1756:80:1" }, "variableNames": [ { "name": "value0", "nodeType": "YulIdentifier", - "src": "1042:6:1" + "src": "1738:6:1" }, { "name": "value1", "nodeType": "YulIdentifier", - "src": "1050:6:1" + "src": "1746:6:1" } ] } @@ -614,13 +894,13 @@ { "name": "headStart", "nodeType": "YulTypedName", - "src": "661:9:1", + "src": "1357:9:1", "type": "" }, { "name": "dataEnd", "nodeType": "YulTypedName", - "src": "672:7:1", + "src": "1368:7:1", "type": "" } ], @@ -629,301 +909,21 @@ { "name": "value0", "nodeType": "YulTypedName", - "src": "684:6:1", + "src": "1380:6:1", "type": "" }, { "name": "value1", "nodeType": "YulTypedName", - "src": "692:6:1", + "src": "1388:6:1", "type": "" } ], - "src": "598:559:1" - }, - { - "body": - { - "nodeType": "YulBlock", - "src": "1203:35:1", - "statements": - [ - { - "nodeType": "YulAssignment", - "src": "1213:19:1", - "value": - { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1229:2:1", - "type": "", - "value": "64" - } - ], - "functionName": - { - "name": "mload", - "nodeType": "YulIdentifier", - "src": "1223:5:1" - }, - "nodeType": "YulFunctionCall", - "src": "1223:9:1" - }, - "variableNames": - [ - { - "name": "memPtr", - "nodeType": "YulIdentifier", - "src": "1213:6:1" - } - ] - } - ] - }, - "name": "allocate_unbounded", - "nodeType": "YulFunctionDefinition", - "returnVariables": - [ - { - "name": "memPtr", - "nodeType": "YulTypedName", - "src": "1196:6:1", - "type": "" - } - ], - "src": "1163:75:1" - }, - { - "body": - { - "nodeType": "YulBlock", - "src": "1333:28:1", - "statements": - [ - { - "expression": - { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1350:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1353:1:1", - "type": "", - "value": "0" - } - ], - "functionName": - { - "name": "revert", - "nodeType": "YulIdentifier", - "src": "1343:6:1" - }, - "nodeType": "YulFunctionCall", - "src": "1343:12:1" - }, - "nodeType": "YulExpressionStatement", - "src": "1343:12:1" - } - ] - }, - "name": "revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490", - "nodeType": "YulFunctionDefinition", - "src": "1244:117:1" - }, - { - "body": - { - "nodeType": "YulBlock", - "src": "1456:28:1", - "statements": - [ - { - "expression": - { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1473:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1476:1:1", - "type": "", - "value": "0" - } - ], - "functionName": - { - "name": "revert", - "nodeType": "YulIdentifier", - "src": "1466:6:1" - }, - "nodeType": "YulFunctionCall", - "src": "1466:12:1" - }, - "nodeType": "YulExpressionStatement", - "src": "1466:12:1" - } - ] - }, - "name": "revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d", - "nodeType": "YulFunctionDefinition", - "src": "1367:117:1" - }, - { - "body": - { - "nodeType": "YulBlock", - "src": "1579:28:1", - "statements": - [ - { - "expression": - { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1596:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1599:1:1", - "type": "", - "value": "0" - } - ], - "functionName": - { - "name": "revert", - "nodeType": "YulIdentifier", - "src": "1589:6:1" - }, - "nodeType": "YulFunctionCall", - "src": "1589:12:1" - }, - "nodeType": "YulExpressionStatement", - "src": "1589:12:1" - } - ] - }, - "name": "revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef", - "nodeType": "YulFunctionDefinition", - "src": "1490:117:1" - }, - { - "body": - { - "nodeType": "YulBlock", - "src": "1702:28:1", - "statements": - [ - { - "expression": - { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1719:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1722:1:1", - "type": "", - "value": "0" - } - ], - "functionName": - { - "name": "revert", - "nodeType": "YulIdentifier", - "src": "1712:6:1" - }, - "nodeType": "YulFunctionCall", - "src": "1712:12:1" - }, - "nodeType": "YulExpressionStatement", - "src": "1712:12:1" - } - ] - }, - "name": "revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db", - "nodeType": "YulFunctionDefinition", - "src": "1613:117:1" - }, - { - "body": - { - "nodeType": "YulBlock", - "src": "1825:28:1", - "statements": - [ - { - "expression": - { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1842:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "1845:1:1", - "type": "", - "value": "0" - } - ], - "functionName": - { - "name": "revert", - "nodeType": "YulIdentifier", - "src": "1835:6:1" - }, - "nodeType": "YulFunctionCall", - "src": "1835:12:1" - }, - "nodeType": "YulExpressionStatement", - "src": "1835:12:1" - } - ] - }, - "name": "revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b", - "nodeType": "YulFunctionDefinition", - "src": "1736:117:1" + "src": "1294:559: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_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() }\n length := calldataload(offset)\n if gt(length, 0xffffffffffffffff) { revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490() }\n arrayPos := add(offset, 0x20)\n if gt(add(arrayPos, mul(length, 0x20)), end) { revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() }\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_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() }\n\n {\n\n let offset := calldataload(add(headStart, 0))\n if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() }\n\n value0, value1 := abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(add(headStart, offset), dataEnd)\n }\n\n }\n\n function allocate_unbounded() -> memPtr {\n memPtr := mload(64)\n }\n\n function revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490() {\n revert(0, 0)\n }\n\n function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() {\n revert(0, 0)\n }\n\n function revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() {\n revert(0, 0)\n }\n\n function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() {\n revert(0, 0)\n }\n\n function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() {\n revert(0, 0)\n }\n\n}\n", + "contents": "{\n\n function allocate_unbounded() -> memPtr {\n memPtr := mload(64)\n }\n\n function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() {\n revert(0, 0)\n }\n\n function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() {\n revert(0, 0)\n }\n\n function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() {\n revert(0, 0)\n }\n\n function revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490() {\n revert(0, 0)\n }\n\n function revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() {\n revert(0, 0)\n }\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_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() }\n length := calldataload(offset)\n if gt(length, 0xffffffffffffffff) { revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490() }\n arrayPos := add(offset, 0x20)\n if gt(add(arrayPos, mul(length, 0x20)), end) { revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() }\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_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() }\n\n {\n\n let offset := calldataload(add(headStart, 0))\n if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() }\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" diff --git a/test/cmdlineTests/combined_json_with_base_path/output b/test/cmdlineTests/combined_json_with_base_path/output index 206bfc639..fe00b71a0 100644 --- a/test/cmdlineTests/combined_json_with_base_path/output +++ b/test/cmdlineTests/combined_json_with_base_path/output @@ -1 +1 @@ -{"contracts":{"combined_json_with_base_path/c.sol:C":{}},"sourceList":["combined_json_with_base_path/c.sol","combined_json_with_base_path/input.sol"],"sources":{"combined_json_with_base_path/c.sol":{"AST":{"absolutePath":"combined_json_with_base_path/c.sol","exportedSymbols":{"C":[5]},"id":6,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":4,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":5,"linearizedBaseContracts":[5],"name":"C","nameLocation":"69:1:0","nodeType":"ContractDefinition","nodes":[],"scope":6,"src":"60:13:0","usedErrors":[]}],"src":"36:38:0"}},"combined_json_with_base_path/input.sol":{"AST":{"absolutePath":"combined_json_with_base_path/input.sol","exportedSymbols":{"C":[5]},"id":3,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:1"},{"absolutePath":"combined_json_with_base_path/c.sol","file":"./c.sol","id":2,"nameLocation":"-1:-1:-1","nodeType":"ImportDirective","scope":3,"sourceUnit":6,"src":"60:17:1","symbolAliases":[],"unitAlias":""}],"src":"36:42:1"}}},"version": ""} +{"contracts":{"combined_json_with_base_path/c.sol:C":{}},"sourceList":["combined_json_with_base_path/c.sol","combined_json_with_base_path/input.sol"],"sources":{"combined_json_with_base_path/c.sol":{"AST":{"absolutePath":"combined_json_with_base_path/c.sol","exportedSymbols":{"C":[5]},"id":6,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":4,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:0"},{"abstract":false,"baseContracts":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":5,"linearizedBaseContracts":[5],"name":"C","nameLocation":"69:1:0","nodeType":"ContractDefinition","nodes":[],"scope":6,"src":"60:13:0","usedErrors":[]}],"src":"36:38:0"}},"combined_json_with_base_path/input.sol":{"AST":{"absolutePath":"combined_json_with_base_path/input.sol","exportedSymbols":{"C":[5]},"id":3,"license":"GPL-3.0","nodeType":"SourceUnit","nodes":[{"id":1,"literals":["solidity",">=","0.0"],"nodeType":"PragmaDirective","src":"36:22:1"},{"absolutePath":"combined_json_with_base_path/c.sol","file":"./c.sol","id":2,"nameLocation":"-1:-1:-1","nodeType":"ImportDirective","scope":3,"sourceUnit":6,"src":"60:17:1","symbolAliases":[],"unitAlias":""}],"src":"36:42:1"}}},"version": ""} diff --git a/test/cmdlineTests/constant_optimizer_yul/output b/test/cmdlineTests/constant_optimizer_yul/output index 7fdb98670..328ac2544 100644 --- a/test/cmdlineTests/constant_optimizer_yul/output +++ b/test/cmdlineTests/constant_optimizer_yul/output @@ -6,30 +6,31 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"constant_optimizer_yul/input.sol", 1:"#utility.yul" +/// @use-src 0:"constant_optimizer_yul/input.sol" object "C_12" { code { { - /// @src 0:61:418 + /// @src 0:61:418 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } - /// @src 0:103:238 + /// @src 0:103:238 "assembly {..." sstore(0, shl(180, 1)) - /// @src 0:61:418 + /// @src 0:61:418 "contract C {..." let _1 := datasize("C_12_deployed") codecopy(128, dataoffset("C_12_deployed"), _1) return(128, _1) } } + /// @use-src 0:"constant_optimizer_yul/input.sol" object "C_12_deployed" { code { { - /// @src 0:61:418 + /// @src 0:61:418 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } - /// @src 0:279:410 + /// @src 0:279:410 "assembly {..." sstore(0, 0x1000000000000000000000000000000000000000000000) - /// @src 0:61:418 + /// @src 0:61:418 "contract C {..." stop() } } diff --git a/test/cmdlineTests/debug_info_in_yul_snippet_escaping/args b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/args new file mode 100644 index 000000000..7282a28fe --- /dev/null +++ b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/args @@ -0,0 +1 @@ +--ir-optimized --ir --optimize diff --git a/test/cmdlineTests/debug_info_in_yul_snippet_escaping/input.sol b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/input.sol new file mode 100644 index 000000000..598f3b280 --- /dev/null +++ b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/input.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +// Intentionally badly wrapped and commented in weird places to get source locations inside code +// snippets in generated Yul. Also contains stuff that could break the assembly if not escaped properly. + +contract C {} contract D /** @src 0:96:165 "contract D {..." */ { + function f() /* @use-src 0:"input.sol", 1:"#utility.yul" @ast-id 15 */ public returns (string memory) { C c = new /// @src 0:149:156 "new C()" + C(); c; + string memory s = "/*"; s; return "/** @src 0:96:165 \"contract D {...\" */" + ; + } +} diff --git a/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output new file mode 100644 index 000000000..a9dc1a290 --- /dev/null +++ b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output @@ -0,0 +1,579 @@ +IR: +/*=====================================================* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *=====================================================*/ + + +/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" +object "C_2" { + code { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + + constructor_C_2() + + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_2_deployed"), datasize("C_2_deployed")) + + return(_1, datasize("C_2_deployed")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + /// @src 0:265:278 "contract C {}" + function constructor_C_2() { + + /// @src 0:265:278 "contract C {}" + + } + /// @src 0:265:278 "contract C {}" + + } + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "C_2_deployed" { + code { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + default {} + } + if iszero(calldatasize()) { } + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + } + + data ".metadata" hex"" + } + +} + + +Optimized IR: +/*=====================================================* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *=====================================================*/ + +/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" +object "C_2" { + code { + { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("C_2_deployed") + codecopy(128, dataoffset("C_2_deployed"), _1) + return(128, _1) + } + } + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "C_2_deployed" { + code { + { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + revert(0, 0) + } + } + data ".metadata" hex"" + } +} + +IR: +/*=====================================================* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *=====================================================*/ + + +/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" +object "D_27" { + code { + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + mstore(64, 128) + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + + constructor_D_27() + + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("D_27_deployed"), datasize("D_27_deployed")) + + return(_1, datasize("D_27_deployed")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + function constructor_D_27() { + + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + + } + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + + } + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "D_27_deployed" { + code { + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x26121ff0 + { + // f() + + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := fun_f_26() + let memPos := allocate_unbounded() + let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + if iszero(calldatasize()) { } + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + } + + function array_length_t_string_memory_ptr(value) -> length { + + length := mload(value) + + } + + function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { + mstore(pos, length) + updated_pos := add(pos, 0x20) + } + + function copy_memory_to_memory(src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + if gt(i, length) + { + // clear end + mstore(add(dst, length), 0) + } + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end { + let length := array_length_t_string_memory_ptr(value) + pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) + copy_memory_to_memory(add(value, 0x20), pos, length) + end := add(pos, round_up_to_mul_of_32(length)) + } + + function abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(headStart , value0) -> tail { + tail := add(headStart, 32) + + mstore(add(headStart, 0), sub(tail, headStart)) + tail := abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value0, tail) + + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function revert_forward_1() { + let pos := allocate_unbounded() + returndatacopy(pos, 0, returndatasize()) + revert(pos, returndatasize()) + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function allocate_memory(size) -> memPtr { + memPtr := allocate_unbounded() + finalize_allocation(memPtr, size) + } + + function array_allocation_size_t_string_memory_ptr(length) -> size { + // Make sure we can allocate memory without overflow + if gt(length, 0xffffffffffffffff) { panic_error_0x41() } + + size := round_up_to_mul_of_32(length) + + // add length slot + size := add(size, 0x20) + + } + + function allocate_memory_array_t_string_memory_ptr(length) -> memPtr { + let allocSize := array_allocation_size_t_string_memory_ptr(length) + memPtr := allocate_memory(allocSize) + + mstore(memPtr, length) + + } + + function store_literal_in_memory_c077635d0709aa1fd7cea2045028c270f982d687d1647e48e759eec32ec54a50(memPtr) { + + mstore(add(memPtr, 0), "/*") + + } + + function copy_literal_to_memory_c077635d0709aa1fd7cea2045028c270f982d687d1647e48e759eec32ec54a50() -> memPtr { + memPtr := allocate_memory_array_t_string_memory_ptr(2) + store_literal_in_memory_c077635d0709aa1fd7cea2045028c270f982d687d1647e48e759eec32ec54a50(add(memPtr, 32)) + } + + function convert_t_stringliteral_c077635d0709aa1fd7cea2045028c270f982d687d1647e48e759eec32ec54a50_to_t_string_memory_ptr() -> converted { + converted := copy_literal_to_memory_c077635d0709aa1fd7cea2045028c270f982d687d1647e48e759eec32ec54a50() + } + + function store_literal_in_memory_5bde9a896e3f09acac1496d16642fcdd887d2a000bf1ab18bdff3f17b91e320b(memPtr) { + + mstore(add(memPtr, 0), 0x2f2a2a204073726320303a39363a313635202022636f6e74726163742044207b) + + mstore(add(memPtr, 32), 0x2e2e2e22202a2f00000000000000000000000000000000000000000000000000) + + } + + function copy_literal_to_memory_5bde9a896e3f09acac1496d16642fcdd887d2a000bf1ab18bdff3f17b91e320b() -> memPtr { + memPtr := allocate_memory_array_t_string_memory_ptr(39) + store_literal_in_memory_5bde9a896e3f09acac1496d16642fcdd887d2a000bf1ab18bdff3f17b91e320b(add(memPtr, 32)) + } + + function convert_t_stringliteral_5bde9a896e3f09acac1496d16642fcdd887d2a000bf1ab18bdff3f17b91e320b_to_t_string_memory_ptr() -> converted { + converted := copy_literal_to_memory_5bde9a896e3f09acac1496d16642fcdd887d2a000bf1ab18bdff3f17b91e320b() + } + + /// @ast-id 26 + /// @src 0:336:597 "function f() /* @use-src 0:\"input.sol\", 1:\"#utility.yul\" @ast-id 15 *\/ public returns (string memory) { C c = new /// @src 0:149:156 \"new C()\"..." + function fun_f_26() -> var__5_mpos { + /// @src 0:423:436 "string memory" + let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + var__5_mpos := zero_t_string_memory_ptr_1_mpos + + /// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." + let _2 := allocate_unbounded() + let _3 := add(_2, datasize("C_2")) + if or(gt(_3, 0xffffffffffffffff), lt(_3, _2)) { panic_error_0x41() } + datacopy(_2, dataoffset("C_2"), datasize("C_2")) + _3 := abi_encode_tuple__to__fromStack(_3) + + let expr_13_address := create(0, _2, sub(_3, _2)) + + if iszero(expr_13_address) { revert_forward_1() } + + /// @src 0:440:491 "C c = new /// @src 0:149:156 \"new C()\"..." + let var_c_9_address := expr_13_address + /// @src 0:493:494 "c" + let _4_address := var_c_9_address + let expr_15_address := _4_address + /// @src 0:504:526 "string memory s = \"/*\"" + let var_s_18_mpos := convert_t_stringliteral_c077635d0709aa1fd7cea2045028c270f982d687d1647e48e759eec32ec54a50_to_t_string_memory_ptr() + /// @src 0:528:529 "s" + let _5_mpos := var_s_18_mpos + let expr_21_mpos := _5_mpos + /// @src 0:531:581 "return \"/** @src 0:96:165 \\\"contract D {...\\\" *\/\"" + var__5_mpos := convert_t_stringliteral_5bde9a896e3f09acac1496d16642fcdd887d2a000bf1ab18bdff3f17b91e320b_to_t_string_memory_ptr() + leave + + } + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + + } + /*=====================================================* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *=====================================================*/ + + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "C_2" { + code { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + + constructor_C_2() + + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_2_deployed"), datasize("C_2_deployed")) + + return(_1, datasize("C_2_deployed")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + /// @src 0:265:278 "contract C {}" + function constructor_C_2() { + + /// @src 0:265:278 "contract C {}" + + } + /// @src 0:265:278 "contract C {}" + + } + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "C_2_deployed" { + code { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + default {} + } + if iszero(calldatasize()) { } + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + } + + data ".metadata" hex"" + } + + } + + data ".metadata" hex"" + } + +} + + +Optimized IR: +/*=====================================================* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *=====================================================*/ + +/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" +object "D_27" { + code { + { + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("D_27_deployed") + codecopy(128, dataoffset("D_27_deployed"), _1) + return(128, _1) + } + } + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "D_27_deployed" { + code { + { + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + 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) } + /// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." + let _2 := datasize("C_2") + let _3 := add(/** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ 128, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ 128)) + /// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." + { panic_error_0x41() } + datacopy(/** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ 128, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ dataoffset("C_2"), _2) + if iszero(create(/** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ _1, 128, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ _2)) + { + /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." + let pos := mload(64) + returndatacopy(pos, _1, returndatasize()) + revert(pos, returndatasize()) + } + mstore(add(allocate_memory_array_string(), 32), "/*") + let memPtr := allocate_memory_array_string_482() + mstore(add(memPtr, 32), 0x2f2a2a204073726320303a39363a313635202022636f6e74726163742044207b) + mstore(add(memPtr, 64), shl(200, 0x2e2e2e22202a2f)) + let memPos := mload(64) + return(memPos, sub(abi_encode_string(memPos, memPtr), memPos)) + } + } + revert(0, 0) + } + function abi_encode_string(headStart, value0) -> tail + { + let _1 := 32 + mstore(headStart, _1) + let length := mload(value0) + mstore(add(headStart, _1), length) + let i := 0 + for { } lt(i, length) { i := add(i, _1) } + { + mstore(add(add(headStart, i), 64), mload(add(add(value0, i), _1))) + } + if gt(i, length) + { + mstore(add(add(headStart, length), 64), 0) + } + tail := add(add(headStart, and(add(length, 31), not(31))), 64) + } + function panic_error_0x41() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + function allocate_memory_array_string() -> memPtr + { + let memPtr_1 := mload(64) + let newFreePtr := add(memPtr_1, 64) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_1)) { panic_error_0x41() } + mstore(64, newFreePtr) + memPtr := memPtr_1 + mstore(memPtr_1, 2) + } + function allocate_memory_array_string_482() -> memPtr + { + let memPtr_1 := mload(64) + let newFreePtr := add(memPtr_1, 96) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_1)) { panic_error_0x41() } + mstore(64, newFreePtr) + memPtr := memPtr_1 + mstore(memPtr_1, 39) + } + } + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "C_2" { + code { + { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + if callvalue() { revert(0, 0) } + let _1 := datasize("C_2_deployed") + codecopy(128, dataoffset("C_2_deployed"), _1) + return(128, _1) + } + } + /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" + object "C_2_deployed" { + code { + { + /// @src 0:265:278 "contract C {}" + mstore(64, 128) + revert(0, 0) + } + } + data ".metadata" hex"" + } + } + data ".metadata" hex"" + } +} diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output index 1872afbe4..c4b71dcac 100644 --- a/test/cmdlineTests/exp_base_literal/output +++ b/test/cmdlineTests/exp_base_literal/output @@ -7,10 +7,10 @@ IR: *=====================================================*/ -/// @use-src 0:"exp_base_literal/input.sol", 1:"#utility.yul" +/// @use-src 0:"exp_base_literal/input.sol" object "C_81" { code { - /// @src 0:82:370 + /// @src 0:82:370 "contract C {..." mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,22 +25,23 @@ object "C_81" { memPtr := mload(64) } - /// @src 0:82:370 - function constructor_C_81() { - - /// @src 0:82:370 - - } - /// @src 0:82:370 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:82:370 "contract C {..." + function constructor_C_81() { + + /// @src 0:82:370 "contract C {..." + + } + /// @src 0:82:370 "contract C {..." + } + /// @use-src 0:"exp_base_literal/input.sol" object "C_81_deployed" { code { - /// @src 0:82:370 + /// @src 0:82:370 "contract C {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -65,6 +66,37 @@ object "C_81" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function cleanup_t_uint256(value) -> cleaned { + cleaned := value + } + + function validator_revert_t_uint256(value) { + if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) } + } + function abi_decode_t_uint256(offset, end) -> value { value := calldataload(offset) validator_revert_t_uint256(value) @@ -103,14 +135,18 @@ object "C_81" { } - 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 cleanup_t_int256(value) -> cleaned { + cleaned := value + } + + function abi_encode_t_int256_to_t_int256_fromStack(value, pos) { + mstore(pos, cleanup_t_int256(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) @@ -124,36 +160,34 @@ object "C_81" { } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) } - function checked_exp_t_rational_0_by_1_t_uint256(exponent) -> power { - exponent := cleanup_t_uint256(exponent) - - power := exp(0, exponent) + function zero_value_for_split_t_uint256() -> ret { + ret := 0 } - 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 zero_value_for_split_t_int256() -> ret { + ret := 0 } - 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 cleanup_t_rational_2_by_1(value) -> cleaned { + cleaned := value } - function checked_exp_t_rational_1_by_1_t_uint256(exponent) -> power { - exponent := cleanup_t_uint256(exponent) + function identity(value) -> ret { + ret := value + } - power := exp(1, exponent) + function convert_t_rational_2_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(identity(cleanup_t_rational_2_by_1(value))) + } + + function panic_error_0x11() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x11) + revert(0, 0x24) } function checked_exp_t_rational_2_by_1_t_uint256(exponent) -> power { @@ -164,10 +198,12 @@ object "C_81" { power := exp(2, exponent) } - function checked_exp_t_rational_minus_1_by_1_t_uint256(exponent) -> power { - exponent := cleanup_t_uint256(exponent) + function cleanup_t_rational_minus_2_by_1(value) -> cleaned { + cleaned := value + } - power := exp(115792089237316195423570985008687907853269984665640564039457584007913129639935, exponent) + function convert_t_rational_minus_2_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_rational_minus_2_by_1(value))) } function checked_exp_t_rational_minus_2_by_1_t_uint256(exponent) -> power { @@ -178,39 +214,39 @@ object "C_81" { power := exp(115792089237316195423570985008687907853269984665640564039457584007913129639934, exponent) } - function cleanup_t_int256(value) -> cleaned { - cleaned := value - } - - function cleanup_t_rational_0_by_1(value) -> cleaned { - cleaned := value - } - function cleanup_t_rational_10_by_1(value) -> cleaned { cleaned := value } + function convert_t_rational_10_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(identity(cleanup_t_rational_10_by_1(value))) + } + + 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 cleanup_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1(value) -> cleaned { cleaned := value } - function cleanup_t_rational_1_by_1(value) -> cleaned { - cleaned := value + function convert_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(identity(cleanup_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1(value))) } - function cleanup_t_rational_2_by_1(value) -> cleaned { - cleaned := value + 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 cleanup_t_rational_minus_1_by_1(value) -> cleaned { - cleaned := value - } - - function cleanup_t_rational_minus_2_by_1(value) -> cleaned { - cleaned := value - } - - function cleanup_t_uint256(value) -> cleaned { + function cleanup_t_rational_0_by_1(value) -> cleaned { cleaned := value } @@ -218,147 +254,158 @@ object "C_81" { converted := cleanup_t_uint256(identity(cleanup_t_rational_0_by_1(value))) } - function convert_t_rational_10_by_1_to_t_uint256(value) -> converted { - converted := cleanup_t_uint256(identity(cleanup_t_rational_10_by_1(value))) + function checked_exp_t_rational_0_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + power := exp(0, exponent) } - function convert_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1_to_t_uint256(value) -> converted { - converted := cleanup_t_uint256(identity(cleanup_t_rational_115792089237316195423570985008687907853269984665640564039457584007913129639935_by_1(value))) - } - - function convert_t_rational_1_by_1_to_t_uint256(value) -> converted { - converted := cleanup_t_uint256(identity(cleanup_t_rational_1_by_1(value))) - } - - function convert_t_rational_2_by_1_to_t_uint256(value) -> converted { - converted := cleanup_t_uint256(identity(cleanup_t_rational_2_by_1(value))) + function cleanup_t_rational_minus_1_by_1(value) -> cleaned { + cleaned := value } function convert_t_rational_minus_1_by_1_to_t_int256(value) -> converted { converted := cleanup_t_int256(identity(cleanup_t_rational_minus_1_by_1(value))) } - function convert_t_rational_minus_2_by_1_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_rational_minus_2_by_1(value))) + function checked_exp_t_rational_minus_1_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + power := exp(115792089237316195423570985008687907853269984665640564039457584007913129639935, exponent) } - /// @src 0:96:368 + function cleanup_t_rational_1_by_1(value) -> cleaned { + cleaned := value + } + + function convert_t_rational_1_by_1_to_t_uint256(value) -> converted { + converted := cleanup_t_uint256(identity(cleanup_t_rational_1_by_1(value))) + } + + function checked_exp_t_rational_1_by_1_t_uint256(exponent) -> power { + exponent := cleanup_t_uint256(exponent) + + power := exp(1, exponent) + } + + /// @ast-id 80 + /// @src 0:96:368 "function f(uint a, uint b, uint c, uint d) public pure returns (uint, int, uint, uint) {..." function fun_f_80(var_a_4, var_b_6, var_c_8, var_d_10) -> var__13, var__15, var__17, var__19 { - /// @src 0:160:164 + /// @src 0:160:164 "uint" let zero_t_uint256_1 := zero_value_for_split_t_uint256() var__13 := zero_t_uint256_1 - /// @src 0:166:169 + /// @src 0:166:169 "int" let zero_t_int256_2 := zero_value_for_split_t_int256() var__15 := zero_t_int256_2 - /// @src 0:171:175 + /// @src 0:171:175 "uint" let zero_t_uint256_3 := zero_value_for_split_t_uint256() var__17 := zero_t_uint256_3 - /// @src 0:177:181 + /// @src 0:177:181 "uint" let zero_t_uint256_4 := zero_value_for_split_t_uint256() var__19 := zero_t_uint256_4 - /// @src 0:196:197 + /// @src 0:196:197 "2" let expr_23 := 0x02 - /// @src 0:199:200 + /// @src 0:199:200 "a" let _5 := var_a_4 let expr_24 := _5 - /// @src 0:196:200 + /// @src 0:196:200 "2**a" 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) - /// @src 0:187:200 + /// @src 0:187:200 "uint w = 2**a" let var_w_22 := expr_25 - /// @src 0:213:215 + /// @src 0:213:215 "-2" let expr_30 := 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe - /// @src 0:212:216 + /// @src 0:212:216 "(-2)" let expr_31 := expr_30 - /// @src 0:218:219 + /// @src 0:218:219 "b" let _7 := var_b_6 let expr_32 := _7 - /// @src 0:212:219 + /// @src 0:212:219 "(-2)**b" 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) - /// @src 0:204:219 + /// @src 0:204:219 "int x = (-2)**b" let var_x_28 := expr_33 - /// @src 0:232:234 + /// @src 0:232:234 "10" let expr_37 := 0x0a - /// @src 0:236:237 + /// @src 0:236:237 "c" let _9 := var_c_8 let expr_38 := _9 - /// @src 0:232:237 + /// @src 0:232:237 "10**c" 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) - /// @src 0:223:237 + /// @src 0:223:237 "uint y = 10**c" let var_y_36 := expr_39 - /// @src 0:251:260 + /// @src 0:251:260 "2**256 -1" let expr_47 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - /// @src 0:250:262 + /// @src 0:250:262 "(2**256 -1 )" let expr_48 := expr_47 - /// @src 0:264:265 + /// @src 0:264:265 "d" let _11 := var_d_10 let expr_49 := _11 - /// @src 0:250:265 + /// @src 0:250:265 "(2**256 -1 )**d" 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) - /// @src 0:241:265 + /// @src 0:241:265 "uint z = (2**256 -1 )**d" let var_z_42 := expr_50 - /// @src 0:308:309 + /// @src 0:308:309 "0" let expr_53 := 0x00 - /// @src 0:307:310 + /// @src 0:307:310 "(0)" let expr_54 := expr_53 - /// @src 0:312:313 + /// @src 0:312:313 "a" let _13 := var_a_4 let expr_55 := _13 - /// @src 0:307:313 + /// @src 0:307:313 "(0)**a" 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) - /// @src 0:303:313 + /// @src 0:303:313 "w = (0)**a" var_w_22 := expr_56 let expr_57 := expr_56 - /// @src 0:322:324 + /// @src 0:322:324 "-1" let expr_61 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - /// @src 0:321:325 + /// @src 0:321:325 "(-1)" let expr_62 := expr_61 - /// @src 0:327:328 + /// @src 0:327:328 "b" let _15 := var_b_6 let expr_63 := _15 - /// @src 0:321:328 + /// @src 0:321:328 "(-1)**b" 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) - /// @src 0:317:328 + /// @src 0:317:328 "x = (-1)**b" var_x_28 := expr_64 let expr_65 := expr_64 - /// @src 0:336:337 + /// @src 0:336:337 "1" let expr_68 := 0x01 - /// @src 0:339:340 + /// @src 0:339:340 "c" let _17 := var_c_8 let expr_69 := _17 - /// @src 0:336:340 + /// @src 0:336:340 "1**c" 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) - /// @src 0:332:340 + /// @src 0:332:340 "y = 1**c" var_y_36 := expr_70 let expr_71 := expr_70 - /// @src 0:353:354 + /// @src 0:353:354 "w" let _19 := var_w_22 let expr_73 := _19 - /// @src 0:352:364 + /// @src 0:352:364 "(w, x, y, z)" let expr_77_component_1 := expr_73 - /// @src 0:356:357 + /// @src 0:356:357 "x" let _20 := var_x_28 let expr_74 := _20 - /// @src 0:352:364 + /// @src 0:352:364 "(w, x, y, z)" let expr_77_component_2 := expr_74 - /// @src 0:359:360 + /// @src 0:359:360 "y" let _21 := var_y_36 let expr_75 := _21 - /// @src 0:352:364 + /// @src 0:352:364 "(w, x, y, z)" let expr_77_component_3 := expr_75 - /// @src 0:362:363 + /// @src 0:362:363 "z" let _22 := var_z_42 let expr_76 := _22 - /// @src 0:352:364 + /// @src 0:352:364 "(w, x, y, z)" let expr_77_component_4 := expr_76 - /// @src 0:345:364 + /// @src 0:345:364 "return (w, x, y, z)" var__13 := expr_77_component_1 var__15 := expr_77_component_2 var__17 := expr_77_component_3 @@ -366,52 +413,7 @@ object "C_81" { leave } - /// @src 0:82:370 - - function identity(value) -> ret { - ret := value - } - - function panic_error_0x11() { - mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) - mstore(4, 0x11) - revert(0, 0x24) - } - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - - function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - 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 - } + /// @src 0:82:370 "contract C {..." } diff --git a/test/cmdlineTests/function_debug_info_via_yul/output b/test/cmdlineTests/function_debug_info_via_yul/output index 5af245ce4..8b952a9e9 100644 --- a/test/cmdlineTests/function_debug_info_via_yul/output +++ b/test/cmdlineTests/function_debug_info_via_yul/output @@ -4,7 +4,20 @@ "function_debug_info_via_yul/input.sol:C": { "function-debug": {}, - "function-debug-runtime": {} + "function-debug-runtime": + { + "abi_encode_uint256": + { + "parameterSlots": 2, + "returnSlots": 1 + }, + "calldata_array_index_access_uint256_dyn_calldata": + { + "entryPoint": 168, + "parameterSlots": 2, + "returnSlots": 1 + } + } } }, "version": "" diff --git a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output index 1af925406..12c61e783 100644 --- a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output +++ b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol", 1:"#utility.yul" +/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol" object "C_7" { code { { - /// @src 0:82:117 + /// @src 0:82:117 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") @@ -18,10 +18,11 @@ object "C_7" { return(128, _1) } } + /// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol" object "C_7_deployed" { code { { - /// @src 0:82:117 + /// @src 0:82:117 "contract C {..." mstore(64, 128) revert(0, 0) } @@ -38,11 +39,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol", 1:"#utility.yul" +/// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol" object "D_10" { code { { - /// @src 0:118:137 + /// @src 0:118:137 "contract D is C {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_10_deployed") @@ -50,10 +51,11 @@ object "D_10" { return(128, _1) } } + /// @use-src 0:"ir_compiler_inheritance_nosubobjects/input.sol" object "D_10_deployed" { code { { - /// @src 0:118:137 + /// @src 0:118:137 "contract D is C {..." mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/ir_compiler_subobjects/output b/test/cmdlineTests/ir_compiler_subobjects/output index ba93e6886..8c23c8ad7 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/output +++ b/test/cmdlineTests/ir_compiler_subobjects/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"ir_compiler_subobjects/input.sol", 1:"#utility.yul" +/// @use-src 0:"ir_compiler_subobjects/input.sol" object "C_3" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -18,10 +18,11 @@ object "C_3" { return(128, _1) } } + /// @use-src 0:"ir_compiler_subobjects/input.sol" object "C_3_deployed" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) revert(0, 0) } @@ -38,11 +39,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"ir_compiler_subobjects/input.sol", 1:"#utility.yul" +/// @use-src 0:"ir_compiler_subobjects/input.sol" object "D_16" { code { { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") @@ -50,10 +51,11 @@ object "D_16" { return(128, _1) } } + /// @use-src 0:"ir_compiler_subobjects/input.sol" object "D_16_deployed" { code { { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -62,22 +64,22 @@ object "D_16" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } - /// @src 0:149:156 + /// @src 0:149:156 "new C()" let _2 := datasize("C_3") - let _3 := add(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ _2) - if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 */ 128)) - /// @src 0:149:156 + let _3 := add(/** @src 0:96:165 "contract D {..." */ 128, /** @src 0:149:156 "new C()" */ _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 "contract D {..." */ 128)) + /// @src 0:149:156 "new C()" { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." mstore(_1, shl(224, 0x4e487b71)) mstore(4, 0x41) revert(_1, 0x24) } - /// @src 0:149:156 - datacopy(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ dataoffset("C_3"), _2) - if iszero(create(/** @src 0:96:165 */ _1, 128, /** @src 0:149:156 */ _2)) + /// @src 0:149:156 "new C()" + datacopy(/** @src 0:96:165 "contract D {..." */ 128, /** @src 0:149:156 "new C()" */ dataoffset("C_3"), _2) + if iszero(create(/** @src 0:96:165 "contract D {..." */ _1, 128, /** @src 0:149:156 "new C()" */ _2)) { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." let pos := mload(64) returndatacopy(pos, _1, returndatasize()) revert(pos, returndatasize()) @@ -88,10 +90,11 @@ object "D_16" { revert(0, 0) } } + /// @use-src 0:"ir_compiler_subobjects/input.sol" object "C_3" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -99,10 +102,11 @@ object "D_16" { return(128, _1) } } + /// @use-src 0:"ir_compiler_subobjects/input.sol" object "C_3_deployed" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output index 89ee1b5d9..930899aea 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"ir_with_assembly_no_memoryguard_creation/input.sol", 1:"#utility.yul" +/// @use-src 0:"ir_with_assembly_no_memoryguard_creation/input.sol" object "D_12" { code { { - /// @src 0:82:161 + /// @src 0:82:161 "contract D {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_12_deployed") @@ -18,10 +18,11 @@ object "D_12" { return(128, _1) } } + /// @use-src 0:"ir_with_assembly_no_memoryguard_creation/input.sol" object "D_12_deployed" { code { { - /// @src 0:82:161 + /// @src 0:82:161 "contract D {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output index 1fcf2fcc8..da6540fa7 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"ir_with_assembly_no_memoryguard_runtime/input.sol", 1:"#utility.yul" +/// @use-src 0:"ir_with_assembly_no_memoryguard_runtime/input.sol" object "D_8" { code { { - /// @src 0:82:153 + /// @src 0:82:153 "contract D {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_8_deployed") @@ -18,10 +18,11 @@ object "D_8" { return(128, _1) } } + /// @use-src 0:"ir_with_assembly_no_memoryguard_runtime/input.sol" object "D_8_deployed" { code { { - /// @src 0:82:153 + /// @src 0:82:153 "contract D {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { diff --git a/test/cmdlineTests/keccak_optimization_deploy_code/output b/test/cmdlineTests/keccak_optimization_deploy_code/output index 83c7c3929..4abb046f6 100644 --- a/test/cmdlineTests/keccak_optimization_deploy_code/output +++ b/test/cmdlineTests/keccak_optimization_deploy_code/output @@ -6,32 +6,33 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"keccak_optimization_deploy_code/input.sol", 1:"#utility.yul" +/// @use-src 0:"keccak_optimization_deploy_code/input.sol" object "C_12" { code { { - /// @src 0:62:463 + /// @src 0:62:463 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } - /// @src 0:103:275 + /// @src 0:103:275 "assembly {..." mstore(0, 100) sstore(0, keccak256(0, 32)) - /// @src 0:62:463 + /// @src 0:62:463 "contract C {..." let _1 := datasize("C_12_deployed") codecopy(128, dataoffset("C_12_deployed"), _1) return(128, _1) } } + /// @use-src 0:"keccak_optimization_deploy_code/input.sol" object "C_12_deployed" { code { { - /// @src 0:62:463 + /// @src 0:62:463 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } - /// @src 0:317:454 + /// @src 0:317:454 "assembly {..." mstore(0, 100) sstore(0, 17385872270140913825666367956517731270094621555228275961425792378517567244498) - /// @src 0:62:463 + /// @src 0:62:463 "contract C {..." stop() } } diff --git a/test/cmdlineTests/keccak_optimization_low_runs/output b/test/cmdlineTests/keccak_optimization_low_runs/output index 203600cfa..25b678ee0 100644 --- a/test/cmdlineTests/keccak_optimization_low_runs/output +++ b/test/cmdlineTests/keccak_optimization_low_runs/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"keccak_optimization_low_runs/input.sol", 1:"#utility.yul" +/// @use-src 0:"keccak_optimization_low_runs/input.sol" object "C_7" { code { { - /// @src 0:62:285 + /// @src 0:62:285 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") @@ -18,16 +18,17 @@ object "C_7" { return(128, _1) } } + /// @use-src 0:"keccak_optimization_low_runs/input.sol" object "C_7_deployed" { code { { - /// @src 0:62:285 + /// @src 0:62:285 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } - /// @src 0:109:277 + /// @src 0:109:277 "assembly {..." mstore(0, 100) sstore(0, keccak256(0, 32)) - /// @src 0:62:285 + /// @src 0:62:285 "contract C {..." stop() } } diff --git a/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/args b/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/args new file mode 100644 index 000000000..fbab96f26 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/args @@ -0,0 +1 @@ +--no-optimize-yul --link --libraries input.sol:L=0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/err b/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/err new file mode 100644 index 000000000..7493f5926 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/err @@ -0,0 +1 @@ +Option --no-optimize-yul is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/stdin_allowed_paths/exit b/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/exit similarity index 100% rename from test/cmdlineTests/stdin_allowed_paths/exit rename to test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/exit diff --git a/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/input.bin b/test/cmdlineTests/linker_mode_invalid_option_no_optimize_yul/input.bin new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize/args b/test/cmdlineTests/linker_mode_invalid_option_optimize/args new file mode 100644 index 000000000..bddcf43f6 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize/args @@ -0,0 +1 @@ +--optimize --link --libraries input.sol:L=0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize/err b/test/cmdlineTests/linker_mode_invalid_option_optimize/err new file mode 100644 index 000000000..4b86fcfcd --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize/err @@ -0,0 +1 @@ +Option --optimize is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize/exit b/test/cmdlineTests/linker_mode_invalid_option_optimize/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize/input.bin b/test/cmdlineTests/linker_mode_invalid_option_optimize/input.bin new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/args b/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/args new file mode 100644 index 000000000..a6de8f8c4 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/args @@ -0,0 +1 @@ +--optimize-runs 1000 --link --libraries input.sol:L=0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/err b/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/err new file mode 100644 index 000000000..624b8f62e --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/err @@ -0,0 +1 @@ +Option --optimize-runs is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/exit b/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/input.bin b/test/cmdlineTests/linker_mode_invalid_option_optimize_runs/input.bin new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/args b/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/args new file mode 100644 index 000000000..50eac8159 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/args @@ -0,0 +1 @@ +--optimize-yul --link --libraries input.sol:L=0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/err b/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/err new file mode 100644 index 000000000..5efb6abf8 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/err @@ -0,0 +1 @@ +Option --optimize-yul is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/exit b/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/input.bin b/test/cmdlineTests/linker_mode_invalid_option_optimize_yul/input.bin new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/args b/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/args new file mode 100644 index 000000000..1dc92992b --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/args @@ -0,0 +1 @@ +--yul-optimizations a --link --libraries input.sol:L=0x1234567890123456789012345678901234567890 diff --git a/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/err b/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/err new file mode 100644 index 000000000..779736122 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/err @@ -0,0 +1 @@ +Option --yul-optimizations is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/exit b/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/input.bin b/test/cmdlineTests/linker_mode_invalid_option_yul_optimizations/input.bin new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/model_checker_targets_all_all_engines/err b/test/cmdlineTests/model_checker_targets_all_all_engines/err index 0f2e84b48..cc8a8ea33 100644 --- a/test/cmdlineTests/model_checker_targets_all_all_engines/err +++ b/test/cmdlineTests/model_checker_targets_all_all_engines/err @@ -1,13 +1,13 @@ Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> model_checker_targets_all_all_engines/input.sol:7:3: | 7 | --x; @@ -16,13 +16,13 @@ test.f(0, 0) Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> model_checker_targets_all_all_engines/input.sol:8:3: | 8 | x + type(uint).max; @@ -31,13 +31,13 @@ test.f(0, 2) Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_all_engines/input.sol:9:3: | 9 | 2 / x; @@ -46,13 +46,13 @@ test.f(0, 1) Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_all_engines/input.sol:11:3: | 11 | assert(x > 0); @@ -61,13 +61,13 @@ test.f(0, 1) Warning: CHC: Empty array "pop" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_all_engines/input.sol:12:3: | 12 | arr.pop(); @@ -76,13 +76,13 @@ test.f(0, 1) Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_all_engines/input.sol:13:3: | 13 | arr[x]; diff --git a/test/cmdlineTests/model_checker_targets_all_chc/err b/test/cmdlineTests/model_checker_targets_all_chc/err index 1484e7f2d..d0404ecfb 100644 --- a/test/cmdlineTests/model_checker_targets_all_chc/err +++ b/test/cmdlineTests/model_checker_targets_all_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> model_checker_targets_all_chc/input.sol:7:3: | 7 | --x; @@ -16,13 +16,13 @@ test.f(0, 0) Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> model_checker_targets_all_chc/input.sol:8:3: | 8 | x + type(uint).max; @@ -31,13 +31,13 @@ test.f(0, 2) Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_chc/input.sol:9:3: | 9 | 2 / x; @@ -46,13 +46,13 @@ test.f(0, 1) Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_chc/input.sol:11:3: | 11 | assert(x > 0); @@ -61,13 +61,13 @@ test.f(0, 1) Warning: CHC: Empty array "pop" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_chc/input.sol:12:3: | 12 | arr.pop(); @@ -76,13 +76,13 @@ test.f(0, 1) Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_all_chc/input.sol:13:3: | 13 | arr[x]; diff --git a/test/cmdlineTests/model_checker_targets_assert_chc/err b/test/cmdlineTests/model_checker_targets_assert_chc/err index a7b73b418..1bf48bfa9 100644 --- a/test/cmdlineTests/model_checker_targets_assert_chc/err +++ b/test/cmdlineTests/model_checker_targets_assert_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_assert_chc/input.sol:11:3: | 11 | assert(x > 0); diff --git a/test/cmdlineTests/model_checker_targets_default_all_engines/err b/test/cmdlineTests/model_checker_targets_default_all_engines/err index 14f3b95ff..c1fe2f5b3 100644 --- a/test/cmdlineTests/model_checker_targets_default_all_engines/err +++ b/test/cmdlineTests/model_checker_targets_default_all_engines/err @@ -1,13 +1,13 @@ Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_all_engines/input.sol:9:3: | 9 | 2 / x; @@ -16,13 +16,13 @@ test.f(0, 1) Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_all_engines/input.sol:11:3: | 11 | assert(x > 0); @@ -31,13 +31,13 @@ test.f(0, 1) Warning: CHC: Empty array "pop" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_all_engines/input.sol:12:3: | 12 | arr.pop(); @@ -46,13 +46,13 @@ test.f(0, 1) Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_all_engines/input.sol:13:3: | 13 | arr[x]; diff --git a/test/cmdlineTests/model_checker_targets_default_chc/err b/test/cmdlineTests/model_checker_targets_default_chc/err index 1c21226e1..bd2aa0b4d 100644 --- a/test/cmdlineTests/model_checker_targets_default_chc/err +++ b/test/cmdlineTests/model_checker_targets_default_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_chc/input.sol:9:3: | 9 | 2 / x; @@ -16,13 +16,13 @@ test.f(0, 1) Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_chc/input.sol:11:3: | 11 | assert(x > 0); @@ -31,13 +31,13 @@ test.f(0, 1) Warning: CHC: Empty array "pop" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_chc/input.sol:12:3: | 12 | arr.pop(); @@ -46,13 +46,13 @@ test.f(0, 1) Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_default_chc/input.sol:13:3: | 13 | arr[x]; diff --git a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err index 4318a501e..3156dd90b 100644 --- a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err +++ b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_div_by_zero_chc/input.sol:9:3: | 9 | 2 / x; diff --git a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err index 661ae5b79..b4426a80e 100644 --- a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err +++ b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_out_of_bounds_chc/input.sol:13:3: | 13 | arr[x]; diff --git a/test/cmdlineTests/model_checker_targets_overflow_chc/err b/test/cmdlineTests/model_checker_targets_overflow_chc/err index fca12d076..e7f98ff4d 100644 --- a/test/cmdlineTests/model_checker_targets_overflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_overflow_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> model_checker_targets_overflow_chc/input.sol:8:3: | 8 | x + type(uint).max; diff --git a/test/cmdlineTests/model_checker_targets_pop_empty_chc/err b/test/cmdlineTests/model_checker_targets_pop_empty_chc/err index 7ec616853..d3478a700 100644 --- a/test/cmdlineTests/model_checker_targets_pop_empty_chc/err +++ b/test/cmdlineTests/model_checker_targets_pop_empty_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Empty array "pop" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_pop_empty_chc/input.sol:12:3: | 12 | arr.pop(); diff --git a/test/cmdlineTests/model_checker_targets_underflow_chc/err b/test/cmdlineTests/model_checker_targets_underflow_chc/err index ac56c82b5..448b0e25c 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> model_checker_targets_underflow_chc/input.sol:7:3: | 7 | --x; diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err index 25680bc15..a0fbbe61a 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> model_checker_targets_underflow_overflow_assert_chc/input.sol:7:3: | 7 | --x; @@ -16,13 +16,13 @@ test.f(0, 0) Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> model_checker_targets_underflow_overflow_assert_chc/input.sol:8:3: | 8 | x + type(uint).max; @@ -31,13 +31,13 @@ test.f(0, 2) Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> model_checker_targets_underflow_overflow_assert_chc/input.sol:11:3: | 11 | assert(x > 0); diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err index 9bb4b52f1..0dcfb1c25 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err @@ -1,13 +1,13 @@ Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> model_checker_targets_underflow_overflow_chc/input.sol:7:3: | 7 | --x; @@ -16,13 +16,13 @@ test.f(0, 0) Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> model_checker_targets_underflow_overflow_chc/input.sol:8:3: | 8 | x + type(uint).max; diff --git a/test/cmdlineTests/name_simplifier/output b/test/cmdlineTests/name_simplifier/output index be3cb3780..73c9cc1aa 100644 --- a/test/cmdlineTests/name_simplifier/output +++ b/test/cmdlineTests/name_simplifier/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"name_simplifier/input.sol", 1:"#utility.yul" +/// @use-src 0:"name_simplifier/input.sol" object "C_59" { code { { - /// @src 0:346:625 + /// @src 0:346:625 "contract C {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_59_deployed") @@ -18,10 +18,11 @@ object "C_59" { return(128, _1) } } + /// @use-src 0:"name_simplifier/input.sol" object "C_59_deployed" { code { { - /// @src 0:346:625 + /// @src 0:346:625 "contract C {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -42,17 +43,16 @@ object "C_59" { let dst_1 := dst mstore(dst, _4) dst := add(dst, _2) + let srcEnd := add(add(offset, _5), 36) + if gt(srcEnd, calldatasize()) { revert(_1, _1) } let src := add(offset, 36) - if gt(add(add(offset, _5), 36), calldatasize()) { revert(_1, _1) } - let i := _1 - for { } lt(i, _4) { i := add(i, 1) } + for { } lt(src, srcEnd) { src := add(src, _2) } { if slt(sub(calldatasize(), src), _2) { revert(_1, _1) } - let value := allocate_memory_1236() + let value := allocate_memory_1174() mstore(value, calldataload(src)) mstore(dst, value) dst := add(dst, _2) - src := add(src, _2) } let ret, ret_1 := fun_sumArray(dst_1) let memPos := mload(64) @@ -61,6 +61,26 @@ object "C_59" { } revert(0, 0) } + function panic_error_0x41() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + function allocate_memory_1174() -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, 32) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + function allocate_memory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, and(add(size, 31), not(31))) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } function abi_encode_uint256_string(headStart, value0, value1) -> tail { mstore(headStart, value0) @@ -79,32 +99,24 @@ object "C_59" { } tail := add(add(headStart, and(add(length, 31), not(31))), 96) } - function allocate_memory_1236() -> memPtr + function panic_error_0x32() { - memPtr := mload(64) - let newFreePtr := add(memPtr, 32) - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x32) + revert(0, 0x24) } - function allocate_memory(size) -> memPtr - { - memPtr := mload(64) - let newFreePtr := add(memPtr, and(add(size, 31), not(31))) - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) - } - /// @src 0:381:623 + /// @ast-id 58 @src 0:381:623 "function sumArray(S[] memory _s) public returns (uint, string memory) {..." function fun_sumArray(var_s_mpos) -> var, var_mpos { - /// @src 0:346:625 + /// @src 0:346:625 "contract C {..." if iszero(mload(var_s_mpos)) { panic_error_0x32() } - sstore(/** @src 0:472:473 */ 0x00, /** @src 0:346:625 */ mload(/** @src 0:469:474 */ mload(/** @src 0:346:625 */ add(var_s_mpos, 32)))) + sstore(/** @src 0:472:473 "0" */ 0x00, /** @src 0:346:625 "contract C {..." */ mload(/** @src 0:469:474 "_s[0]" */ mload(/** @src 0:346:625 "contract C {..." */ add(var_s_mpos, 32)))) if iszero(lt(1, mload(var_s_mpos))) { panic_error_0x32() } - let _1 := mload(/** @src 0:489:494 */ mload(/** @src 0:346:625 */ add(var_s_mpos, 64))) + let _1 := mload(/** @src 0:489:494 "_s[1]" */ mload(/** @src 0:346:625 "contract C {..." */ add(var_s_mpos, 64))) sstore(0x02, _1) - /// @src 0:500:619 + /// @src 0:500:619 "return (t.y[0], \"longstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstring\")" var := _1 - /// @src 0:346:625 + /// @src 0:346:625 "contract C {..." let memPtr := mload(64) let newFreePtr := add(memPtr, 160) if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } @@ -114,22 +126,9 @@ object "C_59" { mstore(add(memPtr, 64), "ngstringlongstringlongstringlong") mstore(add(memPtr, 96), "stringlongstringlongstringlongst") mstore(add(memPtr, 128), "ring") - /// @src 0:500:619 + /// @src 0:500:619 "return (t.y[0], \"longstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstring\")" var_mpos := memPtr } - /// @src 0:346:625 - 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) - } } data ".metadata" hex"" } diff --git a/test/cmdlineTests/optimizer_array_sload/output b/test/cmdlineTests/optimizer_array_sload/output index 36dc23663..448b0badc 100644 --- a/test/cmdlineTests/optimizer_array_sload/output +++ b/test/cmdlineTests/optimizer_array_sload/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"optimizer_array_sload/input.sol", 1:"#utility.yul" +/// @use-src 0:"optimizer_array_sload/input.sol" object "Arraysum_34" { code { { - /// @src 0:80:429 + /// @src 0:80:429 "contract Arraysum {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("Arraysum_34_deployed") @@ -18,10 +18,11 @@ object "Arraysum_34" { return(128, _1) } } + /// @use-src 0:"optimizer_array_sload/input.sol" object "Arraysum_34_deployed" { code { { - /// @src 0:80:429 + /// @src 0:80:429 "contract Arraysum {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -31,27 +32,27 @@ object "Arraysum_34" { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } let var_sum := _1 - /// @src 0:368:378 - let var_i := /** @src 0:80:429 */ _1 + /// @src 0:368:378 "uint i = 0" + let var_i := /** @src 0:80:429 "contract Arraysum {..." */ _1 let _2 := sload(_1) - /// @src 0:364:423 + /// @src 0:364:423 "for(uint i = 0; i < values.length; i++)..." for { } - /** @src 0:380:397 */ lt(var_i, _2) - /// @src 0:368:378 + /** @src 0:380:397 "i < values.length" */ lt(var_i, _2) + /// @src 0:368:378 "uint i = 0" { - /// @src 0:80:429 + /// @src 0:80:429 "contract Arraysum {..." if eq(var_i, not(0)) { panic_error_0x11() } - /// @src 0:399:402 - var_i := /** @src 0:80:429 */ add(var_i, 1) + /// @src 0:399:402 "i++" + var_i := /** @src 0:80:429 "contract Arraysum {..." */ add(var_i, 1) } - /// @src 0:399:402 + /// @src 0:399:402 "i++" { - /// @src 0:80:429 + /// @src 0:80:429 "contract Arraysum {..." mstore(_1, _1) let _3 := sload(add(18569430475105882587588266137607568536673111973893317399460219858819262702947, var_i)) if gt(var_sum, not(_3)) { panic_error_0x11() } - /// @src 0:407:423 - var_sum := /** @src 0:80:429 */ add(var_sum, _3) + /// @src 0:407:423 "sum += values[i]" + var_sum := /** @src 0:80:429 "contract Arraysum {..." */ add(var_sum, _3) } let memPos := mload(64) return(memPos, sub(abi_encode_uint256(memPos, var_sum), memPos)) diff --git a/test/cmdlineTests/recovery_ast_constructor/output b/test/cmdlineTests/recovery_ast_constructor/output index 4a2f711c1..33c07b4c1 100644 --- a/test/cmdlineTests/recovery_ast_constructor/output +++ b/test/cmdlineTests/recovery_ast_constructor/output @@ -31,6 +31,7 @@ JSON AST (compact format): { "abstract": false, "baseContracts": [], + "canonicalName": "Error1", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/cmdlineTests/recovery_standard_json/output.json b/test/cmdlineTests/recovery_standard_json/output.json index 3868e93cb..2378a2084 100644 --- a/test/cmdlineTests/recovery_standard_json/output.json +++ b/test/cmdlineTests/recovery_standard_json/output.json @@ -10,4 +10,4 @@ 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","nameLocation":"68:7:0","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0","usedErrors":[]}],"src":"36:84:0"},"id":0}}} +","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":[],"canonicalName":"Errort6","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":3,"linearizedBaseContracts":[3],"name":"Errort6","nameLocation":"68:7:0","nodeType":"ContractDefinition","nodes":[],"scope":4,"src":"59:35:0","usedErrors":[]}],"src":"36:84:0"},"id":0}}} diff --git a/test/cmdlineTests/revert_strings/output b/test/cmdlineTests/revert_strings/output index 6fa650eb1..718cf0ba9 100644 --- a/test/cmdlineTests/revert_strings/output +++ b/test/cmdlineTests/revert_strings/output @@ -7,10 +7,10 @@ IR: *=====================================================*/ -/// @use-src 0:"revert_strings/input.sol", 1:"#utility.yul" +/// @use-src 0:"revert_strings/input.sol" object "C_15" { code { - /// @src 0:59:147 + /// @src 0:59:147 "contract C {..." mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,14 +25,6 @@ object "C_15" { memPtr := mload(64) } - /// @src 0:59:147 - function constructor_C_15() { - - /// @src 0:59:147 - - } - /// @src 0:59:147 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { let start := allocate_unbounded() @@ -52,10 +44,19 @@ object "C_15" { } + /// @src 0:59:147 "contract C {..." + function constructor_C_15() { + + /// @src 0:59:147 "contract C {..." + + } + /// @src 0:59:147 "contract C {..." + } + /// @use-src 0:"revert_strings/input.sol" object "C_15_deployed" { code { - /// @src 0:59:147 + /// @src 0:59:147 "contract C {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -80,233 +81,17 @@ object "C_15" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() - // uint256[][] - function abi_decode_available_length_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(offset, length, end) -> array { - array := allocate_memory(array_allocation_size_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(length)) - let dst := array + function shift_right_224_unsigned(value) -> newValue { + newValue := - mstore(array, length) - dst := add(array, 0x20) + shr(224, value) - let src := offset - if gt(add(src, mul(length, 0x20)), end) { - revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() - } - for { let i := 0 } lt(i, length) { i := add(i, 1) } - { - - let innerOffset := calldataload(src) - if gt(innerOffset, 0xffffffffffffffff) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } - let elementPos := add(offset, innerOffset) - - mstore(dst, abi_decode_t_array$_t_uint256_$dyn_memory_ptr(elementPos, end)) - dst := add(dst, 0x20) - src := add(src, 0x20) - } - } - - // uint256[] - function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { - array := allocate_memory(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_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() - } - 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_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(offset, end) -> array { - if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } - let length := calldataload(offset) - array := abi_decode_available_length_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(add(offset, 0x20), length, end) - } - - // uint256[] - function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset, end) -> array { - if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } - 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_enum$_E_$3(offset, end) -> value { - value := calldataload(offset) - validator_revert_t_enum$_E_$3(value) - } - - function abi_decode_t_uint256(offset, end) -> value { - value := calldataload(offset) - validator_revert_t_uint256(value) - } - - function abi_decode_tuple_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptrt_enum$_E_$3(headStart, dataEnd) -> value0, value1 { - if slt(sub(dataEnd, headStart), 64) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } - - { - - let offset := calldataload(add(headStart, 0)) - if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() } - - value0 := abi_decode_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(add(headStart, offset), dataEnd) - } - - { - - let offset := 32 - - value1 := abi_decode_t_enum$_E_$3(add(headStart, offset), dataEnd) - } - - } - - function abi_encode_tuple__to__fromStack(headStart ) -> tail { - tail := add(headStart, 0) - - } - - function allocate_memory(size) -> memPtr { - memPtr := allocate_unbounded() - finalize_allocation(memPtr, size) } function allocate_unbounded() -> memPtr { memPtr := mload(64) } - function array_allocation_size_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$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 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 finalize_allocation(memPtr, size) { - let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) - // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) - } - - /// @src 0:93:145 - function fun_f_14(var__7_mpos, var_e_10) { - - } - /// @src 0:59:147 - - function panic_error_0x41() { - mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) - mstore(4, 0x41) - revert(0, 0x24) - } - - function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() { - - let start := allocate_unbounded() - let pos := start - mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) - pos := add(pos, 4) - mstore(pos, 0x20) - pos := add(pos, 0x20) - mstore(pos, 43) - pos := add(pos, 0x20) - - mstore(add(pos, 0), "ABI decoding: invalid calldata a") - - mstore(add(pos, 32), "rray offset") - - revert(start, 132) - - } - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - - let start := allocate_unbounded() - let pos := start - mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) - pos := add(pos, 4) - mstore(pos, 0x20) - pos := add(pos, 0x20) - mstore(pos, 53) - pos := add(pos, 0x20) - - mstore(add(pos, 0), "Contract does not have fallback ") - - mstore(add(pos, 32), "nor receive functions") - - revert(start, 132) - - } - - function revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() { - - let start := allocate_unbounded() - let pos := start - mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) - pos := add(pos, 4) - mstore(pos, 0x20) - pos := add(pos, 0x20) - mstore(pos, 43) - pos := add(pos, 0x20) - - mstore(add(pos, 0), "ABI decoding: invalid calldata a") - - mstore(add(pos, 32), "rray stride") - - revert(start, 132) - - } - - function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { - - let start := allocate_unbounded() - let pos := start - mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) - pos := add(pos, 4) - mstore(pos, 0x20) - pos := add(pos, 0x20) - mstore(pos, 34) - pos := add(pos, 0x20) - - mstore(add(pos, 0), "ABI decoding: invalid tuple offs") - - mstore(add(pos, 32), "et") - - revert(start, 132) - - } - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { let start := allocate_unbounded() @@ -345,25 +130,240 @@ object "C_15" { } + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 34) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "ABI decoding: invalid tuple offs") + + mstore(add(pos, 32), "et") + + revert(start, 132) + + } + + function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 43) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "ABI decoding: invalid calldata a") + + mstore(add(pos, 32), "rray offset") + + revert(start, 132) + + } + function round_up_to_mul_of_32(value) -> result { result := and(add(value, 31), not(31)) } - function shift_right_224_unsigned(value) -> newValue { - newValue := + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } - shr(224, value) + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + function allocate_memory(size) -> memPtr { + memPtr := allocate_unbounded() + finalize_allocation(memPtr, size) + } + + function array_allocation_size_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$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 revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 43) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "ABI decoding: invalid calldata a") + + mstore(add(pos, 32), "rray stride") + + revert(start, 132) + + } + + 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 validator_revert_t_uint256(value) { + if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) } + } + + function abi_decode_t_uint256(offset, end) -> value { + value := calldataload(offset) + validator_revert_t_uint256(value) + } + + // uint256[] + function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { + array := allocate_memory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length)) + let dst := array + + mstore(array, length) + dst := add(array, 0x20) + + let srcEnd := add(offset, mul(length, 0x20)) + if gt(srcEnd, end) { + revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() + } + for { let src := offset } lt(src, srcEnd) { src := add(src, 0x20) } + { + + let elementPos := src + + mstore(dst, abi_decode_t_uint256(elementPos, end)) + dst := add(dst, 0x20) + } + } + + // uint256[] + function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } + let length := calldataload(offset) + array := abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(add(offset, 0x20), length, end) + } + + // uint256[][] + function abi_decode_available_length_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(offset, length, end) -> array { + array := allocate_memory(array_allocation_size_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(length)) + let dst := array + + mstore(array, length) + dst := add(array, 0x20) + + let srcEnd := add(offset, mul(length, 0x20)) + if gt(srcEnd, end) { + revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() + } + for { let src := offset } lt(src, srcEnd) { src := add(src, 0x20) } + { + + let innerOffset := calldataload(src) + if gt(innerOffset, 0xffffffffffffffff) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } + let elementPos := add(offset, innerOffset) + + mstore(dst, abi_decode_t_array$_t_uint256_$dyn_memory_ptr(elementPos, end)) + dst := add(dst, 0x20) + } + } + + // uint256[][] + function abi_decode_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } + let length := calldataload(offset) + array := abi_decode_available_length_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(add(offset, 0x20), length, end) } function validator_revert_t_enum$_E_$3(value) { if iszero(lt(value, 1)) { revert(0, 0) } } - function validator_revert_t_uint256(value) { - if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) } + function abi_decode_t_enum$_E_$3(offset, end) -> value { + value := calldataload(offset) + validator_revert_t_enum$_E_$3(value) } + function abi_decode_tuple_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptrt_enum$_E_$3(headStart, dataEnd) -> value0, value1 { + if slt(sub(dataEnd, headStart), 64) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + { + + let offset := calldataload(add(headStart, 0)) + if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() } + + value0 := abi_decode_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(add(headStart, offset), dataEnd) + } + + { + + let offset := 32 + + value1 := abi_decode_t_enum$_E_$3(add(headStart, offset), dataEnd) + } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 53) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "Contract does not have fallback ") + + mstore(add(pos, 32), "nor receive functions") + + revert(start, 132) + + } + + /// @ast-id 14 + /// @src 0:93:145 "function f(uint[][] memory, E e) public pure {..." + function fun_f_14(var__7_mpos, var_e_10) { + + } + /// @src 0:59:147 "contract C {..." + } data ".metadata" hex"" diff --git a/test/cmdlineTests/yul_source_locations_in_asm/input.json b/test/cmdlineTests/standard_debug_info_in_evm_asm_via_ir_location/input.json similarity index 100% rename from test/cmdlineTests/yul_source_locations_in_asm/input.json rename to test/cmdlineTests/standard_debug_info_in_evm_asm_via_ir_location/input.json diff --git a/test/cmdlineTests/yul_source_locations_in_asm/output.json b/test/cmdlineTests/standard_debug_info_in_evm_asm_via_ir_location/output.json similarity index 89% rename from test/cmdlineTests/yul_source_locations_in_asm/output.json rename to test/cmdlineTests/standard_debug_info_in_evm_asm_via_ir_location/output.json index 7c96a12db..3b25e108a 100644 --- a/test/cmdlineTests/yul_source_locations_in_asm/output.json +++ b/test/cmdlineTests/standard_debug_info_in_evm_asm_via_ir_location/output.json @@ -1,10 +1,10 @@ {"contracts":{"C":{"C":{"evm":{"assembly":" /* \"C\":79:428 contract C... */ mstore(0x40, 0xa0) - jumpi(tag_1, iszero(callvalue)) + jumpi(tag_2, iszero(callvalue)) 0x00 dup1 revert -tag_1: +tag_2: bytecodeSize codesize dup2 @@ -27,12 +27,12 @@ tag_1: lt or iszero - tag_2 + tag_3 jumpi mstore(0x00, shl(0xe0, 0x4e487b71)) mstore(0x04, 0x41) revert(0x00, 0x24) -tag_2: +tag_3: 0x40 mstore dup1 @@ -43,25 +43,26 @@ tag_2: dup2 slt iszero - tag_3 + tag_4 jumpi 0x00 dup1 revert -tag_3: +tag_4: pop pop - tag_4 + tag_5 mload(0xa0) /* \"C\":147:149 42 */ mstore(0x80, 0x2a) + /* \"C\":203:219 stateVar = _init */ 0x00 /* \"C\":79:428 contract C... */ sstore /* \"C\":175:223 constructor(int _init)... */ jump /* \"C\":79:428 contract C... */ -tag_4: +tag_5: mload(0x40) dataSize(sub_0) dup1 @@ -79,7 +80,7 @@ stop sub_0: assembly { /* \"C\":79:428 contract C... */ mstore(0x40, 0x80) - jumpi(tag_1, lt(calldatasize, 0x04)) + jumpi(tag_8, lt(calldatasize, 0x04)) 0x00 dup1 calldataload @@ -88,38 +89,38 @@ sub_0: assembly { 0x26121ff0 dup2 eq - tag_3 + tag_10 jumpi 0x793816ec dup2 eq - tag_4 + tag_11 jumpi 0x9942ec6f dup2 eq - tag_5 + tag_12 jumpi - jump(tag_2) - tag_3: - jumpi(tag_6, iszero(callvalue)) + jump(tag_9) + tag_10: + jumpi(tag_13, iszero(callvalue)) dup2 dup3 revert - tag_6: - tag_7 + tag_13: + tag_14 calldatasize - tag_8 + tag_1 jump\t// in - tag_7: + tag_14: /* \"C\":279:298 constVar + immutVar */ - tag_9 + tag_15 /* \"C\":290:298 immutVar */ immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") /* \"C\":279:298 constVar + immutVar */ - tag_10 + tag_4 jump\t// in - tag_9: + tag_15: /* \"C\":79:428 contract C... */ mload(0x40) dup2 @@ -128,17 +129,17 @@ sub_0: assembly { 0x20 dup2 return - tag_4: - jumpi(tag_13, iszero(callvalue)) + tag_11: + jumpi(tag_17, iszero(callvalue)) dup2 dup3 revert - tag_13: - tag_14 + tag_17: + tag_18 calldatasize - tag_8 + tag_1 jump\t// in - tag_14: + tag_18: dup2 sload mload(0x40) @@ -148,65 +149,50 @@ sub_0: assembly { 0x20 dup2 return - tag_5: - jumpi(tag_16, iszero(callvalue)) + tag_12: + jumpi(tag_20, iszero(callvalue)) dup2 dup3 revert - tag_16: - tag_17 + tag_20: + tag_21 calldatasize - tag_8 + tag_1 jump\t// in - tag_17: + tag_21: /* \"C\":375:378 int */ - tag_9 - tag_19 + tag_15 + tag_6 jump\t// in /* \"C\":79:428 contract C... */ - tag_2: + tag_9: pop pop - tag_1: + tag_8: 0x00 dup1 revert - tag_8: + tag_1: 0x00 not(0x03) dup3 add slt iszero - tag_23 - jumpi - 0x00 - dup1 - revert - tag_23: - pop - jump\t// out - tag_24: - 0x00 - 0x20 - dup3 - dup5 - sub - slt - iszero tag_26 jumpi 0x00 dup1 revert tag_26: - pop - mload - swap2 - swap1 pop jump\t// out - tag_10: + /* \"C\":117:119 41 */ + tag_3: + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) + tag_4: 0x00 sub(shl(0xff, 0x01), 0x2a) dup3 @@ -214,20 +200,18 @@ sub_0: assembly { 0x01 and iszero - tag_30 - jumpi - tag_30 tag_31 + jumpi + tag_31 + tag_3 jump\t// in - tag_30: + tag_31: pop - /* \"C\":117:119 41 */ 0x29 - /* \"C\":79:428 contract C... */ add swap1 jump\t// out - tag_32: + tag_5: 0x00 dup1 dup3 @@ -242,12 +226,12 @@ sub_0: assembly { sgt and iszero - tag_35 + tag_34 jumpi - tag_35 - tag_31 + tag_34 + tag_3 jump\t// in - tag_35: + tag_34: shl(0xff, 0x01) dup4 swap1 @@ -257,19 +241,19 @@ sub_0: assembly { dup2 and iszero - tag_37 + tag_36 jumpi - tag_37 - tag_31 + tag_36 + tag_3 jump\t// in - tag_37: + tag_36: pop pop add swap1 jump\t// out /* \"C\":304:341 modifier m()... */ - tag_19: + tag_6: 0x00 /* \"C\":79:428 contract C... */ dup1 @@ -282,13 +266,15 @@ sub_0: assembly { dup2 eq iszero - tag_40 + tag_39 jumpi - tag_40 - tag_31 + tag_39 + tag_3 jump\t// in - tag_40: + tag_39: + /* \"C\":117:119 41 */ 0x01 + /* \"C\":79:428 contract C... */ add dup1 dup3 @@ -297,14 +283,14 @@ sub_0: assembly { address /* \"C\":403:411 this.f() */ extcodesize - tag_41 + tag_40 jumpi /* \"C\":79:428 contract C... */ dup2 dup3 revert /* \"C\":403:411 this.f() */ - tag_41: + tag_40: /* \"C\":79:428 contract C... */ mload(0x40) shl(0xe4, 0x026121ff) @@ -324,7 +310,7 @@ sub_0: assembly { gas staticcall dup1 - tag_42 + tag_41 jumpi /* \"C\":79:428 contract C... */ mload(0x40) @@ -336,13 +322,13 @@ sub_0: assembly { dup2 revert /* \"C\":403:411 this.f() */ - tag_42: + tag_41: /* \"C\":79:428 contract C... */ dup4 /* \"C\":403:411 this.f() */ dup2 iszero - tag_43 + tag_42 jumpi returndatasize /* \"C\":79:428 contract C... */ @@ -350,7 +336,6 @@ sub_0: assembly { add not(0x1f) and - /* \"C\":117:119 41 */ dup4 add 0xffffffffffffffff @@ -361,10 +346,9 @@ sub_0: assembly { lt or iszero - tag_44 + tag_43 jumpi shl(0xe0, 0x4e487b71) - /* \"C\":79:428 contract C... */ dup7 mstore 0x41 @@ -375,31 +359,28 @@ sub_0: assembly { 0x24 dup7 revert - /* \"C\":117:119 41 */ - tag_44: - /* \"C\":79:428 contract C... */ + tag_43: 0x40 - /* \"C\":117:119 41 */ mstore /* \"C\":403:411 this.f() */ - tag_45 + tag_44 returndatasize dup5 add dup5 - tag_24 + tag_7 jump\t// in - tag_45: + tag_44: swap1 pop - tag_43: + tag_42: /* \"C\":392:411 stateVar + this.f() */ - tag_46 + tag_45 dup2 dup6 - tag_32 + tag_5 jump\t// in - tag_46: + tag_45: swap5 pop pop @@ -407,14 +388,14 @@ sub_0: assembly { pop pop /* \"C\":392:422 stateVar + this.f() + immutVar */ - tag_47 + tag_46 /* \"C\":414:422 immutVar */ immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") /* \"C\":392:422 stateVar + this.f() + immutVar */ dup3 - tag_32 + tag_5 jump\t// in - tag_47: + tag_46: /* \"C\":336:337 _ */ swap2 pop @@ -423,20 +404,36 @@ sub_0: assembly { swap1 jump\t// out /* \"C\":79:428 contract C... */ - tag_31: - mstore(0x00, shl(0xe0, 0x4e487b71)) - mstore(0x04, 0x11) - revert(0x00, 0x24) + tag_7: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_48 + jumpi + 0x00 + dup1 + revert + tag_48: + pop + mload + swap2 + swap1 + pop + jump\t// out auxdata: } "}}},"D":{"D":{"evm":{"assembly":" /* \"D\":91:166 contract D is C(3)... */ mstore(0x40, 0xa0) - jumpi(tag_1, iszero(callvalue)) + jumpi(tag_2, iszero(callvalue)) 0x00 dup1 revert -tag_1: +tag_2: bytecodeSize codesize dup2 @@ -459,12 +456,12 @@ tag_1: lt or iszero - tag_2 + tag_3 jumpi mstore(0x00, shl(0xe0, 0x4e487b71)) mstore(0x04, 0x41) revert(0x00, 0x24) -tag_2: +tag_3: 0x40 mstore dup1 @@ -475,19 +472,19 @@ tag_2: dup2 slt iszero - tag_3 + tag_4 jumpi 0x00 dup1 revert -tag_3: - pop - pop - tag_4 - mload(0xa0) - tag_5 - jump\t// in tag_4: + pop + pop + tag_5 + mload(0xa0) + tag_1 + jump\t// in +tag_5: mload(0x40) dataSize(sub_0) dup1 @@ -501,11 +498,12 @@ tag_4: dup3 return /* \"D\":113:164 constructor(int _init2)... */ -tag_5: +tag_1: /* \"C\":147:149 42 */ mstore(0x80, 0x2a) /* \"D\":107:108 3 */ 0x03 + /* \"C\":203:219 stateVar = _init */ 0x00 /* \"D\":91:166 contract D is C(3)... */ sstore @@ -517,15 +515,25 @@ tag_5: iszero tag_8 jumpi - mstore(0x00, shl(0xe0, 0x4e487b71)) + shl(0xe0, 0x4e487b71) + /* \"C\":203:219 stateVar = _init */ + 0x00 + /* \"D\":91:166 contract D is C(3)... */ + mstore mstore(0x04, 0x11) - revert(0x00, 0x24) + 0x24 + /* \"C\":203:219 stateVar = _init */ + 0x00 + /* \"D\":91:166 contract D is C(3)... */ + revert tag_8: /* \"D\":107:108 3 */ 0x03 /* \"D\":91:166 contract D is C(3)... */ add + /* \"C\":203:219 stateVar = _init */ 0x00 + /* \"D\":91:166 contract D is C(3)... */ sstore /* \"D\":113:164 constructor(int _init2)... */ jump\t// out @@ -534,7 +542,7 @@ stop sub_0: assembly { /* \"D\":91:166 contract D is C(3)... */ mstore(0x40, 0x80) - jumpi(tag_1, lt(calldatasize, 0x04)) + jumpi(tag_8, lt(calldatasize, 0x04)) 0x00 dup1 calldataload @@ -543,38 +551,38 @@ sub_0: assembly { 0x26121ff0 dup2 eq - tag_3 + tag_10 jumpi 0x793816ec dup2 eq - tag_4 + tag_11 jumpi 0x9942ec6f dup2 eq - tag_5 + tag_12 jumpi - jump(tag_2) - tag_3: - jumpi(tag_6, iszero(callvalue)) + jump(tag_9) + tag_10: + jumpi(tag_13, iszero(callvalue)) dup2 dup3 revert - tag_6: - tag_7 + tag_13: + tag_14 calldatasize - tag_8 + tag_1 jump\t// in - tag_7: + tag_14: /* \"C\":279:298 constVar + immutVar */ - tag_9 + tag_15 /* \"C\":290:298 immutVar */ immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") /* \"C\":279:298 constVar + immutVar */ - tag_10 + tag_4 jump\t// in - tag_9: + tag_15: /* \"D\":91:166 contract D is C(3)... */ mload(0x40) dup2 @@ -583,17 +591,17 @@ sub_0: assembly { 0x20 dup2 return - tag_4: - jumpi(tag_13, iszero(callvalue)) + tag_11: + jumpi(tag_17, iszero(callvalue)) dup2 dup3 revert - tag_13: - tag_14 + tag_17: + tag_18 calldatasize - tag_8 + tag_1 jump\t// in - tag_14: + tag_18: dup2 sload mload(0x40) @@ -603,65 +611,50 @@ sub_0: assembly { 0x20 dup2 return - tag_5: - jumpi(tag_16, iszero(callvalue)) + tag_12: + jumpi(tag_20, iszero(callvalue)) dup2 dup3 revert - tag_16: - tag_17 + tag_20: + tag_21 calldatasize - tag_8 + tag_1 jump\t// in - tag_17: + tag_21: /* \"C\":375:378 int */ - tag_9 - tag_19 + tag_15 + tag_6 jump\t// in /* \"D\":91:166 contract D is C(3)... */ - tag_2: + tag_9: pop pop - tag_1: + tag_8: 0x00 dup1 revert - tag_8: + tag_1: 0x00 not(0x03) dup3 add slt iszero - tag_23 - jumpi - 0x00 - dup1 - revert - tag_23: - pop - jump\t// out - tag_24: - 0x00 - 0x20 - dup3 - dup5 - sub - slt - iszero tag_26 jumpi 0x00 dup1 revert tag_26: - pop - mload - swap2 - swap1 pop jump\t// out - tag_10: + /* \"C\":117:119 41 */ + tag_3: + mstore(0x00, shl(0xe0, 0x4e487b71)) + mstore(0x04, 0x11) + revert(0x00, 0x24) + tag_4: 0x00 sub(shl(0xff, 0x01), 0x2a) dup3 @@ -669,20 +662,18 @@ sub_0: assembly { 0x01 and iszero - tag_30 - jumpi - tag_30 tag_31 + jumpi + tag_31 + tag_3 jump\t// in - tag_30: + tag_31: pop - /* \"C\":117:119 41 */ 0x29 - /* \"D\":91:166 contract D is C(3)... */ add swap1 jump\t// out - tag_32: + tag_5: 0x00 dup1 dup3 @@ -697,12 +688,12 @@ sub_0: assembly { sgt and iszero - tag_35 + tag_34 jumpi - tag_35 - tag_31 + tag_34 + tag_3 jump\t// in - tag_35: + tag_34: shl(0xff, 0x01) dup4 swap1 @@ -712,19 +703,19 @@ sub_0: assembly { dup2 and iszero - tag_37 + tag_36 jumpi - tag_37 - tag_31 + tag_36 + tag_3 jump\t// in - tag_37: + tag_36: pop pop add swap1 jump\t// out /* \"C\":304:341 modifier m()... */ - tag_19: + tag_6: 0x00 /* \"D\":91:166 contract D is C(3)... */ dup1 @@ -737,13 +728,15 @@ sub_0: assembly { dup2 eq iszero - tag_40 + tag_39 jumpi - tag_40 - tag_31 + tag_39 + tag_3 jump\t// in - tag_40: + tag_39: + /* \"C\":117:119 41 */ 0x01 + /* \"D\":91:166 contract D is C(3)... */ add dup1 dup3 @@ -752,14 +745,14 @@ sub_0: assembly { address /* \"C\":403:411 this.f() */ extcodesize - tag_41 + tag_40 jumpi /* \"D\":91:166 contract D is C(3)... */ dup2 dup3 revert /* \"C\":403:411 this.f() */ - tag_41: + tag_40: /* \"D\":91:166 contract D is C(3)... */ mload(0x40) shl(0xe4, 0x026121ff) @@ -779,7 +772,7 @@ sub_0: assembly { gas staticcall dup1 - tag_42 + tag_41 jumpi /* \"D\":91:166 contract D is C(3)... */ mload(0x40) @@ -791,13 +784,13 @@ sub_0: assembly { dup2 revert /* \"C\":403:411 this.f() */ - tag_42: + tag_41: /* \"D\":91:166 contract D is C(3)... */ dup4 /* \"C\":403:411 this.f() */ dup2 iszero - tag_43 + tag_42 jumpi returndatasize /* \"D\":91:166 contract D is C(3)... */ @@ -805,7 +798,6 @@ sub_0: assembly { add not(0x1f) and - /* \"C\":117:119 41 */ dup4 add 0xffffffffffffffff @@ -816,10 +808,9 @@ sub_0: assembly { lt or iszero - tag_44 + tag_43 jumpi shl(0xe0, 0x4e487b71) - /* \"D\":91:166 contract D is C(3)... */ dup7 mstore 0x41 @@ -830,31 +821,28 @@ sub_0: assembly { 0x24 dup7 revert - /* \"C\":117:119 41 */ - tag_44: - /* \"D\":91:166 contract D is C(3)... */ + tag_43: 0x40 - /* \"C\":117:119 41 */ mstore /* \"C\":403:411 this.f() */ - tag_45 + tag_44 returndatasize dup5 add dup5 - tag_24 + tag_7 jump\t// in - tag_45: + tag_44: swap1 pop - tag_43: + tag_42: /* \"C\":392:411 stateVar + this.f() */ - tag_46 + tag_45 dup2 dup6 - tag_32 + tag_5 jump\t// in - tag_46: + tag_45: swap5 pop pop @@ -862,14 +850,14 @@ sub_0: assembly { pop pop /* \"C\":392:422 stateVar + this.f() + immutVar */ - tag_47 + tag_46 /* \"C\":414:422 immutVar */ immutable(\"0xe4b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10\") /* \"C\":392:422 stateVar + this.f() + immutVar */ dup3 - tag_32 + tag_5 jump\t// in - tag_47: + tag_46: /* \"C\":336:337 _ */ swap2 pop @@ -878,10 +866,26 @@ sub_0: assembly { swap1 jump\t// out /* \"D\":91:166 contract D is C(3)... */ - tag_31: - mstore(0x00, shl(0xe0, 0x4e487b71)) - mstore(0x04, 0x11) - revert(0x00, 0x24) + tag_7: + 0x00 + 0x20 + dup3 + dup5 + sub + slt + iszero + tag_48 + jumpi + 0x00 + dup1 + revert + tag_48: + pop + mload + swap2 + swap1 + pop + jump\t// out auxdata: } diff --git a/test/cmdlineTests/standard_debug_info_in_yul_location/input.json b/test/cmdlineTests/standard_debug_info_in_yul_location/input.json new file mode 100644 index 000000000..8ba84239a --- /dev/null +++ b/test/cmdlineTests/standard_debug_info_in_yul_location/input.json @@ -0,0 +1,22 @@ +{ + "language": "Solidity", + "sources": + { + "C": + { + "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\n\ncontract C\n{\n int public constant constVar = 41;\n int immutable immutVar = 42;\n int public stateVar;\n\n constructor(int _init)\n {\n stateVar = _init;\n }\n\n function f() external pure returns (int)\n {\n return constVar + immutVar;\n }\n modifier m()\n {\n stateVar++;\n _;\n }\n function f2() m public returns (int)\n {\n return stateVar + this.f() + immutVar;\n }\n}\n" + }, + "D": + { + "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\nimport \"C\";\n\ncontract D is C(3)\n{\n constructor(int _init2)\n {\n stateVar += _init2;\n }\n}\n" + } + }, + "settings": + { + "outputSelection": + { + "*": { "*": ["ir", "irOptimized"] } + }, + "optimizer": { "enabled": true } + } +} diff --git a/test/cmdlineTests/yul_source_locations/output.json b/test/cmdlineTests/standard_debug_info_in_yul_location/output.json similarity index 61% rename from test/cmdlineTests/yul_source_locations/output.json rename to test/cmdlineTests/standard_debug_info_in_yul_location/output.json index 100249924..0ba3b09a3 100644 --- a/test/cmdlineTests/yul_source_locations/output.json +++ b/test/cmdlineTests/standard_debug_info_in_yul_location/output.json @@ -6,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"C\", 1:\"D\", 2:\"#utility.yul\" +/// @use-src 0:\"C\" object \"C_54\" { code { - /// @src 0:79:428 + /// @src 0:79:435 \"contract C...\" mstore(64, 160) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -23,6 +23,52 @@ object \"C_54\" { return(_2, datasize(\"C_54_deployed\")) + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function allocate_memory(size) -> memPtr { + memPtr := allocate_unbounded() + finalize_allocation(memPtr, size) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function cleanup_t_int256(value) -> cleaned { + cleaned := value + } + + function validator_revert_t_int256(value) { + if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } + } + function abi_decode_t_int256_fromMemory(offset, end) -> value { value := mload(offset) validator_revert_t_int256(value) @@ -40,51 +86,6 @@ object \"C_54\" { } - function allocate_memory(size) -> memPtr { - memPtr := allocate_unbounded() - finalize_allocation(memPtr, size) - } - - function allocate_unbounded() -> memPtr { - memPtr := mload(64) - } - - function cleanup_t_int256(value) -> cleaned { - cleaned := value - } - - function cleanup_t_rational_42_by_1(value) -> cleaned { - cleaned := value - } - - /// @src 0:175:223 - function constructor_C_54(var__init_12) { - - /// @src 0:175:223 - - /// @src 0:147:149 - let expr_7 := 0x2a - let _3 := convert_t_rational_42_by_1_to_t_int256(expr_7) - mstore(128, _3) - - /// @src 0:214:219 - let _4 := var__init_12 - let expr_16 := _4 - /// @src 0:203:219 - update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) - let expr_17 := expr_16 - - } - /// @src 0:79:428 - - function convert_t_int256_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_int256(value))) - } - - function convert_t_rational_42_by_1_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_rational_42_by_1(value))) - } - function copy_arguments_for_constructor_20_object_C_54() -> ret_param_0 { let programSize := datasize(\"C_54\") let argSize := sub(codesize(), programSize) @@ -95,41 +96,16 @@ object \"C_54\" { ret_param_0 := abi_decode_tuple_t_int256_fromMemory(memoryDataOffset, add(memoryDataOffset, argSize)) } - function finalize_allocation(memPtr, size) { - let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) - // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) + function cleanup_t_rational_42_by_1(value) -> cleaned { + cleaned := value } function identity(value) -> ret { ret := value } - function panic_error_0x41() { - mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) - mstore(4, 0x41) - revert(0, 0x24) - } - - function prepare_store_t_int256(value) -> ret { - ret := value - } - - function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function round_up_to_mul_of_32(value) -> result { - result := and(add(value, 31), not(31)) + function convert_t_rational_42_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_rational_42_by_1(value))) } function shift_left_0(value) -> newValue { @@ -146,19 +122,45 @@ object \"C_54\" { result := or(value, and(toInsert, mask)) } + function convert_t_int256_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_int256(value))) + } + + function prepare_store_t_int256(value) -> ret { + ret := value + } + function update_storage_value_offset_0t_int256_to_t_int256(slot, value_0) { let convertedValue_0 := convert_t_int256_to_t_int256(value_0) sstore(slot, update_byte_slice_32_shift_0(sload(slot), prepare_store_t_int256(convertedValue_0))) } - function validator_revert_t_int256(value) { - if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } + /// @ast-id 20 + /// @src 0:182:230 \"constructor(int _init)...\" + function constructor_C_54(var__init_12) { + + /// @src 0:182:230 \"constructor(int _init)...\" + + /// @src 0:154:156 \"42\" + let expr_7 := 0x2a + let _3 := convert_t_rational_42_by_1_to_t_int256(expr_7) + mstore(128, _3) + + /// @src 0:221:226 \"_init\" + let _4 := var__init_12 + let expr_16 := _4 + /// @src 0:210:226 \"stateVar = _init\" + update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) + let expr_17 := expr_16 + } + /// @src 0:79:435 \"contract C...\" } + /// @use-src 0:\"C\" object \"C_54_deployed\" { code { - /// @src 0:79:428 + /// @src 0:79:435 \"contract C...\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -202,14 +204,40 @@ object \"C_54\" { return(memPos, sub(memEnd, memPos)) } + case 0xa00b982b + { + // constVar() + + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := getter_fun_constVar_5() + let memPos := allocate_unbounded() + let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + default {} } if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() - function abi_decode_t_int256_fromMemory(offset, end) -> value { - value := mload(offset) - validator_revert_t_int256(value) + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) } function abi_decode_tuple_(headStart, dataEnd) { @@ -217,27 +245,14 @@ object \"C_54\" { } - function abi_decode_tuple_t_int256_fromMemory(headStart, dataEnd) -> value0 { - if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } - - { - - let offset := 0 - - value0 := abi_decode_t_int256_fromMemory(add(headStart, offset), dataEnd) - } - + function cleanup_t_int256(value) -> cleaned { + cleaned := value } function abi_encode_t_int256_to_t_int256_fromStack(value, pos) { mstore(pos, cleanup_t_int256(value)) } - function abi_encode_tuple__to__fromStack(headStart ) -> tail { - tail := add(headStart, 0) - - } - function abi_encode_tuple_t_int256__to_t_int256__fromStack(headStart , value0) -> tail { tail := add(headStart, 32) @@ -245,8 +260,78 @@ object \"C_54\" { } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) + function shift_right_unsigned_dynamic(bits, value) -> newValue { + newValue := + + shr(bits, value) + + } + + function cleanup_from_storage_t_int256(value) -> cleaned { + cleaned := value + } + + function extract_from_storage_value_dynamict_int256(slot_value, offset) -> value { + value := cleanup_from_storage_t_int256(shift_right_unsigned_dynamic(mul(offset, 8), slot_value)) + } + + function read_from_storage_split_dynamic_t_int256(slot, offset) -> value { + value := extract_from_storage_value_dynamict_int256(sload(slot), offset) + + } + + /// @ast-id 10 + /// @src 0:159:178 \"int public stateVar\" + function getter_fun_stateVar_10() -> ret { + + let slot := 0 + let offset := 0 + + ret := read_from_storage_split_dynamic_t_int256(slot, offset) + + } + /// @src 0:79:435 \"contract C...\" + + function cleanup_t_rational_41_by_1(value) -> cleaned { + cleaned := value + } + + function identity(value) -> ret { + ret := value + } + + function convert_t_rational_41_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_rational_41_by_1(value))) + } + + /// @src 0:93:126 \"int public constant constVar = 41\" + function constant_constVar_5() -> ret { + /// @src 0:124:126 \"41\" + let expr_4 := 0x29 + let _1 := convert_t_rational_41_by_1_to_t_int256(expr_4) + + ret := _1 + } + + /// @ast-id 5 + /// @src 0:93:126 \"int public constant constVar = 41\" + function getter_fun_constVar_5() -> ret_0 { + ret_0 := constant_constVar_5() + } + /// @src 0:79:435 \"contract C...\" + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function zero_value_for_split_t_int256() -> ret { + ret := 0 + } + + function panic_error_0x11() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x11) + revert(0, 0x24) } function checked_add_t_int256(x, y) -> sum { @@ -261,57 +346,121 @@ object \"C_54\" { sum := add(x, y) } - function cleanup_from_storage_t_int256(value) -> cleaned { - cleaned := value + /// @ast-id 30 + /// @src 0:233:309 \"function f() external pure returns (int)...\" + function fun_f_30() -> var__23 { + /// @src 0:269:272 \"int\" + let zero_t_int256_2 := zero_value_for_split_t_int256() + var__23 := zero_t_int256_2 + + /// @src 0:286:294 \"constVar\" + let expr_25 := constant_constVar_5() + /// @src 0:297:305 \"immutVar\" + let _3 := loadimmutable(\"8\") + let expr_26 := _3 + /// @src 0:286:305 \"constVar + immutVar\" + let expr_27 := checked_add_t_int256(expr_25, expr_26) + + /// @src 0:279:305 \"return constVar + immutVar\" + var__23 := expr_27 + leave + + } + /// @src 0:79:435 \"contract C...\" + + function shift_right_0_unsigned(value) -> newValue { + newValue := + + shr(0, value) + } - function cleanup_t_int256(value) -> cleaned { - cleaned := value + function extract_from_storage_value_offset_0t_int256(slot_value) -> value { + value := cleanup_from_storage_t_int256(shift_right_0_unsigned(slot_value)) } - function cleanup_t_rational_41_by_1(value) -> cleaned { - cleaned := value + function read_from_storage_split_offset_0_t_int256(slot) -> value { + value := extract_from_storage_value_offset_0t_int256(sload(slot)) + } - function cleanup_t_uint160(value) -> cleaned { - cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) + function increment_t_int256(value) -> ret { + value := cleanup_t_int256(value) + if eq(value, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) { panic_error_0x11() } + ret := add(value, 1) } - /// @src 0:93:119 - function constant_constVar_5() -> ret { - /// @src 0:117:119 - let expr_4 := 0x29 - let _2 := convert_t_rational_41_by_1_to_t_int256(expr_4) + function shift_left_0(value) -> newValue { + newValue := + + shl(0, value) - ret := _2 } - function convert_t_contract$_C_$54_to_t_address(value) -> converted { - converted := convert_t_uint160_to_t_address(value) + function update_byte_slice_32_shift_0(value, toInsert) -> result { + let mask := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + toInsert := shift_left_0(toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) } function convert_t_int256_to_t_int256(value) -> converted { converted := cleanup_t_int256(identity(cleanup_t_int256(value))) } - function convert_t_rational_41_by_1_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_rational_41_by_1(value))) + function prepare_store_t_int256(value) -> ret { + ret := value } - function convert_t_uint160_to_t_address(value) -> converted { - converted := convert_t_uint160_to_t_uint160(value) + function update_storage_value_offset_0t_int256_to_t_int256(slot, value_0) { + let convertedValue_0 := convert_t_int256_to_t_int256(value_0) + sstore(slot, update_byte_slice_32_shift_0(sload(slot), prepare_store_t_int256(convertedValue_0))) + } + + /// @ast-id 37 + /// @src 0:311:348 \"modifier m()...\" + function modifier_m_40(var__42) -> _5 { + _5 := var__42 + + /// @src 0:329:339 \"stateVar++\" + let _7 := read_from_storage_split_offset_0_t_int256(0x00) + let _6 := increment_t_int256(_7) + update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) + let expr_33 := _7 + /// @src 0:343:344 \"_\" + _5 := fun_f2_53_inner(var__42) + + } + /// @src 0:79:435 \"contract C...\" + + function cleanup_t_uint160(value) -> cleaned { + cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) } function convert_t_uint160_to_t_uint160(value) -> converted { converted := cleanup_t_uint160(identity(cleanup_t_uint160(value))) } - function extract_from_storage_value_dynamict_int256(slot_value, offset) -> value { - value := cleanup_from_storage_t_int256(shift_right_unsigned_dynamic(mul(offset, 8), slot_value)) + function convert_t_uint160_to_t_address(value) -> converted { + converted := convert_t_uint160_to_t_uint160(value) } - function extract_from_storage_value_offset_0t_int256(slot_value) -> value { - value := cleanup_from_storage_t_int256(shift_right_0_unsigned(slot_value)) + function convert_t_contract$_C_$54_to_t_address(value) -> converted { + converted := convert_t_uint160_to_t_address(value) + } + + function revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() { + revert(0, 0) + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) } function finalize_allocation(memPtr, size) { @@ -321,29 +470,62 @@ object \"C_54\" { mstore(64, newFreePtr) } - /// @src 0:343:426 - function fun_f2_53() -> var__42 { - /// @src 0:375:378 - let zero_t_int256_4 := zero_value_for_split_t_int256() - var__42 := zero_t_int256_4 + function shift_left_224(value) -> newValue { + newValue := + + shl(224, value) - var__42 := modifier_m_40(var__42) } - /// @src 0:79:428 - /// @src 0:343:426 + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function validator_revert_t_int256(value) { + if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } + } + + function abi_decode_t_int256_fromMemory(offset, end) -> value { + value := mload(offset) + validator_revert_t_int256(value) + } + + function abi_decode_tuple_t_int256_fromMemory(headStart, dataEnd) -> value0 { + if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + { + + let offset := 0 + + value0 := abi_decode_t_int256_fromMemory(add(headStart, offset), dataEnd) + } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function revert_forward_1() { + let pos := allocate_unbounded() + returndatacopy(pos, 0, returndatasize()) + revert(pos, returndatasize()) + } + + /// @src 0:350:433 \"function f2() m public returns (int)...\" function fun_f2_53_inner(_8) -> var__42 { var__42 := _8 - /// @src 0:392:400 + /// @src 0:399:407 \"stateVar\" let _9 := read_from_storage_split_offset_0_t_int256(0x00) let expr_44 := _9 - /// @src 0:403:407 + /// @src 0:410:414 \"this\" let expr_45_address := address() - /// @src 0:403:409 + /// @src 0:410:416 \"this.f\" let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_functionSelector := 0x26121ff0 - /// @src 0:403:411 + /// @src 0:410:418 \"this.f()\" if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() } // storage for arguments and returned data @@ -364,189 +546,32 @@ object \"C_54\" { // decode return parameters from external try-call into retVars expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize())) } - /// @src 0:392:411 + /// @src 0:399:418 \"stateVar + this.f()\" let expr_48 := checked_add_t_int256(expr_44, expr_47) - /// @src 0:414:422 + /// @src 0:421:429 \"immutVar\" let _13 := loadimmutable(\"8\") let expr_49 := _13 - /// @src 0:392:422 + /// @src 0:399:429 \"stateVar + this.f() + immutVar\" let expr_50 := checked_add_t_int256(expr_48, expr_49) - /// @src 0:385:422 + /// @src 0:392:429 \"return stateVar + this.f() + immutVar\" var__42 := expr_50 leave } - /// @src 0:79:428 + /// @src 0:79:435 \"contract C...\" - /// @src 0:226:302 - function fun_f_30() -> var__23 { - /// @src 0:262:265 - let zero_t_int256_1 := zero_value_for_split_t_int256() - var__23 := zero_t_int256_1 - - /// @src 0:279:287 - let expr_25 := constant_constVar_5() - /// @src 0:290:298 - let _3 := loadimmutable(\"8\") - let expr_26 := _3 - /// @src 0:279:298 - let expr_27 := checked_add_t_int256(expr_25, expr_26) - - /// @src 0:272:298 - var__23 := expr_27 - leave + /// @ast-id 53 + /// @src 0:350:433 \"function f2() m public returns (int)...\" + function fun_f2_53() -> var__42 { + /// @src 0:382:385 \"int\" + let zero_t_int256_4 := zero_value_for_split_t_int256() + var__42 := zero_t_int256_4 + var__42 := modifier_m_40(var__42) } - /// @src 0:79:428 - - /// @src 0:152:171 - function getter_fun_stateVar_10() -> ret { - - let slot := 0 - let offset := 0 - - ret := read_from_storage_split_dynamic_t_int256(slot, offset) - - } - /// @src 0:79:428 - - function identity(value) -> ret { - ret := value - } - - function increment_t_int256(value) -> ret { - value := cleanup_t_int256(value) - if eq(value, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) { panic_error_0x11() } - ret := add(value, 1) - } - - /// @src 0:304:341 - function modifier_m_40(var__42) -> _5 { - _5 := var__42 - - /// @src 0:322:332 - let _7 := read_from_storage_split_offset_0_t_int256(0x00) - let _6 := increment_t_int256(_7) - update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) - let expr_33 := _7 - /// @src 0:336:337 - _5 := fun_f2_53_inner(var__42) - - } - /// @src 0:79:428 - - 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 prepare_store_t_int256(value) -> ret { - ret := value - } - - function read_from_storage_split_dynamic_t_int256(slot, offset) -> value { - value := extract_from_storage_value_dynamict_int256(sload(slot), offset) - - } - - function read_from_storage_split_offset_0_t_int256(slot) -> value { - value := extract_from_storage_value_offset_0t_int256(sload(slot)) - - } - - function revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() { - revert(0, 0) - } - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - - function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function revert_forward_1() { - let pos := allocate_unbounded() - returndatacopy(pos, 0, returndatasize()) - revert(pos, returndatasize()) - } - - function round_up_to_mul_of_32(value) -> result { - result := and(add(value, 31), not(31)) - } - - function shift_left_0(value) -> newValue { - newValue := - - shl(0, value) - - } - - function shift_left_224(value) -> newValue { - newValue := - - shl(224, value) - - } - - function shift_right_0_unsigned(value) -> newValue { - newValue := - - shr(0, value) - - } - - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - - } - - function shift_right_unsigned_dynamic(bits, value) -> newValue { - newValue := - - shr(bits, value) - - } - - function update_byte_slice_32_shift_0(value, toInsert) -> result { - let mask := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - toInsert := shift_left_0(toInsert) - value := and(value, not(mask)) - result := or(value, and(toInsert, mask)) - } - - function update_storage_value_offset_0t_int256_to_t_int256(slot, value_0) { - let convertedValue_0 := convert_t_int256_to_t_int256(value_0) - sstore(slot, update_byte_slice_32_shift_0(sload(slot), prepare_store_t_int256(convertedValue_0))) - } - - function validator_revert_t_int256(value) { - if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } - } - - function zero_value_for_split_t_int256() -> ret { - ret := 0 - } + /// @src 0:79:435 \"contract C...\" } @@ -555,6 +580,188 @@ object \"C_54\" { } +","irOptimized":"/*=====================================================* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *=====================================================*/ + +/// @use-src 0:\"C\" +object \"C_54\" { + code { + { + /// @src 0:79:435 \"contract C...\" + mstore(64, 160) + if callvalue() { revert(0, 0) } + let programSize := datasize(\"C_54\") + let argSize := sub(codesize(), programSize) + let newFreePtr := add(160, and(add(argSize, 31), not(31))) + if or(gt(newFreePtr, sub(shl(64, 1), 1)), lt(newFreePtr, 160)) + { + mstore(/** @src -1:-1:-1 */ 0, /** @src 0:79:435 \"contract C...\" */ shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(/** @src -1:-1:-1 */ 0, /** @src 0:79:435 \"contract C...\" */ 0x24) + } + mstore(64, newFreePtr) + codecopy(160, programSize, argSize) + if slt(argSize, 32) + { + revert(/** @src -1:-1:-1 */ 0, 0) + } + /// @src 0:79:435 \"contract C...\" + constructor_C(mload(160)) + let _1 := mload(64) + let _2 := datasize(\"C_54_deployed\") + codecopy(_1, dataoffset(\"C_54_deployed\"), _2) + setimmutable(_1, \"8\", mload(128)) + return(_1, _2) + } + /// @ast-id 20 @src 0:182:230 \"constructor(int _init)...\" + function constructor_C(var_init) + { + /// @src 0:154:156 \"42\" + mstore(128, 0x2a) + /// @src 0:79:435 \"contract C...\" + sstore(/** @src 0:210:226 \"stateVar = _init\" */ 0x00, /** @src 0:79:435 \"contract C...\" */ var_init) + } + } + /// @use-src 0:\"C\" + object \"C_54_deployed\" { + code { + { + /// @src 0:79:435 \"contract C...\" + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + switch shr(224, calldataload(_1)) + case 0x26121ff0 { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_568(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\")) + /// @src 0:79:435 \"contract C...\" + let memPos := mload(64) + return(memPos, sub(abi_encode_int256(memPos, ret), memPos)) + } + case 0x793816ec { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let ret_1 := sload(_1) + let memPos_1 := mload(64) + return(memPos_1, sub(abi_encode_int256(memPos_1, ret_1), memPos_1)) + } + case 0x9942ec6f { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let ret_2 := /** @src 0:382:385 \"int\" */ modifier_m() + /// @src 0:79:435 \"contract C...\" + let memPos_2 := mload(64) + return(memPos_2, sub(abi_encode_int256(memPos_2, ret_2), memPos_2)) + } + case 0xa00b982b { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let memPos_3 := mload(64) + return(memPos_3, sub(abi_encode_int256_567(memPos_3), memPos_3)) + } + } + revert(0, 0) + } + function abi_decode(dataEnd) + { + if slt(add(dataEnd, not(3)), 0) { revert(0, 0) } + } + function abi_encode_int256_567(headStart) -> tail + { + tail := add(headStart, 32) + mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29) + } + /// @src 0:79:435 \"contract C...\" + function abi_encode_int256(headStart, value0) -> tail + { + tail := add(headStart, 32) + mstore(headStart, value0) + } + function panic_error_0x11() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x11) + revert(0, 0x24) + } + function checked_add_int256_568(y) -> sum + { + if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() } + sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 0:79:435 \"contract C...\" */ y) + } + function checked_add_int256(x, y) -> sum + { + let _1 := slt(x, 0) + if and(iszero(_1), sgt(y, sub(sub(shl(255, 1), 1), x))) { panic_error_0x11() } + if and(_1, slt(y, sub(shl(255, 1), x))) { panic_error_0x11() } + sum := add(x, y) + } + /// @ast-id 37 @src 0:311:348 \"modifier m()...\" + function modifier_m() -> _1 + { + /// @src 0:79:435 \"contract C...\" + let _2 := 0 + let _3 := sload(_2) + if eq(_3, sub(shl(255, 1), 1)) { panic_error_0x11() } + let ret := add(_3, 1) + sstore(_2, ret) + /// @src 0:410:418 \"this.f()\" + if iszero(extcodesize(/** @src 0:410:414 \"this\" */ address())) + /// @src 0:410:418 \"this.f()\" + { + /// @src 0:79:435 \"contract C...\" + revert(_2, _2) + } + /// @src 0:410:418 \"this.f()\" + let _4 := /** @src 0:79:435 \"contract C...\" */ mload(64) + /// @src 0:410:418 \"this.f()\" + mstore(_4, /** @src 0:79:435 \"contract C...\" */ shl(228, 0x026121ff)) + /// @src 0:410:418 \"this.f()\" + let _5 := staticcall(gas(), /** @src 0:410:414 \"this\" */ address(), /** @src 0:410:418 \"this.f()\" */ _4, 4, _4, 32) + if iszero(_5) + { + /// @src 0:79:435 \"contract C...\" + let pos := mload(64) + returndatacopy(pos, _2, returndatasize()) + revert(pos, returndatasize()) + } + /// @src 0:410:418 \"this.f()\" + let expr := /** @src 0:79:435 \"contract C...\" */ _2 + /// @src 0:410:418 \"this.f()\" + if _5 + { + /// @src 0:79:435 \"contract C...\" + let newFreePtr := add(_4, and(add(/** @src 0:410:418 \"this.f()\" */ returndatasize(), /** @src 0:79:435 \"contract C...\" */ 31), not(31))) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _4)) + { + mstore(_2, shl(224, 0x4e487b71)) + mstore(/** @src 0:410:418 \"this.f()\" */ 4, /** @src 0:79:435 \"contract C...\" */ 0x41) + revert(_2, 0x24) + } + mstore(64, newFreePtr) + /// @src 0:410:418 \"this.f()\" + expr := abi_decode_int256_fromMemory(_4, add(_4, returndatasize())) + } + /// @src 0:399:418 \"stateVar + this.f()\" + let expr_1 := checked_add_int256(ret, expr) + /// @src 0:343:344 \"_\" + _1 := /** @src 0:399:429 \"stateVar + this.f() + immutVar\" */ checked_add_int256(expr_1, /** @src 0:421:429 \"immutVar\" */ loadimmutable(\"8\")) + } + /// @src 0:79:435 \"contract C...\" + function abi_decode_int256_fromMemory(headStart, dataEnd) -> value0 + { + if slt(sub(dataEnd, headStart), 32) { revert(0, 0) } + value0 := mload(headStart) + } + } + data \".metadata\" hex\"\" + } +} "}},"D":{"D":{"ir":"/*=====================================================* * WARNING * * Solidity to Yul compilation is still EXPERIMENTAL * @@ -563,10 +770,10 @@ object \"C_54\" { *=====================================================*/ -/// @use-src 0:\"C\", 1:\"D\", 2:\"#utility.yul\" +/// @use-src 0:\"C\", 1:\"D\" object \"D_72\" { code { - /// @src 1:91:166 + /// @src 1:91:166 \"contract D is C(3)...\" mstore(64, 160) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -580,6 +787,52 @@ object \"D_72\" { return(_2, datasize(\"D_72_deployed\")) + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function allocate_memory(size) -> memPtr { + memPtr := allocate_unbounded() + finalize_allocation(memPtr, size) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function cleanup_t_int256(value) -> cleaned { + cleaned := value + } + + function validator_revert_t_int256(value) { + if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } + } + function abi_decode_t_int256_fromMemory(offset, end) -> value { value := mload(offset) validator_revert_t_int256(value) @@ -597,13 +850,52 @@ object \"D_72\" { } - function allocate_memory(size) -> memPtr { - memPtr := allocate_unbounded() - finalize_allocation(memPtr, size) + function copy_arguments_for_constructor_71_object_D_72() -> ret_param_0 { + let programSize := datasize(\"D_72\") + let argSize := sub(codesize(), programSize) + + let memoryDataOffset := allocate_memory(argSize) + codecopy(memoryDataOffset, programSize, argSize) + + ret_param_0 := abi_decode_tuple_t_int256_fromMemory(memoryDataOffset, add(memoryDataOffset, argSize)) } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) + function cleanup_t_rational_3_by_1(value) -> cleaned { + cleaned := value + } + + function identity(value) -> ret { + ret := value + } + + function convert_t_rational_3_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_rational_3_by_1(value))) + } + + function shift_right_0_unsigned(value) -> newValue { + newValue := + + shr(0, value) + + } + + function cleanup_from_storage_t_int256(value) -> cleaned { + cleaned := value + } + + function extract_from_storage_value_offset_0t_int256(slot_value) -> value { + value := cleanup_from_storage_t_int256(shift_right_0_unsigned(slot_value)) + } + + function read_from_storage_split_offset_0_t_int256(slot) -> value { + value := extract_from_storage_value_offset_0t_int256(sload(slot)) + + } + + function panic_error_0x11() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x11) + revert(0, 0x24) } function checked_add_t_int256(x, y) -> sum { @@ -618,137 +910,6 @@ object \"D_72\" { sum := add(x, y) } - function cleanup_from_storage_t_int256(value) -> cleaned { - cleaned := value - } - - function cleanup_t_int256(value) -> cleaned { - cleaned := value - } - - function cleanup_t_rational_3_by_1(value) -> cleaned { - cleaned := value - } - - function cleanup_t_rational_42_by_1(value) -> cleaned { - cleaned := value - } - - /// @src 0:175:223 - function constructor_C_54(var__init_12) { - - /// @src 0:175:223 - - /// @src 0:147:149 - let expr_7 := 0x2a - let _6 := convert_t_rational_42_by_1_to_t_int256(expr_7) - mstore(128, _6) - - /// @src 0:214:219 - let _7 := var__init_12 - let expr_16 := _7 - /// @src 0:203:219 - update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) - let expr_17 := expr_16 - - } - /// @src 1:91:166 - - /// @src 1:113:164 - function constructor_D_72(var__init2_63) { - /// @src 1:107:108 - let expr_60 := 0x03 - let _3 := convert_t_rational_3_by_1_to_t_int256(expr_60) - - /// @src 1:113:164 - constructor_C_54(_3) - - /// @src 1:154:160 - let _4 := var__init2_63 - let expr_67 := _4 - /// @src 1:142:160 - let _5 := read_from_storage_split_offset_0_t_int256(0x00) - let expr_68 := checked_add_t_int256(_5, expr_67) - - update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_68) - - } - /// @src 1:91:166 - - function convert_t_int256_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_int256(value))) - } - - function convert_t_rational_3_by_1_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_rational_3_by_1(value))) - } - - function convert_t_rational_42_by_1_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_rational_42_by_1(value))) - } - - function copy_arguments_for_constructor_71_object_D_72() -> ret_param_0 { - let programSize := datasize(\"D_72\") - let argSize := sub(codesize(), programSize) - - let memoryDataOffset := allocate_memory(argSize) - codecopy(memoryDataOffset, programSize, argSize) - - ret_param_0 := abi_decode_tuple_t_int256_fromMemory(memoryDataOffset, add(memoryDataOffset, argSize)) - } - - function extract_from_storage_value_offset_0t_int256(slot_value) -> value { - value := cleanup_from_storage_t_int256(shift_right_0_unsigned(slot_value)) - } - - function finalize_allocation(memPtr, size) { - let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) - // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) - } - - function identity(value) -> ret { - ret := value - } - - 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 prepare_store_t_int256(value) -> ret { - ret := value - } - - function read_from_storage_split_offset_0_t_int256(slot) -> value { - value := extract_from_storage_value_offset_0t_int256(sload(slot)) - - } - - function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function round_up_to_mul_of_32(value) -> result { - result := and(add(value, 31), not(31)) - } - function shift_left_0(value) -> newValue { newValue := @@ -756,13 +917,6 @@ object \"D_72\" { } - function shift_right_0_unsigned(value) -> newValue { - newValue := - - shr(0, value) - - } - function update_byte_slice_32_shift_0(value, toInsert) -> result { let mask := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff toInsert := shift_left_0(toInsert) @@ -770,19 +924,75 @@ object \"D_72\" { result := or(value, and(toInsert, mask)) } + function convert_t_int256_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_int256(value))) + } + + function prepare_store_t_int256(value) -> ret { + ret := value + } + function update_storage_value_offset_0t_int256_to_t_int256(slot, value_0) { let convertedValue_0 := convert_t_int256_to_t_int256(value_0) sstore(slot, update_byte_slice_32_shift_0(sload(slot), prepare_store_t_int256(convertedValue_0))) } - function validator_revert_t_int256(value) { - if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } + /// @ast-id 71 + /// @src 1:113:164 \"constructor(int _init2)...\" + function constructor_D_72(var__init2_63) { + /// @src 1:107:108 \"3\" + let expr_60 := 0x03 + let _3 := convert_t_rational_3_by_1_to_t_int256(expr_60) + + /// @src 1:113:164 \"constructor(int _init2)...\" + constructor_C_54(_3) + + /// @src 1:154:160 \"_init2\" + let _4 := var__init2_63 + let expr_67 := _4 + /// @src 1:142:160 \"stateVar += _init2\" + let _5 := read_from_storage_split_offset_0_t_int256(0x00) + let expr_68 := checked_add_t_int256(_5, expr_67) + + update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_68) + + } + /// @src 1:91:166 \"contract D is C(3)...\" + + function cleanup_t_rational_42_by_1(value) -> cleaned { + cleaned := value } + function convert_t_rational_42_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_rational_42_by_1(value))) + } + + /// @ast-id 20 + /// @src 0:182:230 \"constructor(int _init)...\" + function constructor_C_54(var__init_12) { + + /// @src 0:182:230 \"constructor(int _init)...\" + + /// @src 0:154:156 \"42\" + let expr_7 := 0x2a + let _6 := convert_t_rational_42_by_1_to_t_int256(expr_7) + mstore(128, _6) + + /// @src 0:221:226 \"_init\" + let _7 := var__init_12 + let expr_16 := _7 + /// @src 0:210:226 \"stateVar = _init\" + update_storage_value_offset_0t_int256_to_t_int256(0x00, expr_16) + let expr_17 := expr_16 + + } + /// @src 1:91:166 \"contract D is C(3)...\" + } + /// @use-src 0:\"C\", 1:\"D\" object \"D_72_deployed\" { code { - /// @src 1:91:166 + /// @src 1:91:166 \"contract D is C(3)...\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -826,14 +1036,40 @@ object \"D_72\" { return(memPos, sub(memEnd, memPos)) } + case 0xa00b982b + { + // constVar() + + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + abi_decode_tuple_(4, calldatasize()) + let ret_0 := getter_fun_constVar_5() + let memPos := allocate_unbounded() + let memEnd := abi_encode_tuple_t_int256__to_t_int256__fromStack(memPos , ret_0) + return(memPos, sub(memEnd, memPos)) + } + default {} } if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() - function abi_decode_t_int256_fromMemory(offset, end) -> value { - value := mload(offset) - validator_revert_t_int256(value) + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) } function abi_decode_tuple_(headStart, dataEnd) { @@ -841,27 +1077,14 @@ object \"D_72\" { } - function abi_decode_tuple_t_int256_fromMemory(headStart, dataEnd) -> value0 { - if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } - - { - - let offset := 0 - - value0 := abi_decode_t_int256_fromMemory(add(headStart, offset), dataEnd) - } - + function cleanup_t_int256(value) -> cleaned { + cleaned := value } function abi_encode_t_int256_to_t_int256_fromStack(value, pos) { mstore(pos, cleanup_t_int256(value)) } - function abi_encode_tuple__to__fromStack(headStart ) -> tail { - tail := add(headStart, 0) - - } - function abi_encode_tuple_t_int256__to_t_int256__fromStack(headStart , value0) -> tail { tail := add(headStart, 32) @@ -869,8 +1092,78 @@ object \"D_72\" { } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) + function shift_right_unsigned_dynamic(bits, value) -> newValue { + newValue := + + shr(bits, value) + + } + + function cleanup_from_storage_t_int256(value) -> cleaned { + cleaned := value + } + + function extract_from_storage_value_dynamict_int256(slot_value, offset) -> value { + value := cleanup_from_storage_t_int256(shift_right_unsigned_dynamic(mul(offset, 8), slot_value)) + } + + function read_from_storage_split_dynamic_t_int256(slot, offset) -> value { + value := extract_from_storage_value_dynamict_int256(sload(slot), offset) + + } + + /// @ast-id 10 + /// @src 0:159:178 \"int public stateVar\" + function getter_fun_stateVar_10() -> ret { + + let slot := 0 + let offset := 0 + + ret := read_from_storage_split_dynamic_t_int256(slot, offset) + + } + /// @src 1:91:166 \"contract D is C(3)...\" + + function cleanup_t_rational_41_by_1(value) -> cleaned { + cleaned := value + } + + function identity(value) -> ret { + ret := value + } + + function convert_t_rational_41_by_1_to_t_int256(value) -> converted { + converted := cleanup_t_int256(identity(cleanup_t_rational_41_by_1(value))) + } + + /// @src 0:93:126 \"int public constant constVar = 41\" + function constant_constVar_5() -> ret { + /// @src 0:124:126 \"41\" + let expr_4 := 0x29 + let _1 := convert_t_rational_41_by_1_to_t_int256(expr_4) + + ret := _1 + } + + /// @ast-id 5 + /// @src 0:93:126 \"int public constant constVar = 41\" + function getter_fun_constVar_5() -> ret_0 { + ret_0 := constant_constVar_5() + } + /// @src 1:91:166 \"contract D is C(3)...\" + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function zero_value_for_split_t_int256() -> ret { + ret := 0 + } + + function panic_error_0x11() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x11) + revert(0, 0x24) } function checked_add_t_int256(x, y) -> sum { @@ -885,57 +1178,121 @@ object \"D_72\" { sum := add(x, y) } - function cleanup_from_storage_t_int256(value) -> cleaned { - cleaned := value + /// @ast-id 30 + /// @src 0:233:309 \"function f() external pure returns (int)...\" + function fun_f_30() -> var__23 { + /// @src 0:269:272 \"int\" + let zero_t_int256_2 := zero_value_for_split_t_int256() + var__23 := zero_t_int256_2 + + /// @src 0:286:294 \"constVar\" + let expr_25 := constant_constVar_5() + /// @src 0:297:305 \"immutVar\" + let _3 := loadimmutable(\"8\") + let expr_26 := _3 + /// @src 0:286:305 \"constVar + immutVar\" + let expr_27 := checked_add_t_int256(expr_25, expr_26) + + /// @src 0:279:305 \"return constVar + immutVar\" + var__23 := expr_27 + leave + + } + /// @src 1:91:166 \"contract D is C(3)...\" + + function shift_right_0_unsigned(value) -> newValue { + newValue := + + shr(0, value) + } - function cleanup_t_int256(value) -> cleaned { - cleaned := value + function extract_from_storage_value_offset_0t_int256(slot_value) -> value { + value := cleanup_from_storage_t_int256(shift_right_0_unsigned(slot_value)) } - function cleanup_t_rational_41_by_1(value) -> cleaned { - cleaned := value + function read_from_storage_split_offset_0_t_int256(slot) -> value { + value := extract_from_storage_value_offset_0t_int256(sload(slot)) + } - function cleanup_t_uint160(value) -> cleaned { - cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) + function increment_t_int256(value) -> ret { + value := cleanup_t_int256(value) + if eq(value, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) { panic_error_0x11() } + ret := add(value, 1) } - /// @src 0:93:119 - function constant_constVar_5() -> ret { - /// @src 0:117:119 - let expr_4 := 0x29 - let _2 := convert_t_rational_41_by_1_to_t_int256(expr_4) + function shift_left_0(value) -> newValue { + newValue := + + shl(0, value) - ret := _2 } - function convert_t_contract$_C_$54_to_t_address(value) -> converted { - converted := convert_t_uint160_to_t_address(value) + function update_byte_slice_32_shift_0(value, toInsert) -> result { + let mask := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + toInsert := shift_left_0(toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) } function convert_t_int256_to_t_int256(value) -> converted { converted := cleanup_t_int256(identity(cleanup_t_int256(value))) } - function convert_t_rational_41_by_1_to_t_int256(value) -> converted { - converted := cleanup_t_int256(identity(cleanup_t_rational_41_by_1(value))) + function prepare_store_t_int256(value) -> ret { + ret := value } - function convert_t_uint160_to_t_address(value) -> converted { - converted := convert_t_uint160_to_t_uint160(value) + function update_storage_value_offset_0t_int256_to_t_int256(slot, value_0) { + let convertedValue_0 := convert_t_int256_to_t_int256(value_0) + sstore(slot, update_byte_slice_32_shift_0(sload(slot), prepare_store_t_int256(convertedValue_0))) + } + + /// @ast-id 37 + /// @src 0:311:348 \"modifier m()...\" + function modifier_m_40(var__42) -> _5 { + _5 := var__42 + + /// @src 0:329:339 \"stateVar++\" + let _7 := read_from_storage_split_offset_0_t_int256(0x00) + let _6 := increment_t_int256(_7) + update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) + let expr_33 := _7 + /// @src 0:343:344 \"_\" + _5 := fun_f2_53_inner(var__42) + + } + /// @src 1:91:166 \"contract D is C(3)...\" + + function cleanup_t_uint160(value) -> cleaned { + cleaned := and(value, 0xffffffffffffffffffffffffffffffffffffffff) } function convert_t_uint160_to_t_uint160(value) -> converted { converted := cleanup_t_uint160(identity(cleanup_t_uint160(value))) } - function extract_from_storage_value_dynamict_int256(slot_value, offset) -> value { - value := cleanup_from_storage_t_int256(shift_right_unsigned_dynamic(mul(offset, 8), slot_value)) + function convert_t_uint160_to_t_address(value) -> converted { + converted := convert_t_uint160_to_t_uint160(value) } - function extract_from_storage_value_offset_0t_int256(slot_value) -> value { - value := cleanup_from_storage_t_int256(shift_right_0_unsigned(slot_value)) + function convert_t_contract$_C_$54_to_t_address(value) -> converted { + converted := convert_t_uint160_to_t_address(value) + } + + function revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() { + revert(0, 0) + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) } function finalize_allocation(memPtr, size) { @@ -945,29 +1302,62 @@ object \"D_72\" { mstore(64, newFreePtr) } - /// @src 0:343:426 - function fun_f2_53() -> var__42 { - /// @src 0:375:378 - let zero_t_int256_4 := zero_value_for_split_t_int256() - var__42 := zero_t_int256_4 + function shift_left_224(value) -> newValue { + newValue := + + shl(224, value) - var__42 := modifier_m_40(var__42) } - /// @src 1:91:166 - /// @src 0:343:426 + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function validator_revert_t_int256(value) { + if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } + } + + function abi_decode_t_int256_fromMemory(offset, end) -> value { + value := mload(offset) + validator_revert_t_int256(value) + } + + function abi_decode_tuple_t_int256_fromMemory(headStart, dataEnd) -> value0 { + if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + { + + let offset := 0 + + value0 := abi_decode_t_int256_fromMemory(add(headStart, offset), dataEnd) + } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function revert_forward_1() { + let pos := allocate_unbounded() + returndatacopy(pos, 0, returndatasize()) + revert(pos, returndatasize()) + } + + /// @src 0:350:433 \"function f2() m public returns (int)...\" function fun_f2_53_inner(_8) -> var__42 { var__42 := _8 - /// @src 0:392:400 + /// @src 0:399:407 \"stateVar\" let _9 := read_from_storage_split_offset_0_t_int256(0x00) let expr_44 := _9 - /// @src 0:403:407 + /// @src 0:410:414 \"this\" let expr_45_address := address() - /// @src 0:403:409 + /// @src 0:410:416 \"this.f\" let expr_46_address := convert_t_contract$_C_$54_to_t_address(expr_45_address) let expr_46_functionSelector := 0x26121ff0 - /// @src 0:403:411 + /// @src 0:410:418 \"this.f()\" if iszero(extcodesize(expr_46_address)) { revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() } // storage for arguments and returned data @@ -988,189 +1378,32 @@ object \"D_72\" { // decode return parameters from external try-call into retVars expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, returndatasize())) } - /// @src 0:392:411 + /// @src 0:399:418 \"stateVar + this.f()\" let expr_48 := checked_add_t_int256(expr_44, expr_47) - /// @src 0:414:422 + /// @src 0:421:429 \"immutVar\" let _13 := loadimmutable(\"8\") let expr_49 := _13 - /// @src 0:392:422 + /// @src 0:399:429 \"stateVar + this.f() + immutVar\" let expr_50 := checked_add_t_int256(expr_48, expr_49) - /// @src 0:385:422 + /// @src 0:392:429 \"return stateVar + this.f() + immutVar\" var__42 := expr_50 leave } - /// @src 1:91:166 + /// @src 1:91:166 \"contract D is C(3)...\" - /// @src 0:226:302 - function fun_f_30() -> var__23 { - /// @src 0:262:265 - let zero_t_int256_1 := zero_value_for_split_t_int256() - var__23 := zero_t_int256_1 - - /// @src 0:279:287 - let expr_25 := constant_constVar_5() - /// @src 0:290:298 - let _3 := loadimmutable(\"8\") - let expr_26 := _3 - /// @src 0:279:298 - let expr_27 := checked_add_t_int256(expr_25, expr_26) - - /// @src 0:272:298 - var__23 := expr_27 - leave + /// @ast-id 53 + /// @src 0:350:433 \"function f2() m public returns (int)...\" + function fun_f2_53() -> var__42 { + /// @src 0:382:385 \"int\" + let zero_t_int256_4 := zero_value_for_split_t_int256() + var__42 := zero_t_int256_4 + var__42 := modifier_m_40(var__42) } - /// @src 1:91:166 - - /// @src 0:152:171 - function getter_fun_stateVar_10() -> ret { - - let slot := 0 - let offset := 0 - - ret := read_from_storage_split_dynamic_t_int256(slot, offset) - - } - /// @src 1:91:166 - - function identity(value) -> ret { - ret := value - } - - function increment_t_int256(value) -> ret { - value := cleanup_t_int256(value) - if eq(value, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) { panic_error_0x11() } - ret := add(value, 1) - } - - /// @src 0:304:341 - function modifier_m_40(var__42) -> _5 { - _5 := var__42 - - /// @src 0:322:332 - let _7 := read_from_storage_split_offset_0_t_int256(0x00) - let _6 := increment_t_int256(_7) - update_storage_value_offset_0t_int256_to_t_int256(0x00, _6) - let expr_33 := _7 - /// @src 0:336:337 - _5 := fun_f2_53_inner(var__42) - - } - /// @src 1:91:166 - - 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 prepare_store_t_int256(value) -> ret { - ret := value - } - - function read_from_storage_split_dynamic_t_int256(slot, offset) -> value { - value := extract_from_storage_value_dynamict_int256(sload(slot), offset) - - } - - function read_from_storage_split_offset_0_t_int256(slot) -> value { - value := extract_from_storage_value_offset_0t_int256(sload(slot)) - - } - - function revert_error_0cc013b6b3b6beabea4e3a74a6d380f0df81852ca99887912475e1f66b2a2c20() { - revert(0, 0) - } - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - - function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function revert_forward_1() { - let pos := allocate_unbounded() - returndatacopy(pos, 0, returndatasize()) - revert(pos, returndatasize()) - } - - function round_up_to_mul_of_32(value) -> result { - result := and(add(value, 31), not(31)) - } - - function shift_left_0(value) -> newValue { - newValue := - - shl(0, value) - - } - - function shift_left_224(value) -> newValue { - newValue := - - shl(224, value) - - } - - function shift_right_0_unsigned(value) -> newValue { - newValue := - - shr(0, value) - - } - - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - - } - - function shift_right_unsigned_dynamic(bits, value) -> newValue { - newValue := - - shr(bits, value) - - } - - function update_byte_slice_32_shift_0(value, toInsert) -> result { - let mask := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - toInsert := shift_left_0(toInsert) - value := and(value, not(mask)) - result := or(value, and(toInsert, mask)) - } - - function update_storage_value_offset_0t_int256_to_t_int256(slot, value_0) { - let convertedValue_0 := convert_t_int256_to_t_int256(value_0) - sstore(slot, update_byte_slice_32_shift_0(sload(slot), prepare_store_t_int256(convertedValue_0))) - } - - function validator_revert_t_int256(value) { - if iszero(eq(value, cleanup_t_int256(value))) { revert(0, 0) } - } - - function zero_value_for_split_t_int256() -> ret { - ret := 0 - } + /// @src 1:91:166 \"contract D is C(3)...\" } @@ -1179,4 +1412,194 @@ object \"D_72\" { } +","irOptimized":"/*=====================================================* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *=====================================================*/ + +/// @use-src 0:\"C\", 1:\"D\" +object \"D_72\" { + code { + { + /// @src 1:91:166 \"contract D is C(3)...\" + mstore(64, 160) + if callvalue() { revert(0, 0) } + let programSize := datasize(\"D_72\") + let argSize := sub(codesize(), programSize) + let newFreePtr := add(160, and(add(argSize, 31), not(31))) + if or(gt(newFreePtr, sub(shl(64, 1), 1)), lt(newFreePtr, 160)) + { + mstore(/** @src -1:-1:-1 */ 0, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(/** @src -1:-1:-1 */ 0, /** @src 1:91:166 \"contract D is C(3)...\" */ 0x24) + } + mstore(64, newFreePtr) + codecopy(160, programSize, argSize) + if slt(argSize, 32) + { + revert(/** @src -1:-1:-1 */ 0, 0) + } + /// @src 1:91:166 \"contract D is C(3)...\" + constructor_D(mload(160)) + let _1 := mload(64) + let _2 := datasize(\"D_72_deployed\") + codecopy(_1, dataoffset(\"D_72_deployed\"), _2) + setimmutable(_1, \"8\", mload(128)) + return(_1, _2) + } + /// @ast-id 71 @src 1:113:164 \"constructor(int _init2)...\" + function constructor_D(var_init2) + { + /// @src 0:154:156 \"42\" + mstore(128, 0x2a) + /// @src 1:91:166 \"contract D is C(3)...\" + sstore(/** @src 0:210:226 \"stateVar = _init\" */ 0x00, /** @src 1:107:108 \"3\" */ 0x03) + /// @src 1:91:166 \"contract D is C(3)...\" + if and(1, sgt(var_init2, sub(shl(255, 1), 4))) + { + mstore(/** @src 0:210:226 \"stateVar = _init\" */ 0x00, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(224, 0x4e487b71)) + mstore(4, 0x11) + revert(/** @src 0:210:226 \"stateVar = _init\" */ 0x00, /** @src 1:91:166 \"contract D is C(3)...\" */ 0x24) + } + sstore(/** @src 0:210:226 \"stateVar = _init\" */ 0x00, /** @src 1:91:166 \"contract D is C(3)...\" */ add(/** @src 1:107:108 \"3\" */ 0x03, /** @src 1:91:166 \"contract D is C(3)...\" */ var_init2)) + } + } + /// @use-src 0:\"C\", 1:\"D\" + object \"D_72_deployed\" { + code { + { + /// @src 1:91:166 \"contract D is C(3)...\" + mstore(64, 128) + if iszero(lt(calldatasize(), 4)) + { + let _1 := 0 + switch shr(224, calldataload(_1)) + case 0x26121ff0 { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let ret := /** @src 0:286:305 \"constVar + immutVar\" */ checked_add_int256_568(/** @src 0:297:305 \"immutVar\" */ loadimmutable(\"8\")) + /// @src 1:91:166 \"contract D is C(3)...\" + let memPos := mload(64) + return(memPos, sub(abi_encode_int256(memPos, ret), memPos)) + } + case 0x793816ec { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let ret_1 := sload(_1) + let memPos_1 := mload(64) + return(memPos_1, sub(abi_encode_int256(memPos_1, ret_1), memPos_1)) + } + case 0x9942ec6f { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let ret_2 := /** @src 0:382:385 \"int\" */ modifier_m() + /// @src 1:91:166 \"contract D is C(3)...\" + let memPos_2 := mload(64) + return(memPos_2, sub(abi_encode_int256(memPos_2, ret_2), memPos_2)) + } + case 0xa00b982b { + if callvalue() { revert(_1, _1) } + abi_decode(calldatasize()) + let memPos_3 := mload(64) + return(memPos_3, sub(abi_encode_int256_567(memPos_3), memPos_3)) + } + } + revert(0, 0) + } + function abi_decode(dataEnd) + { + if slt(add(dataEnd, not(3)), 0) { revert(0, 0) } + } + function abi_encode_int256_567(headStart) -> tail + { + tail := add(headStart, 32) + mstore(headStart, /** @src 0:124:126 \"41\" */ 0x29) + } + /// @src 1:91:166 \"contract D is C(3)...\" + function abi_encode_int256(headStart, value0) -> tail + { + tail := add(headStart, 32) + mstore(headStart, value0) + } + function panic_error_0x11() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x11) + revert(0, 0x24) + } + function checked_add_int256_568(y) -> sum + { + if and(1, sgt(y, sub(shl(255, 1), 42))) { panic_error_0x11() } + sum := add(/** @src 0:124:126 \"41\" */ 0x29, /** @src 1:91:166 \"contract D is C(3)...\" */ y) + } + function checked_add_int256(x, y) -> sum + { + let _1 := slt(x, 0) + if and(iszero(_1), sgt(y, sub(sub(shl(255, 1), 1), x))) { panic_error_0x11() } + if and(_1, slt(y, sub(shl(255, 1), x))) { panic_error_0x11() } + sum := add(x, y) + } + /// @ast-id 37 @src 0:311:348 \"modifier m()...\" + function modifier_m() -> _1 + { + /// @src 1:91:166 \"contract D is C(3)...\" + let _2 := 0 + let _3 := sload(_2) + if eq(_3, sub(shl(255, 1), 1)) { panic_error_0x11() } + let ret := add(_3, 1) + sstore(_2, ret) + /// @src 0:410:418 \"this.f()\" + if iszero(extcodesize(/** @src 0:410:414 \"this\" */ address())) + /// @src 0:410:418 \"this.f()\" + { + /// @src 1:91:166 \"contract D is C(3)...\" + revert(_2, _2) + } + /// @src 0:410:418 \"this.f()\" + let _4 := /** @src 1:91:166 \"contract D is C(3)...\" */ mload(64) + /// @src 0:410:418 \"this.f()\" + mstore(_4, /** @src 1:91:166 \"contract D is C(3)...\" */ shl(228, 0x026121ff)) + /// @src 0:410:418 \"this.f()\" + let _5 := staticcall(gas(), /** @src 0:410:414 \"this\" */ address(), /** @src 0:410:418 \"this.f()\" */ _4, 4, _4, 32) + if iszero(_5) + { + /// @src 1:91:166 \"contract D is C(3)...\" + let pos := mload(64) + returndatacopy(pos, _2, returndatasize()) + revert(pos, returndatasize()) + } + /// @src 0:410:418 \"this.f()\" + let expr := /** @src 1:91:166 \"contract D is C(3)...\" */ _2 + /// @src 0:410:418 \"this.f()\" + if _5 + { + /// @src 1:91:166 \"contract D is C(3)...\" + let newFreePtr := add(_4, and(add(/** @src 0:410:418 \"this.f()\" */ returndatasize(), /** @src 1:91:166 \"contract D is C(3)...\" */ 31), not(31))) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _4)) + { + mstore(_2, shl(224, 0x4e487b71)) + mstore(/** @src 0:410:418 \"this.f()\" */ 4, /** @src 1:91:166 \"contract D is C(3)...\" */ 0x41) + revert(_2, 0x24) + } + mstore(64, newFreePtr) + /// @src 0:410:418 \"this.f()\" + expr := abi_decode_int256_fromMemory(_4, add(_4, returndatasize())) + } + /// @src 0:399:418 \"stateVar + this.f()\" + let expr_1 := checked_add_int256(ret, expr) + /// @src 0:343:344 \"_\" + _1 := /** @src 0:399:429 \"stateVar + this.f() + immutVar\" */ checked_add_int256(expr_1, /** @src 0:421:429 \"immutVar\" */ loadimmutable(\"8\")) + } + /// @src 1:91:166 \"contract D is C(3)...\" + function abi_decode_int256_fromMemory(headStart, dataEnd) -> value0 + { + if slt(sub(dataEnd, headStart), 32) { revert(0, 0) } + value0 := mload(headStart) + } + } + data \".metadata\" hex\"\" + } +} "}}},"sources":{"C":{"id":0},"D":{"id":1}}} diff --git a/test/cmdlineTests/standard_function_debug_info/output.json b/test/cmdlineTests/standard_function_debug_info/output.json index e0e6d7257..bb86d0924 100644 --- a/test/cmdlineTests/standard_function_debug_info/output.json +++ b/test/cmdlineTests/standard_function_debug_info/output.json @@ -1 +1 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"functionDebugData":{}},"deployedBytecode":{"functionDebugData":{"@f_19":{"entryPoint":96,"id":19,"parameterSlots":1,"returnSlots":1},"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":171,"id":null,"parameterSlots":3,"returnSlots":1},"abi_decode_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":283,"id":null,"parameterSlots":2,"returnSlots":1},"abi_decode_t_uint256":{"entryPoint":329,"id":null,"parameterSlots":2,"returnSlots":1},"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":350,"id":null,"parameterSlots":2,"returnSlots":1},"abi_encode_t_uint256_to_t_uint256_fromStack":{"entryPoint":423,"id":null,"parameterSlots":2,"returnSlots":0},"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed":{"entryPoint":438,"id":null,"parameterSlots":2,"returnSlots":1},"allocate_memory":{"entryPoint":465,"id":null,"parameterSlots":1,"returnSlots":1},"allocate_unbounded":{"entryPoint":492,"id":null,"parameterSlots":0,"returnSlots":1},"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":502,"id":null,"parameterSlots":1,"returnSlots":1},"checked_add_t_uint256":{"entryPoint":546,"id":null,"parameterSlots":2,"returnSlots":1},"cleanup_t_uint256":{"entryPoint":632,"id":null,"parameterSlots":1,"returnSlots":1},"finalize_allocation":{"entryPoint":642,"id":null,"parameterSlots":2,"returnSlots":0},"panic_error_0x11":{"entryPoint":691,"id":null,"parameterSlots":0,"returnSlots":0},"panic_error_0x32":{"entryPoint":738,"id":null,"parameterSlots":0,"returnSlots":0},"panic_error_0x41":{"entryPoint":785,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d":{"entryPoint":832,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef":{"entryPoint":837,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db":{"entryPoint":842,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b":{"entryPoint":847,"id":null,"parameterSlots":0,"returnSlots":0},"round_up_to_mul_of_32":{"entryPoint":852,"id":null,"parameterSlots":1,"returnSlots":1},"validator_revert_t_uint256":{"entryPoint":869,"id":null,"parameterSlots":1,"returnSlots":0}}}}}}},"sources":{"a.sol":{"id":0}}} +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"functionDebugData":{}},"deployedBytecode":{"functionDebugData":{"@f_19":{"entryPoint":96,"id":19,"parameterSlots":1,"returnSlots":1},"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":439,"id":null,"parameterSlots":3,"returnSlots":1},"abi_decode_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":544,"id":null,"parameterSlots":2,"returnSlots":1},"abi_decode_t_uint256":{"entryPoint":418,"id":null,"parameterSlots":2,"returnSlots":1},"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":590,"id":null,"parameterSlots":2,"returnSlots":1},"abi_encode_t_uint256_to_t_uint256_fromStack":{"entryPoint":663,"id":null,"parameterSlots":2,"returnSlots":0},"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed":{"entryPoint":678,"id":null,"parameterSlots":2,"returnSlots":1},"allocate_memory":{"entryPoint":309,"id":null,"parameterSlots":1,"returnSlots":1},"allocate_unbounded":{"entryPoint":171,"id":null,"parameterSlots":0,"returnSlots":1},"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":336,"id":null,"parameterSlots":1,"returnSlots":1},"checked_add_t_uint256":{"entryPoint":799,"id":null,"parameterSlots":2,"returnSlots":1},"cleanup_t_uint256":{"entryPoint":385,"id":null,"parameterSlots":1,"returnSlots":1},"finalize_allocation":{"entryPoint":260,"id":null,"parameterSlots":2,"returnSlots":0},"panic_error_0x11":{"entryPoint":752,"id":null,"parameterSlots":0,"returnSlots":0},"panic_error_0x32":{"entryPoint":705,"id":null,"parameterSlots":0,"returnSlots":0},"panic_error_0x41":{"entryPoint":213,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d":{"entryPoint":191,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef":{"entryPoint":380,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db":{"entryPoint":186,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b":{"entryPoint":181,"id":null,"parameterSlots":0,"returnSlots":0},"round_up_to_mul_of_32":{"entryPoint":196,"id":null,"parameterSlots":1,"returnSlots":1},"validator_revert_t_uint256":{"entryPoint":395,"id":null,"parameterSlots":1,"returnSlots":0}}}}}}},"sources":{"a.sol":{"id":0}}} diff --git a/test/cmdlineTests/standard_generatedSources/output.json b/test/cmdlineTests/standard_generatedSources/output.json index b09f6750f..05405ceab 100644 --- a/test/cmdlineTests/standard_generatedSources/output.json +++ b/test/cmdlineTests/standard_generatedSources/output.json @@ -1,4 +1,70 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:4001:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:620:1","statements":[{"nodeType":"YulAssignment","src":"136:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"218:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"161:56:1"},"nodeType":"YulFunctionCall","src":"161:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"145:15:1"},"nodeType":"YulFunctionCall","src":"145:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"235:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"246:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"239:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"268:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"275:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"261:6:1"},"nodeType":"YulFunctionCall","src":"261:21:1"},"nodeType":"YulExpressionStatement","src":"261:21:1"},{"nodeType":"YulAssignment","src":"291:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"302:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"309:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"298:3:1"},"nodeType":"YulFunctionCall","src":"298:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"291:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"324:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"335:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"328:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"390:103:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulIdentifier","src":"404:77:1"},"nodeType":"YulFunctionCall","src":"404:79:1"},"nodeType":"YulExpressionStatement","src":"404:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"360:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"369:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"377:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"365:3:1"},"nodeType":"YulFunctionCall","src":"365:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"356:3:1"},"nodeType":"YulFunctionCall","src":"356:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"385:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"353:2:1"},"nodeType":"YulFunctionCall","src":"353:36:1"},"nodeType":"YulIf","src":"350:143:1"},{"body":{"nodeType":"YulBlock","src":"562:178:1","statements":[{"nodeType":"YulVariableDeclaration","src":"577:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"595:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"581:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"619:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"645:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"657:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"624:20:1"},"nodeType":"YulFunctionCall","src":"624:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"612:6:1"},"nodeType":"YulFunctionCall","src":"612:50:1"},"nodeType":"YulExpressionStatement","src":"612:50:1"},{"nodeType":"YulAssignment","src":"675:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"686:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"691:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"682:3:1"},"nodeType":"YulFunctionCall","src":"682:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"675:3:1"}]},{"nodeType":"YulAssignment","src":"709:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"720:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"725:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"716:3:1"},"nodeType":"YulFunctionCall","src":"716:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"709:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"524:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"527:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"521:2:1"},"nodeType":"YulFunctionCall","src":"521:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"535:18:1","statements":[{"nodeType":"YulAssignment","src":"537:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"546:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"549:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"542:3:1"},"nodeType":"YulFunctionCall","src":"542:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"537:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"506:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"508:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"517:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"512:1:1","type":""}]}]},"src":"502:238: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:722:1"},{"body":{"nodeType":"YulBlock","src":"846:293:1","statements":[{"body":{"nodeType":"YulBlock","src":"895:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulIdentifier","src":"897:77:1"},"nodeType":"YulFunctionCall","src":"897:79:1"},"nodeType":"YulExpressionStatement","src":"897:79:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"874:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"882:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"870:3:1"},"nodeType":"YulFunctionCall","src":"870:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"889:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"866:3:1"},"nodeType":"YulFunctionCall","src":"866:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"859:6:1"},"nodeType":"YulFunctionCall","src":"859:35:1"},"nodeType":"YulIf","src":"856:122:1"},{"nodeType":"YulVariableDeclaration","src":"987:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1014:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1001:12:1"},"nodeType":"YulFunctionCall","src":"1001:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"991:6:1","type":""}]},{"nodeType":"YulAssignment","src":"1030:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1106:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1114:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1102:3:1"},"nodeType":"YulFunctionCall","src":"1102:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"1121:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"1129:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1039:62:1"},"nodeType":"YulFunctionCall","src":"1039:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"1030:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"824:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"832:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"840:5:1","type":""}],"src":"769:370:1"},{"body":{"nodeType":"YulBlock","src":"1197:87:1","statements":[{"nodeType":"YulAssignment","src":"1207:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1229:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1216:12:1"},"nodeType":"YulFunctionCall","src":"1216:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1207:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1272:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1245:26:1"},"nodeType":"YulFunctionCall","src":"1245:33:1"},"nodeType":"YulExpressionStatement","src":"1245:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1175:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1183:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1191:5:1","type":""}],"src":"1145:139:1"},{"body":{"nodeType":"YulBlock","src":"1381:448:1","statements":[{"body":{"nodeType":"YulBlock","src":"1427:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulIdentifier","src":"1429:77:1"},"nodeType":"YulFunctionCall","src":"1429:79:1"},"nodeType":"YulExpressionStatement","src":"1429:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1402:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1411:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1398:3:1"},"nodeType":"YulFunctionCall","src":"1398:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1423:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1394:3:1"},"nodeType":"YulFunctionCall","src":"1394:32:1"},"nodeType":"YulIf","src":"1391:119:1"},{"nodeType":"YulBlock","src":"1520:302:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1535:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1566:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1577:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1562:3:1"},"nodeType":"YulFunctionCall","src":"1562:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1549:12:1"},"nodeType":"YulFunctionCall","src":"1549:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1539:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1627:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulIdentifier","src":"1629:77:1"},"nodeType":"YulFunctionCall","src":"1629:79:1"},"nodeType":"YulExpressionStatement","src":"1629:79:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1599:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1607:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1596:2:1"},"nodeType":"YulFunctionCall","src":"1596:30:1"},"nodeType":"YulIf","src":"1593:117:1"},{"nodeType":"YulAssignment","src":"1724:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1784:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1795:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1780:3:1"},"nodeType":"YulFunctionCall","src":"1780:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1804:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1734:45:1"},"nodeType":"YulFunctionCall","src":"1734:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1724:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1351:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1362:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1374:6:1","type":""}],"src":"1290:539:1"},{"body":{"nodeType":"YulBlock","src":"1900:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1917:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1940:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1922:17:1"},"nodeType":"YulFunctionCall","src":"1922:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1910:6:1"},"nodeType":"YulFunctionCall","src":"1910:37:1"},"nodeType":"YulExpressionStatement","src":"1910:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1888:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1895:3:1","type":""}],"src":"1835:118:1"},{"body":{"nodeType":"YulBlock","src":"2057:124:1","statements":[{"nodeType":"YulAssignment","src":"2067:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2079:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2090:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2075:3:1"},"nodeType":"YulFunctionCall","src":"2075:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"2067:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"2147:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2160:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2171:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2156:3:1"},"nodeType":"YulFunctionCall","src":"2156:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"2103:43:1"},"nodeType":"YulFunctionCall","src":"2103:71:1"},"nodeType":"YulExpressionStatement","src":"2103:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"2029:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"2041:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"2052:4:1","type":""}],"src":"1959:222:1"},{"body":{"nodeType":"YulBlock","src":"2228:88:1","statements":[{"nodeType":"YulAssignment","src":"2238:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"2248:18:1"},"nodeType":"YulFunctionCall","src":"2248:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2238:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2297:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"2305:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"2277:19:1"},"nodeType":"YulFunctionCall","src":"2277:33:1"},"nodeType":"YulExpressionStatement","src":"2277:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"2212:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2221:6:1","type":""}],"src":"2187:129:1"},{"body":{"nodeType":"YulBlock","src":"2362:35:1","statements":[{"nodeType":"YulAssignment","src":"2372:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2388:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"2382:5:1"},"nodeType":"YulFunctionCall","src":"2382:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2372:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2355:6:1","type":""}],"src":"2322:75:1"},{"body":{"nodeType":"YulBlock","src":"2485:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2590:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2592:16:1"},"nodeType":"YulFunctionCall","src":"2592:18:1"},"nodeType":"YulExpressionStatement","src":"2592:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2562:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2570:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2559:2:1"},"nodeType":"YulFunctionCall","src":"2559:30:1"},"nodeType":"YulIf","src":"2556:56:1"},{"nodeType":"YulAssignment","src":"2622:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2634:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2642:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2630:3:1"},"nodeType":"YulFunctionCall","src":"2630:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2622:4:1"}]},{"nodeType":"YulAssignment","src":"2684:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2696:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2702:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2692:3:1"},"nodeType":"YulFunctionCall","src":"2692:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2684:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2469:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2480:4:1","type":""}],"src":"2403:311:1"},{"body":{"nodeType":"YulBlock","src":"2765:32:1","statements":[{"nodeType":"YulAssignment","src":"2775:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2786:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2775:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2747:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2757:7:1","type":""}],"src":"2720:77:1"},{"body":{"nodeType":"YulBlock","src":"2846:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2856:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2878:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2908:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"2886:21:1"},"nodeType":"YulFunctionCall","src":"2886:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2874:3:1"},"nodeType":"YulFunctionCall","src":"2874:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"2860:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"3025:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"3027:16:1"},"nodeType":"YulFunctionCall","src":"3027:18:1"},"nodeType":"YulExpressionStatement","src":"3027:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2968:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2980:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2965:2:1"},"nodeType":"YulFunctionCall","src":"2965:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3004:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"3016:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"3001:2:1"},"nodeType":"YulFunctionCall","src":"3001:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2962:2:1"},"nodeType":"YulFunctionCall","src":"2962:62:1"},"nodeType":"YulIf","src":"2959:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3063:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3067:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3056:6:1"},"nodeType":"YulFunctionCall","src":"3056:22:1"},"nodeType":"YulExpressionStatement","src":"3056:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"2832:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"2840:4:1","type":""}],"src":"2803:281:1"},{"body":{"nodeType":"YulBlock","src":"3118:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3135:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3138:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3128:6:1"},"nodeType":"YulFunctionCall","src":"3128:88:1"},"nodeType":"YulExpressionStatement","src":"3128:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3232:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"3235:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3225:6:1"},"nodeType":"YulFunctionCall","src":"3225:15:1"},"nodeType":"YulExpressionStatement","src":"3225:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3256:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3259:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3249:6:1"},"nodeType":"YulFunctionCall","src":"3249:15:1"},"nodeType":"YulExpressionStatement","src":"3249:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"3090:180:1"},{"body":{"nodeType":"YulBlock","src":"3365:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3382:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3385:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3375:6:1"},"nodeType":"YulFunctionCall","src":"3375:12:1"},"nodeType":"YulExpressionStatement","src":"3375:12:1"}]},"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulFunctionDefinition","src":"3276:117:1"},{"body":{"nodeType":"YulBlock","src":"3488:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3505:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3508:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3498:6:1"},"nodeType":"YulFunctionCall","src":"3498:12:1"},"nodeType":"YulExpressionStatement","src":"3498:12:1"}]},"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulFunctionDefinition","src":"3399:117:1"},{"body":{"nodeType":"YulBlock","src":"3611:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3628:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3631:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3621:6:1"},"nodeType":"YulFunctionCall","src":"3621:12:1"},"nodeType":"YulExpressionStatement","src":"3621:12:1"}]},"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulFunctionDefinition","src":"3522:117:1"},{"body":{"nodeType":"YulBlock","src":"3734:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3751:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3754:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3744:6:1"},"nodeType":"YulFunctionCall","src":"3744:12:1"},"nodeType":"YulExpressionStatement","src":"3744:12:1"}]},"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulFunctionDefinition","src":"3645:117:1"},{"body":{"nodeType":"YulBlock","src":"3816:54:1","statements":[{"nodeType":"YulAssignment","src":"3826:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3844:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"3851:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3840:3:1"},"nodeType":"YulFunctionCall","src":"3840:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3860:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"3856:3:1"},"nodeType":"YulFunctionCall","src":"3856:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"3836:3:1"},"nodeType":"YulFunctionCall","src":"3836:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"3826:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3799:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"3809:6:1","type":""}],"src":"3768:102:1"},{"body":{"nodeType":"YulBlock","src":"3919:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"3976:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3985:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3988:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3978:6:1"},"nodeType":"YulFunctionCall","src":"3978:12:1"},"nodeType":"YulExpressionStatement","src":"3978:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3942:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3967:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3949:17:1"},"nodeType":"YulFunctionCall","src":"3949:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"3939:2:1"},"nodeType":"YulFunctionCall","src":"3939:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"3932:6:1"},"nodeType":"YulFunctionCall","src":"3932:43:1"},"nodeType":"YulIf","src":"3929:63:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3912:5:1","type":""}],"src":"3876:122:1"}]},"contents":"{ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:3989:1","statements":[{"body":{"nodeType":"YulBlock","src":"47:35:1","statements":[{"nodeType":"YulAssignment","src":"57:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"73:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"67:5:1"},"nodeType":"YulFunctionCall","src":"67:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"57:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"40:6:1","type":""}],"src":"7:75:1"},{"body":{"nodeType":"YulBlock","src":"177:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"194:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"197:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"187:6:1"},"nodeType":"YulFunctionCall","src":"187:12:1"},"nodeType":"YulExpressionStatement","src":"187:12:1"}]},"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulFunctionDefinition","src":"88:117:1"},{"body":{"nodeType":"YulBlock","src":"300:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"317:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"320:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"310:6:1"},"nodeType":"YulFunctionCall","src":"310:12:1"},"nodeType":"YulExpressionStatement","src":"310:12:1"}]},"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulFunctionDefinition","src":"211:117:1"},{"body":{"nodeType":"YulBlock","src":"423:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"440:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"443:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"433:6:1"},"nodeType":"YulFunctionCall","src":"433:12:1"},"nodeType":"YulExpressionStatement","src":"433:12:1"}]},"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulFunctionDefinition","src":"334:117:1"},{"body":{"nodeType":"YulBlock","src":"505:54:1","statements":[{"nodeType":"YulAssignment","src":"515:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"533:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"540:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"529:3:1"},"nodeType":"YulFunctionCall","src":"529:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"549:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"545:3:1"},"nodeType":"YulFunctionCall","src":"545:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"525:3:1"},"nodeType":"YulFunctionCall","src":"525:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"515:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"488:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"498:6:1","type":""}],"src":"457:102:1"},{"body":{"nodeType":"YulBlock","src":"593:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"610:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"613:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"603:6:1"},"nodeType":"YulFunctionCall","src":"603:88:1"},"nodeType":"YulExpressionStatement","src":"603:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"707:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"710:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"700:6:1"},"nodeType":"YulFunctionCall","src":"700:15:1"},"nodeType":"YulExpressionStatement","src":"700:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"731:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"734:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"724:6:1"},"nodeType":"YulFunctionCall","src":"724:15:1"},"nodeType":"YulExpressionStatement","src":"724:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"565:180:1"},{"body":{"nodeType":"YulBlock","src":"794:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"804:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"826:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"856:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"834:21:1"},"nodeType":"YulFunctionCall","src":"834:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"822:3:1"},"nodeType":"YulFunctionCall","src":"822:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"808:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"973:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"975:16:1"},"nodeType":"YulFunctionCall","src":"975:18:1"},"nodeType":"YulExpressionStatement","src":"975:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"916:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"928:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"913:2:1"},"nodeType":"YulFunctionCall","src":"913:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"952:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"964:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"949:2:1"},"nodeType":"YulFunctionCall","src":"949:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"910:2:1"},"nodeType":"YulFunctionCall","src":"910:62:1"},"nodeType":"YulIf","src":"907:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1011:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"1015:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1004:6:1"},"nodeType":"YulFunctionCall","src":"1004:22:1"},"nodeType":"YulExpressionStatement","src":"1004:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"780:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"788:4:1","type":""}],"src":"751:281:1"},{"body":{"nodeType":"YulBlock","src":"1079:88:1","statements":[{"nodeType":"YulAssignment","src":"1089:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"1099:18:1"},"nodeType":"YulFunctionCall","src":"1099:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"1089:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"1148:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"1156:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"1128:19:1"},"nodeType":"YulFunctionCall","src":"1128:33:1"},"nodeType":"YulExpressionStatement","src":"1128:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"1063:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"1072:6:1","type":""}],"src":"1038:129:1"},{"body":{"nodeType":"YulBlock","src":"1255:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"1360:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"1362:16:1"},"nodeType":"YulFunctionCall","src":"1362:18:1"},"nodeType":"YulExpressionStatement","src":"1362:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"1332:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1340:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1329:2:1"},"nodeType":"YulFunctionCall","src":"1329:30:1"},"nodeType":"YulIf","src":"1326:56:1"},{"nodeType":"YulAssignment","src":"1392:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"1404:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1412:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"1400:3:1"},"nodeType":"YulFunctionCall","src":"1400:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"1392:4:1"}]},{"nodeType":"YulAssignment","src":"1454:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"1466:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"1472:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1462:3:1"},"nodeType":"YulFunctionCall","src":"1462:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"1454:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"1239:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"1250:4:1","type":""}],"src":"1173:311:1"},{"body":{"nodeType":"YulBlock","src":"1579:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1596:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1599:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1589:6:1"},"nodeType":"YulFunctionCall","src":"1589:12:1"},"nodeType":"YulExpressionStatement","src":"1589:12:1"}]},"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulFunctionDefinition","src":"1490:117:1"},{"body":{"nodeType":"YulBlock","src":"1658:32:1","statements":[{"nodeType":"YulAssignment","src":"1668:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"1679:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"1668:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1640:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"1650:7:1","type":""}],"src":"1613:77:1"},{"body":{"nodeType":"YulBlock","src":"1739:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"1796:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1805:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1808:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1798:6:1"},"nodeType":"YulFunctionCall","src":"1798:12:1"},"nodeType":"YulExpressionStatement","src":"1798:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1762:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1787:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1769:17:1"},"nodeType":"YulFunctionCall","src":"1769:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"1759:2:1"},"nodeType":"YulFunctionCall","src":"1759:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"1752:6:1"},"nodeType":"YulFunctionCall","src":"1752:43:1"},"nodeType":"YulIf","src":"1749:63:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1732:5:1","type":""}],"src":"1696:122:1"},{"body":{"nodeType":"YulBlock","src":"1876:87:1","statements":[{"nodeType":"YulAssignment","src":"1886:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1908:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1895:12:1"},"nodeType":"YulFunctionCall","src":"1895:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1886:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1951:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1924:26:1"},"nodeType":"YulFunctionCall","src":"1924:33:1"},"nodeType":"YulExpressionStatement","src":"1924:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1854:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1862:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1870:5:1","type":""}],"src":"1824:139:1"},{"body":{"nodeType":"YulBlock","src":"2088:608:1","statements":[{"nodeType":"YulAssignment","src":"2098:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2180:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"2123:56:1"},"nodeType":"YulFunctionCall","src":"2123:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"2107:15:1"},"nodeType":"YulFunctionCall","src":"2107:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"2098:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"2197:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"2208:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"2201:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"2230:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"2237:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2223:6:1"},"nodeType":"YulFunctionCall","src":"2223:21:1"},"nodeType":"YulExpressionStatement","src":"2223:21:1"},{"nodeType":"YulAssignment","src":"2253:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"2264:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"2271:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2260:3:1"},"nodeType":"YulFunctionCall","src":"2260:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"2253:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"2286:44:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"2304:6:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2316:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2324:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2312:3:1"},"nodeType":"YulFunctionCall","src":"2312:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2300:3:1"},"nodeType":"YulFunctionCall","src":"2300:30:1"},"variables":[{"name":"srcEnd","nodeType":"YulTypedName","src":"2290:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"2358:103:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulIdentifier","src":"2372:77:1"},"nodeType":"YulFunctionCall","src":"2372:79:1"},"nodeType":"YulExpressionStatement","src":"2372:79:1"}]},"condition":{"arguments":[{"name":"srcEnd","nodeType":"YulIdentifier","src":"2345:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"2353:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2342:2:1"},"nodeType":"YulFunctionCall","src":"2342:15:1"},"nodeType":"YulIf","src":"2339:122:1"},{"body":{"nodeType":"YulBlock","src":"2546:144:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2561:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"2579:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"2565:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"2603:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"2629:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"2641:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"2608:20:1"},"nodeType":"YulFunctionCall","src":"2608:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2596:6:1"},"nodeType":"YulFunctionCall","src":"2596:50:1"},"nodeType":"YulExpressionStatement","src":"2596:50:1"},{"nodeType":"YulAssignment","src":"2659:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"2670:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"2675:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2666:3:1"},"nodeType":"YulFunctionCall","src":"2666:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"2659:3:1"}]}]},"condition":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"2499:3:1"},{"name":"srcEnd","nodeType":"YulIdentifier","src":"2504:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"2496:2:1"},"nodeType":"YulFunctionCall","src":"2496:15:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"2512:25:1","statements":[{"nodeType":"YulAssignment","src":"2514:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"2525:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"2530:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2521:3:1"},"nodeType":"YulFunctionCall","src":"2521:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"2514:3:1"}]}]},"pre":{"nodeType":"YulBlock","src":"2474:21:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2476:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"2487:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"2480:3:1","type":""}]}]},"src":"2470:220:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"2058:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"2066:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"2074:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"2082:5:1","type":""}],"src":"1986:710:1"},{"body":{"nodeType":"YulBlock","src":"2796:293:1","statements":[{"body":{"nodeType":"YulBlock","src":"2845:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulIdentifier","src":"2847:77:1"},"nodeType":"YulFunctionCall","src":"2847:79:1"},"nodeType":"YulExpressionStatement","src":"2847:79:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"2824:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2832:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2820:3:1"},"nodeType":"YulFunctionCall","src":"2820:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"2839:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"2816:3:1"},"nodeType":"YulFunctionCall","src":"2816:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"2809:6:1"},"nodeType":"YulFunctionCall","src":"2809:35:1"},"nodeType":"YulIf","src":"2806:122:1"},{"nodeType":"YulVariableDeclaration","src":"2937:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"2964:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"2951:12:1"},"nodeType":"YulFunctionCall","src":"2951:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"2941:6:1","type":""}]},{"nodeType":"YulAssignment","src":"2980:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"3056:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"3064:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3052:3:1"},"nodeType":"YulFunctionCall","src":"3052:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"3071:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"3079:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"2989:62:1"},"nodeType":"YulFunctionCall","src":"2989:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"2980:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"2774:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"2782:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"2790:5:1","type":""}],"src":"2719:370:1"},{"body":{"nodeType":"YulBlock","src":"3186:448:1","statements":[{"body":{"nodeType":"YulBlock","src":"3232:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulIdentifier","src":"3234:77:1"},"nodeType":"YulFunctionCall","src":"3234:79:1"},"nodeType":"YulExpressionStatement","src":"3234:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"3207:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"3216:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"3203:3:1"},"nodeType":"YulFunctionCall","src":"3203:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"3228:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"3199:3:1"},"nodeType":"YulFunctionCall","src":"3199:32:1"},"nodeType":"YulIf","src":"3196:119:1"},{"nodeType":"YulBlock","src":"3325:302:1","statements":[{"nodeType":"YulVariableDeclaration","src":"3340:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"3371:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"3382:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3367:3:1"},"nodeType":"YulFunctionCall","src":"3367:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"3354:12:1"},"nodeType":"YulFunctionCall","src":"3354:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"3344:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"3432:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulIdentifier","src":"3434:77:1"},"nodeType":"YulFunctionCall","src":"3434:79:1"},"nodeType":"YulExpressionStatement","src":"3434:79:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"3404:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"3412:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"3401:2:1"},"nodeType":"YulFunctionCall","src":"3401:30:1"},"nodeType":"YulIf","src":"3398:117:1"},{"nodeType":"YulAssignment","src":"3529:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"3589:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"3600:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3585:3:1"},"nodeType":"YulFunctionCall","src":"3585:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"3609:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"3539:45:1"},"nodeType":"YulFunctionCall","src":"3539:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"3529:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"3156:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"3167:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"3179:6:1","type":""}],"src":"3095:539:1"},{"body":{"nodeType":"YulBlock","src":"3705:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"3722:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3745:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3727:17:1"},"nodeType":"YulFunctionCall","src":"3727:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3715:6:1"},"nodeType":"YulFunctionCall","src":"3715:37:1"},"nodeType":"YulExpressionStatement","src":"3715:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3693:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"3700:3:1","type":""}],"src":"3640:118:1"},{"body":{"nodeType":"YulBlock","src":"3862:124:1","statements":[{"nodeType":"YulAssignment","src":"3872:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"3884:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"3895:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3880:3:1"},"nodeType":"YulFunctionCall","src":"3880:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"3872:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"3952:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"3965:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"3976:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3961:3:1"},"nodeType":"YulFunctionCall","src":"3961:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"3908:43:1"},"nodeType":"YulFunctionCall","src":"3908:71:1"},"nodeType":"YulExpressionStatement","src":"3908:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"3834:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"3846:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"3857:4:1","type":""}],"src":"3764:222:1"}]},"contents":"{ + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() { + revert(0, 0) + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function allocate_memory(size) -> memPtr { + memPtr := allocate_unbounded() + finalize_allocation(memPtr, size) + } + + 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 revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() { + revert(0, 0) + } + + function cleanup_t_uint256(value) -> cleaned { + cleaned := value + } + + function validator_revert_t_uint256(value) { + if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) } + } + + function abi_decode_t_uint256(offset, end) -> value { + value := calldataload(offset) + validator_revert_t_uint256(value) + } // uint256[] function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { @@ -8,18 +74,17 @@ mstore(array, length) dst := add(array, 0x20) - let src := offset - if gt(add(src, mul(length, 0x20)), end) { + let srcEnd := add(offset, mul(length, 0x20)) + if gt(srcEnd, end) { revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() } - for { let i := 0 } lt(i, length) { i := add(i, 1) } + for { let src := offset } lt(src, srcEnd) { src := add(src, 0x20) } { let elementPos := src mstore(dst, abi_decode_t_uint256(elementPos, end)) dst := add(dst, 0x20) - src := add(src, 0x20) } } @@ -30,11 +95,6 @@ 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_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } @@ -59,67 +119,6 @@ } - function allocate_memory(size) -> memPtr { - memPtr := allocate_unbounded() - finalize_allocation(memPtr, size) - } - - function allocate_unbounded() -> memPtr { - memPtr := mload(64) - } - - 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 finalize_allocation(memPtr, size) { - let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) - // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) - } - - function panic_error_0x41() { - mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) - mstore(4, 0x41) - revert(0, 0x24) - } - - function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() { - revert(0, 0) - } - - function revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() { - revert(0, 0) - } - - function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function round_up_to_mul_of_32(value) -> result { - result := and(add(value, 31), not(31)) - } - - 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 diff --git a/test/cmdlineTests/standard_invalid_option_no_optimize_yul/args b/test/cmdlineTests/standard_invalid_option_no_optimize_yul/args new file mode 100644 index 000000000..de395419b --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_no_optimize_yul/args @@ -0,0 +1 @@ +--no-optimize-yul diff --git a/test/cmdlineTests/standard_invalid_option_no_optimize_yul/err b/test/cmdlineTests/standard_invalid_option_no_optimize_yul/err new file mode 100644 index 000000000..7493f5926 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_no_optimize_yul/err @@ -0,0 +1 @@ +Option --no-optimize-yul is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/standard_invalid_option_no_optimize_yul/exit b/test/cmdlineTests/standard_invalid_option_no_optimize_yul/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_no_optimize_yul/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/standard_invalid_option_no_optimize_yul/input.json b/test/cmdlineTests/standard_invalid_option_no_optimize_yul/input.json new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/standard_invalid_option_optimize/args b/test/cmdlineTests/standard_invalid_option_optimize/args new file mode 100644 index 000000000..0ba259dbf --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize/args @@ -0,0 +1 @@ +--optimize diff --git a/test/cmdlineTests/standard_invalid_option_optimize/err b/test/cmdlineTests/standard_invalid_option_optimize/err new file mode 100644 index 000000000..4b86fcfcd --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize/err @@ -0,0 +1 @@ +Option --optimize is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/standard_invalid_option_optimize/exit b/test/cmdlineTests/standard_invalid_option_optimize/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/standard_invalid_option_optimize/input.json b/test/cmdlineTests/standard_invalid_option_optimize/input.json new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/standard_invalid_option_optimize_runs/args b/test/cmdlineTests/standard_invalid_option_optimize_runs/args new file mode 100644 index 000000000..c9d29f534 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize_runs/args @@ -0,0 +1 @@ +--optimize-runs 1000 diff --git a/test/cmdlineTests/standard_invalid_option_optimize_runs/err b/test/cmdlineTests/standard_invalid_option_optimize_runs/err new file mode 100644 index 000000000..624b8f62e --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize_runs/err @@ -0,0 +1 @@ +Option --optimize-runs is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/standard_invalid_option_optimize_runs/exit b/test/cmdlineTests/standard_invalid_option_optimize_runs/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize_runs/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/standard_invalid_option_optimize_runs/input.json b/test/cmdlineTests/standard_invalid_option_optimize_runs/input.json new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/standard_invalid_option_optimize_yul/args b/test/cmdlineTests/standard_invalid_option_optimize_yul/args new file mode 100644 index 000000000..9625ac425 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize_yul/args @@ -0,0 +1 @@ +--optimize-yul diff --git a/test/cmdlineTests/standard_invalid_option_optimize_yul/err b/test/cmdlineTests/standard_invalid_option_optimize_yul/err new file mode 100644 index 000000000..5efb6abf8 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize_yul/err @@ -0,0 +1 @@ +Option --optimize-yul is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/standard_invalid_option_optimize_yul/exit b/test/cmdlineTests/standard_invalid_option_optimize_yul/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_optimize_yul/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/standard_invalid_option_optimize_yul/input.json b/test/cmdlineTests/standard_invalid_option_optimize_yul/input.json new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/standard_invalid_option_yul_optimizations/args b/test/cmdlineTests/standard_invalid_option_yul_optimizations/args new file mode 100644 index 000000000..f72079a37 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_yul_optimizations/args @@ -0,0 +1 @@ +--yul-optimizations a diff --git a/test/cmdlineTests/standard_invalid_option_yul_optimizations/err b/test/cmdlineTests/standard_invalid_option_yul_optimizations/err new file mode 100644 index 000000000..779736122 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_yul_optimizations/err @@ -0,0 +1 @@ +Option --yul-optimizations is only valid in compiler and assembler modes. diff --git a/test/cmdlineTests/standard_invalid_option_yul_optimizations/exit b/test/cmdlineTests/standard_invalid_option_yul_optimizations/exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/test/cmdlineTests/standard_invalid_option_yul_optimizations/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/standard_invalid_option_yul_optimizations/input.json b/test/cmdlineTests/standard_invalid_option_yul_optimizations/input.json new file mode 100644 index 000000000..e69de29bb diff --git a/test/cmdlineTests/standard_irOptimized_requested/output.json b/test/cmdlineTests/standard_irOptimized_requested/output.json index 8b9ada71a..c636adcc4 100644 --- a/test/cmdlineTests/standard_irOptimized_requested/output.json +++ b/test/cmdlineTests/standard_irOptimized_requested/output.json @@ -5,10 +5,10 @@ * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_7\" { code { - /// @src 0:79:121 + /// @src 0:79:121 \"contract C { function f() public pure {} }\" mstore(64, 128) if callvalue() { @@ -20,14 +20,15 @@ object \"C_7\" { return(_1, datasize(\"C_7_deployed\")) function allocate_unbounded() -> memPtr { memPtr := mload(64) } - function constructor_C_7() - { } function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + function constructor_C_7() + { } } + /// @use-src 0:\"A\" object \"C_7_deployed\" { code { - /// @src 0:79:121 + /// @src 0:79:121 \"contract C { function f() public pure {} }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -48,6 +49,14 @@ object \"C_7\" { } if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue + { newValue := shr(224, value) } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + { revert(0, 0) } function abi_decode_tuple_(headStart, dataEnd) { if slt(sub(dataEnd, headStart), 0) @@ -57,20 +66,11 @@ object \"C_7\" { } function abi_encode_tuple__to__fromStack(headStart) -> tail { tail := add(headStart, 0) } - function allocate_unbounded() -> memPtr - { memPtr := mload(64) } - /// @src 0:92:119 - function fun_f_6() - { } - /// @src 0:79:121 function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) } - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() - { revert(0, 0) } - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() - { revert(0, 0) } - function shift_right_224_unsigned(value) -> newValue - { newValue := shr(224, value) } + /// @ast-id 6 @src 0:92:119 \"function f() public pure {}\" + function fun_f_6() + { } } data \".metadata\" hex\"\" } diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 2eb815d33..2c01878f3 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -6,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_7\" { code { - /// @src 0:79:121 + /// @src 0:79:121 \"contract C { function f() public pure {} }\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,22 +24,23 @@ object \"C_7\" { memPtr := mload(64) } - /// @src 0:79:121 - function constructor_C_7() { - - /// @src 0:79:121 - - } - /// @src 0:79:121 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:79:121 \"contract C { function f() public pure {} }\" + function constructor_C_7() { + + /// @src 0:79:121 \"contract C { function f() public pure {} }\" + + } + /// @src 0:79:121 \"contract C { function f() public pure {} }\" + } + /// @use-src 0:\"A\" object \"C_7_deployed\" { code { - /// @src 0:79:121 + /// @src 0:79:121 \"contract C { function f() public pure {} }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -64,13 +65,10 @@ object \"C_7\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() - function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + function shift_right_224_unsigned(value) -> newValue { + newValue := - } - - function abi_encode_tuple__to__fromStack(headStart ) -> tail { - tail := add(headStart, 0) + shr(224, value) } @@ -78,16 +76,6 @@ object \"C_7\" { memPtr := mload(64) } - /// @src 0:92:119 - function fun_f_6() { - - } - /// @src 0:79:121 - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } @@ -96,13 +84,27 @@ object \"C_7\" { revert(0, 0) } - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + /// @ast-id 6 + /// @src 0:92:119 \"function f() public pure {}\" + function fun_f_6() { + + } + /// @src 0:79:121 \"contract C { function f() public pure {} }\" + } data \".metadata\" hex\"\" diff --git a/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json index 0ecf87bd7..fc4b9b9da 100644 --- a/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:12:7: | 12 | \t\t\t\t\t\tassert(x > 0); @@ -16,10 +16,10 @@ test.f(0, 1) ","message":"CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json b/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json index d5491b274..ad6418636 100644 --- a/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json @@ -126,13 +126,13 @@ "}},"errors":[{"component":"general","errorCode":"4281","formattedMessage":"Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:10:7: | 10 | \t\t\t\t\t\t2 / x; @@ -141,22 +141,22 @@ test.f(0, 1) ","message":"CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":216,"file":"A","start":211},"type":"Warning"},{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":216,"file":"A","start":211},"type":"Warning"},{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:12:7: | 12 | \t\t\t\t\t\tassert(x > 0); @@ -165,22 +165,22 @@ test.f(0, 1) ","message":"CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"},{"component":"general","errorCode":"2529","formattedMessage":"Warning: CHC: Empty array \"pop\" happens here. +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"},{"component":"general","errorCode":"2529","formattedMessage":"Warning: CHC: Empty array \"pop\" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:13:7: | 13 | \t\t\t\t\t\tarr.pop(); @@ -189,22 +189,22 @@ test.f(0, 1) ","message":"CHC: Empty array \"pop\" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":275,"file":"A","start":266},"type":"Warning"},{"component":"general","errorCode":"6368","formattedMessage":"Warning: CHC: Out of bounds access happens here. +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":275,"file":"A","start":266},"type":"Warning"},{"component":"general","errorCode":"6368","formattedMessage":"Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:14:7: | 14 | \t\t\t\t\t\tarr[x]; @@ -213,13 +213,13 @@ test.f(0, 1) ","message":"CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":289,"file":"A","start":283},"type":"Warning"},{"component":"general","errorCode":"6838","formattedMessage":"Warning: BMC: Condition is always true. +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":289,"file":"A","start":283},"type":"Warning"},{"component":"general","errorCode":"6838","formattedMessage":"Warning: BMC: Condition is always true. --> A:7:15: | 7 | \t\t\t\t\t\trequire(x >= 0); diff --git a/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json index 4049582dc..bcdf48e50 100644 --- a/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"4281","formattedMessage":"Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:10:7: | 10 | \t\t\t\t\t\t2 / x; @@ -16,22 +16,22 @@ test.f(0, 1) ","message":"CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":216,"file":"A","start":211},"type":"Warning"},{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":216,"file":"A","start":211},"type":"Warning"},{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:12:7: | 12 | \t\t\t\t\t\tassert(x > 0); @@ -40,22 +40,22 @@ test.f(0, 1) ","message":"CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"},{"component":"general","errorCode":"2529","formattedMessage":"Warning: CHC: Empty array \"pop\" happens here. +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"},{"component":"general","errorCode":"2529","formattedMessage":"Warning: CHC: Empty array \"pop\" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:13:7: | 13 | \t\t\t\t\t\tarr.pop(); @@ -64,22 +64,22 @@ test.f(0, 1) ","message":"CHC: Empty array \"pop\" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":275,"file":"A","start":266},"type":"Warning"},{"component":"general","errorCode":"6368","formattedMessage":"Warning: CHC: Out of bounds access happens here. +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":275,"file":"A","start":266},"type":"Warning"},{"component":"general","errorCode":"6368","formattedMessage":"Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:14:7: | 14 | \t\t\t\t\t\tarr[x]; @@ -88,10 +88,10 @@ test.f(0, 1) ","message":"CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":289,"file":"A","start":283},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":289,"file":"A","start":283},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json index eca84d4b0..bd8b26c12 100644 --- a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"4281","formattedMessage":"Warning: CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:10:7: | 10 | \t\t\t\t\t\t2 / x; @@ -16,10 +16,10 @@ test.f(0, 1) ","message":"CHC: Division by zero happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":216,"file":"A","start":211},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":216,"file":"A","start":211},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json index bdaa14b50..436942c45 100644 --- a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"6368","formattedMessage":"Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:14:7: | 14 | \t\t\t\t\t\tarr[x]; @@ -16,10 +16,10 @@ test.f(0, 1) ","message":"CHC: Out of bounds access happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":289,"file":"A","start":283},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":289,"file":"A","start":283},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json index 794a11b04..5a5410b0c 100644 --- a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"4984","formattedMessage":"Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> A:9:7: | 9 | \t\t\t\t\t\tx + type(uint).max; @@ -16,10 +16,10 @@ test.f(0, 2) ","message":"CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2)","severity":"warning","sourceLocation":{"end":203,"file":"A","start":185},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 2)","severity":"warning","sourceLocation":{"end":203,"file":"A","start":185},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json index b6e891d6c..87edd534d 100644 --- a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"2529","formattedMessage":"Warning: CHC: Empty array \"pop\" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:13:7: | 13 | \t\t\t\t\t\tarr.pop(); @@ -16,10 +16,10 @@ test.f(0, 1) ","message":"CHC: Empty array \"pop\" happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":275,"file":"A","start":266},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":275,"file":"A","start":266},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json index 7a22d148a..096bb971b 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"3944","formattedMessage":"Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> A:8:7: | 8 | \t\t\t\t\t\t--x; @@ -16,10 +16,10 @@ test.f(0, 0) ","message":"CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0)","severity":"warning","sourceLocation":{"end":177,"file":"A","start":174},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 0)","severity":"warning","sourceLocation":{"end":177,"file":"A","start":174},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json index 472866447..3ea910cba 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"3944","formattedMessage":"Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> A:8:7: | 8 | \t\t\t\t\t\t--x; @@ -16,22 +16,22 @@ test.f(0, 0) ","message":"CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0)","severity":"warning","sourceLocation":{"end":177,"file":"A","start":174},"type":"Warning"},{"component":"general","errorCode":"4984","formattedMessage":"Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +test.f(0x0, 0)","severity":"warning","sourceLocation":{"end":177,"file":"A","start":174},"type":"Warning"},{"component":"general","errorCode":"4984","formattedMessage":"Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> A:9:7: | 9 | \t\t\t\t\t\tx + type(uint).max; @@ -40,22 +40,22 @@ test.f(0, 2) ","message":"CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2)","severity":"warning","sourceLocation":{"end":203,"file":"A","start":185},"type":"Warning"},{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. +test.f(0x0, 2)","severity":"warning","sourceLocation":{"end":203,"file":"A","start":185},"type":"Warning"},{"component":"general","errorCode":"6328","formattedMessage":"Warning: CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1) +test.f(0x0, 1) --> A:12:7: | 12 | \t\t\t\t\t\tassert(x > 0); @@ -64,10 +64,10 @@ test.f(0, 1) ","message":"CHC: Assertion violation happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 1)","severity":"warning","sourceLocation":{"end":258,"file":"A","start":245},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json index d4a502b4b..88ed4136b 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json @@ -1,13 +1,13 @@ {"errors":[{"component":"general","errorCode":"3944","formattedMessage":"Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0) +test.f(0x0, 0) --> A:8:7: | 8 | \t\t\t\t\t\t--x; @@ -16,22 +16,22 @@ test.f(0, 0) ","message":"CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 0 Transaction trace: test.constructor() State: arr = [] -test.f(0, 0)","severity":"warning","sourceLocation":{"end":177,"file":"A","start":174},"type":"Warning"},{"component":"general","errorCode":"4984","formattedMessage":"Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. +test.f(0x0, 0)","severity":"warning","sourceLocation":{"end":177,"file":"A","start":174},"type":"Warning"},{"component":"general","errorCode":"4984","formattedMessage":"Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2) +test.f(0x0, 2) --> A:9:7: | 9 | \t\t\t\t\t\tx + type(uint).max; @@ -40,10 +40,10 @@ test.f(0, 2) ","message":"CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] -a = 0 +a = 0x0 x = 1 Transaction trace: test.constructor() State: arr = [] -test.f(0, 2)","severity":"warning","sourceLocation":{"end":203,"file":"A","start":185},"type":"Warning"}],"sources":{"A":{"id":0}}} +test.f(0x0, 2)","severity":"warning","sourceLocation":{"end":203,"file":"A","start":185},"type":"Warning"}],"sources":{"A":{"id":0}}} diff --git a/test/cmdlineTests/standard_only_ast_requested/output.json b/test/cmdlineTests/standard_only_ast_requested/output.json index 31e349c4d..0177dd7fc 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","fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nameLocation":"68:1:0","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nameLocation":"81:1:0","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","usedErrors":[]}],"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":[],"canonicalName":"C","contractDependencies":[],"contractKind":"contract","fullyImplemented":true,"id":6,"linearizedBaseContracts":[6],"name":"C","nameLocation":"68:1:0","nodeType":"ContractDefinition","nodes":[{"body":{"id":4,"nodeType":"Block","src":"97:2:0","statements":[]},"functionSelector":"26121ff0","id":5,"implemented":true,"kind":"function","modifiers":[],"name":"f","nameLocation":"81:1:0","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","usedErrors":[]}],"src":"36:65:0"},"id":0}}} diff --git a/test/cmdlineTests/standard_optimizer_generatedSources/output.json b/test/cmdlineTests/standard_optimizer_generatedSources/output.json index 439101cc4..940450827 100644 --- a/test/cmdlineTests/standard_optimizer_generatedSources/output.json +++ b/test/cmdlineTests/standard_optimizer_generatedSources/output.json @@ -1,5 +1,11 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:1456:1","statements":[{"nodeType":"YulBlock","src":"6:3:1","statements":[]},{"body":{"nodeType":"YulBlock","src":"109:1031: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:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"185:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"188:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"178:6:1"},"nodeType":"YulFunctionCall","src":"178:12:1"},"nodeType":"YulExpressionStatement","src":"178:12: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:52:1"},{"nodeType":"YulVariableDeclaration","src":"201:37:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"228:9:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"215:12:1"},"nodeType":"YulFunctionCall","src":"215:23:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"205:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"247:28:1","value":{"kind":"number","nodeType":"YulLiteral","src":"257:18:1","type":"","value":"0xffffffffffffffff"},"variables":[{"name":"_2","nodeType":"YulTypedName","src":"251:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"302:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"311:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"314:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"304:6:1"},"nodeType":"YulFunctionCall","src":"304:12:1"},"nodeType":"YulExpressionStatement","src":"304:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"290:6:1"},{"name":"_2","nodeType":"YulIdentifier","src":"298:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"287:2:1"},"nodeType":"YulFunctionCall","src":"287:14:1"},"nodeType":"YulIf","src":"284:34:1"},{"nodeType":"YulVariableDeclaration","src":"327:32:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"341:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"352:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"337:3:1"},"nodeType":"YulFunctionCall","src":"337:22:1"},"variables":[{"name":"_3","nodeType":"YulTypedName","src":"331:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"407:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"416:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"419:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"409:6:1"},"nodeType":"YulFunctionCall","src":"409:12:1"},"nodeType":"YulExpressionStatement","src":"409:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"386:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"390:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"382:3:1"},"nodeType":"YulFunctionCall","src":"382:13:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"397:7:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"378:3:1"},"nodeType":"YulFunctionCall","src":"378:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"371:6:1"},"nodeType":"YulFunctionCall","src":"371:35:1"},"nodeType":"YulIf","src":"368:55:1"},{"nodeType":"YulVariableDeclaration","src":"432:26:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"455:2:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"442:12:1"},"nodeType":"YulFunctionCall","src":"442:16:1"},"variables":[{"name":"_4","nodeType":"YulTypedName","src":"436:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"481:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"483:16:1"},"nodeType":"YulFunctionCall","src":"483:18:1"},"nodeType":"YulExpressionStatement","src":"483:18:1"}]},"condition":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"473:2:1"},{"name":"_2","nodeType":"YulIdentifier","src":"477:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"470:2:1"},"nodeType":"YulFunctionCall","src":"470:10:1"},"nodeType":"YulIf","src":"467:36:1"},{"nodeType":"YulVariableDeclaration","src":"512:20:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"526:1:1","type":"","value":"5"},{"name":"_4","nodeType":"YulIdentifier","src":"529:2:1"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"522:3:1"},"nodeType":"YulFunctionCall","src":"522:10:1"},"variables":[{"name":"_5","nodeType":"YulTypedName","src":"516:2:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"541:23:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"561:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"555:5:1"},"nodeType":"YulFunctionCall","src":"555:9:1"},"variables":[{"name":"memPtr","nodeType":"YulTypedName","src":"545:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"573:56:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"595:6:1"},{"arguments":[{"arguments":[{"name":"_5","nodeType":"YulIdentifier","src":"611:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"615:2:1","type":"","value":"63"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"607:3:1"},"nodeType":"YulFunctionCall","src":"607:11:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"624:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"620:3:1"},"nodeType":"YulFunctionCall","src":"620:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"603:3:1"},"nodeType":"YulFunctionCall","src":"603:25:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"591:3:1"},"nodeType":"YulFunctionCall","src":"591:38:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"577:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"688:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"690:16:1"},"nodeType":"YulFunctionCall","src":"690:18:1"},"nodeType":"YulExpressionStatement","src":"690:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"647:10:1"},{"name":"_2","nodeType":"YulIdentifier","src":"659:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"644:2:1"},"nodeType":"YulFunctionCall","src":"644:18:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"667:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"679:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"664:2:1"},"nodeType":"YulFunctionCall","src":"664:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"641:2:1"},"nodeType":"YulFunctionCall","src":"641:46:1"},"nodeType":"YulIf","src":"638:72:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"726:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"730:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"719:6:1"},"nodeType":"YulFunctionCall","src":"719:22:1"},"nodeType":"YulExpressionStatement","src":"719:22:1"},{"nodeType":"YulVariableDeclaration","src":"750:17:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"761:6:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"754:3:1","type":""}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"783:6:1"},{"name":"_4","nodeType":"YulIdentifier","src":"791:2:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"776:6:1"},"nodeType":"YulFunctionCall","src":"776:18:1"},"nodeType":"YulExpressionStatement","src":"776:18:1"},{"nodeType":"YulAssignment","src":"803:22:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"814:6:1"},{"name":"_1","nodeType":"YulIdentifier","src":"822:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"810:3:1"},"nodeType":"YulFunctionCall","src":"810:15:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"803:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"834:22:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"849:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"853:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"845:3:1"},"nodeType":"YulFunctionCall","src":"845:11:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"838:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"902:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"911:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"914:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"904:6:1"},"nodeType":"YulFunctionCall","src":"904:12:1"},"nodeType":"YulExpressionStatement","src":"904:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"879:2:1"},{"name":"_5","nodeType":"YulIdentifier","src":"883:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"875:3:1"},"nodeType":"YulFunctionCall","src":"875:11:1"},{"name":"_1","nodeType":"YulIdentifier","src":"888:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"871:3:1"},"nodeType":"YulFunctionCall","src":"871:20:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"893:7:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"868:2:1"},"nodeType":"YulFunctionCall","src":"868:33:1"},"nodeType":"YulIf","src":"865:53:1"},{"nodeType":"YulVariableDeclaration","src":"927:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"936:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"931:1:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"991:118:1","statements":[{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1012:3:1"},{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1030:3:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1017:12:1"},"nodeType":"YulFunctionCall","src":"1017:17:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1005:6:1"},"nodeType":"YulFunctionCall","src":"1005:30:1"},"nodeType":"YulExpressionStatement","src":"1005:30:1"},{"nodeType":"YulAssignment","src":"1048:19:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1059:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1064:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1055:3:1"},"nodeType":"YulFunctionCall","src":"1055:12:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"1048:3:1"}]},{"nodeType":"YulAssignment","src":"1080:19:1","value":{"arguments":[{"name":"src","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":"src","nodeType":"YulIdentifier","src":"1080:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"957:1:1"},{"name":"_4","nodeType":"YulIdentifier","src":"960:2:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"954:2:1"},"nodeType":"YulFunctionCall","src":"954:9:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"964:18:1","statements":[{"nodeType":"YulAssignment","src":"966:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"975:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"978:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"971:3:1"},"nodeType":"YulFunctionCall","src":"971:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"966:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"950:3:1","statements":[]},"src":"946:163:1"},{"nodeType":"YulAssignment","src":"1118:16:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"1128:6:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1118: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:1126:1"},{"body":{"nodeType":"YulBlock","src":"1246:76:1","statements":[{"nodeType":"YulAssignment","src":"1256:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1268:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1279:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1264:3:1"},"nodeType":"YulFunctionCall","src":"1264:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1256:4:1"}]},{"expression":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1298:9:1"},{"name":"value0","nodeType":"YulIdentifier","src":"1309:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1291:6:1"},"nodeType":"YulFunctionCall","src":"1291:25:1"},"nodeType":"YulExpressionStatement","src":"1291:25:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1215:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1226:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1237:4:1","type":""}],"src":"1145:177:1"},{"body":{"nodeType":"YulBlock","src":"1359:95:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1376:1:1","type":"","value":"0"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1383:3:1","type":"","value":"224"},{"kind":"number","nodeType":"YulLiteral","src":"1388:10:1","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"1379:3:1"},"nodeType":"YulFunctionCall","src":"1379:20:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1369:6:1"},"nodeType":"YulFunctionCall","src":"1369:31:1"},"nodeType":"YulExpressionStatement","src":"1369:31:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1416:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"1419:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1409:6:1"},"nodeType":"YulFunctionCall","src":"1409:15:1"},"nodeType":"YulExpressionStatement","src":"1409:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1440:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1443:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1433:6:1"},"nodeType":"YulFunctionCall","src":"1433:15:1"},"nodeType":"YulExpressionStatement","src":"1433:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"1327:127:1"}]},"contents":"{ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:1445:1","statements":[{"nodeType":"YulBlock","src":"6:3:1","statements":[]},{"body":{"nodeType":"YulBlock","src":"46:95:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"63:1:1","type":"","value":"0"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"70:3:1","type":"","value":"224"},{"kind":"number","nodeType":"YulLiteral","src":"75:10:1","type":"","value":"0x4e487b71"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"66:3:1"},"nodeType":"YulFunctionCall","src":"66:20:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"56:6:1"},"nodeType":"YulFunctionCall","src":"56:31:1"},"nodeType":"YulExpressionStatement","src":"56:31:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"103:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"106:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"96:6:1"},"nodeType":"YulFunctionCall","src":"96:15:1"},"nodeType":"YulExpressionStatement","src":"96:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"127:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"130:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"120:6:1"},"nodeType":"YulFunctionCall","src":"120:15:1"},"nodeType":"YulExpressionStatement","src":"120:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"14:127:1"},{"body":{"nodeType":"YulBlock","src":"241:1020:1","statements":[{"nodeType":"YulVariableDeclaration","src":"251:12:1","value":{"kind":"number","nodeType":"YulLiteral","src":"261:2:1","type":"","value":"32"},"variables":[{"name":"_1","nodeType":"YulTypedName","src":"255:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"308:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"317:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"320:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"310:6:1"},"nodeType":"YulFunctionCall","src":"310:12:1"},"nodeType":"YulExpressionStatement","src":"310:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"283:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"292:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"279:3:1"},"nodeType":"YulFunctionCall","src":"279:23:1"},{"name":"_1","nodeType":"YulIdentifier","src":"304:2:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"275:3:1"},"nodeType":"YulFunctionCall","src":"275:32:1"},"nodeType":"YulIf","src":"272:52:1"},{"nodeType":"YulVariableDeclaration","src":"333:37:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"360:9:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"347:12:1"},"nodeType":"YulFunctionCall","src":"347:23:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"337:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"379:28:1","value":{"kind":"number","nodeType":"YulLiteral","src":"389:18:1","type":"","value":"0xffffffffffffffff"},"variables":[{"name":"_2","nodeType":"YulTypedName","src":"383:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"434:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"443:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"446:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"436:6:1"},"nodeType":"YulFunctionCall","src":"436:12:1"},"nodeType":"YulExpressionStatement","src":"436:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"422:6:1"},{"name":"_2","nodeType":"YulIdentifier","src":"430:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"419:2:1"},"nodeType":"YulFunctionCall","src":"419:14:1"},"nodeType":"YulIf","src":"416:34:1"},{"nodeType":"YulVariableDeclaration","src":"459:32:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"473:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"484:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"469:3:1"},"nodeType":"YulFunctionCall","src":"469:22:1"},"variables":[{"name":"_3","nodeType":"YulTypedName","src":"463:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"539:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"548:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"551:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"541:6:1"},"nodeType":"YulFunctionCall","src":"541:12:1"},"nodeType":"YulExpressionStatement","src":"541:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"518:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"522:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"514:3:1"},"nodeType":"YulFunctionCall","src":"514:13:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"529:7:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"510:3:1"},"nodeType":"YulFunctionCall","src":"510:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"503:6:1"},"nodeType":"YulFunctionCall","src":"503:35:1"},"nodeType":"YulIf","src":"500:55:1"},{"nodeType":"YulVariableDeclaration","src":"564:26:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"587:2:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"574:12:1"},"nodeType":"YulFunctionCall","src":"574:16:1"},"variables":[{"name":"_4","nodeType":"YulTypedName","src":"568:2:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"613:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"615:16:1"},"nodeType":"YulFunctionCall","src":"615:18:1"},"nodeType":"YulExpressionStatement","src":"615:18:1"}]},"condition":{"arguments":[{"name":"_4","nodeType":"YulIdentifier","src":"605:2:1"},{"name":"_2","nodeType":"YulIdentifier","src":"609:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"602:2:1"},"nodeType":"YulFunctionCall","src":"602:10:1"},"nodeType":"YulIf","src":"599:36:1"},{"nodeType":"YulVariableDeclaration","src":"644:20:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"658:1:1","type":"","value":"5"},{"name":"_4","nodeType":"YulIdentifier","src":"661:2:1"}],"functionName":{"name":"shl","nodeType":"YulIdentifier","src":"654:3:1"},"nodeType":"YulFunctionCall","src":"654:10:1"},"variables":[{"name":"_5","nodeType":"YulTypedName","src":"648:2:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"673:23:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"693:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"687:5:1"},"nodeType":"YulFunctionCall","src":"687:9:1"},"variables":[{"name":"memPtr","nodeType":"YulTypedName","src":"677:6:1","type":""}]},{"nodeType":"YulVariableDeclaration","src":"705:56:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"727:6:1"},{"arguments":[{"arguments":[{"name":"_5","nodeType":"YulIdentifier","src":"743:2:1"},{"kind":"number","nodeType":"YulLiteral","src":"747:2:1","type":"","value":"63"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"739:3:1"},"nodeType":"YulFunctionCall","src":"739:11:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"756:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"752:3:1"},"nodeType":"YulFunctionCall","src":"752:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"735:3:1"},"nodeType":"YulFunctionCall","src":"735:25:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"723:3:1"},"nodeType":"YulFunctionCall","src":"723:38:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"709:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"820:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"822:16:1"},"nodeType":"YulFunctionCall","src":"822:18:1"},"nodeType":"YulExpressionStatement","src":"822:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"779:10:1"},{"name":"_2","nodeType":"YulIdentifier","src":"791:2:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"776:2:1"},"nodeType":"YulFunctionCall","src":"776:18:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"799:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"811:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"796:2:1"},"nodeType":"YulFunctionCall","src":"796:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"773:2:1"},"nodeType":"YulFunctionCall","src":"773:46:1"},"nodeType":"YulIf","src":"770:72:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"858:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"862:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"851:6:1"},"nodeType":"YulFunctionCall","src":"851:22:1"},"nodeType":"YulExpressionStatement","src":"851:22:1"},{"nodeType":"YulVariableDeclaration","src":"882:17:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"893:6:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"886:3:1","type":""}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"915:6:1"},{"name":"_4","nodeType":"YulIdentifier","src":"923:2:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"908:6:1"},"nodeType":"YulFunctionCall","src":"908:18:1"},"nodeType":"YulExpressionStatement","src":"908:18:1"},{"nodeType":"YulAssignment","src":"935:22:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"946:6:1"},{"name":"_1","nodeType":"YulIdentifier","src":"954:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"942:3:1"},"nodeType":"YulFunctionCall","src":"942:15:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"935:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"966:34:1","value":{"arguments":[{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"988:2:1"},{"name":"_5","nodeType":"YulIdentifier","src":"992:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"984:3:1"},"nodeType":"YulFunctionCall","src":"984:11:1"},{"name":"_1","nodeType":"YulIdentifier","src":"997:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"980:3:1"},"nodeType":"YulFunctionCall","src":"980:20:1"},"variables":[{"name":"srcEnd","nodeType":"YulTypedName","src":"970:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1032:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1041:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1044:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1034:6:1"},"nodeType":"YulFunctionCall","src":"1034:12:1"},"nodeType":"YulExpressionStatement","src":"1034:12:1"}]},"condition":{"arguments":[{"name":"srcEnd","nodeType":"YulIdentifier","src":"1015:6:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1023:7:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1012:2:1"},"nodeType":"YulFunctionCall","src":"1012:19:1"},"nodeType":"YulIf","src":"1009:39:1"},{"nodeType":"YulVariableDeclaration","src":"1057:22:1","value":{"arguments":[{"name":"_3","nodeType":"YulIdentifier","src":"1072:2:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1076:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1068:3:1"},"nodeType":"YulFunctionCall","src":"1068:11:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"1061:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1144:86:1","statements":[{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1165:3:1"},{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1183:3:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1170:12:1"},"nodeType":"YulFunctionCall","src":"1170:17:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1158:6:1"},"nodeType":"YulFunctionCall","src":"1158:30:1"},"nodeType":"YulExpressionStatement","src":"1158:30:1"},{"nodeType":"YulAssignment","src":"1201:19:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"1212:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1217:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1208:3:1"},"nodeType":"YulFunctionCall","src":"1208:12:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"1201:3:1"}]}]},"condition":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1099:3:1"},{"name":"srcEnd","nodeType":"YulIdentifier","src":"1104:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"1096:2:1"},"nodeType":"YulFunctionCall","src":"1096:15:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"1112:23:1","statements":[{"nodeType":"YulAssignment","src":"1114:19:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"1125:3:1"},{"name":"_1","nodeType":"YulIdentifier","src":"1130:2:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1121:3:1"},"nodeType":"YulFunctionCall","src":"1121:12:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"1114:3:1"}]}]},"pre":{"nodeType":"YulBlock","src":"1092:3:1","statements":[]},"src":"1088:142:1"},{"nodeType":"YulAssignment","src":"1239:16:1","value":{"name":"memPtr","nodeType":"YulIdentifier","src":"1249:6:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1239:6:1"}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"207:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"218:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"230:6:1","type":""}],"src":"146:1115:1"},{"body":{"nodeType":"YulBlock","src":"1367:76:1","statements":[{"nodeType":"YulAssignment","src":"1377:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1389:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1400:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1385:3:1"},"nodeType":"YulFunctionCall","src":"1385:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1377:4:1"}]},{"expression":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1419:9:1"},{"name":"value0","nodeType":"YulIdentifier","src":"1430:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1412:6:1"},"nodeType":"YulFunctionCall","src":"1412:25:1"},"nodeType":"YulExpressionStatement","src":"1412:25:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1336:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1347:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1358:4:1","type":""}],"src":"1266:177:1"}]},"contents":"{ { } + function panic_error_0x41() + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } function abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr(headStart, dataEnd) -> value0 { let _1 := 32 @@ -19,14 +25,13 @@ let dst := memPtr mstore(memPtr, _4) dst := add(memPtr, _1) + let srcEnd := add(add(_3, _5), _1) + if gt(srcEnd, dataEnd) { revert(0, 0) } let src := add(_3, _1) - if gt(add(add(_3, _5), _1), dataEnd) { revert(0, 0) } - let i := 0 - for { } lt(i, _4) { i := add(i, 1) } + for { } lt(src, srcEnd) { src := add(src, _1) } { mstore(dst, calldataload(src)) dst := add(dst, _1) - src := add(src, _1) } value0 := memPtr } @@ -35,10 +40,4 @@ 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_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index dca052187..9ae88b90c 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -6,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_3\" { code { - /// @src 0:79:92 + /// @src 0:79:92 \"contract C {}\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,22 +24,23 @@ object \"C_3\" { memPtr := mload(64) } - /// @src 0:79:92 - function constructor_C_3() { - - /// @src 0:79:92 - - } - /// @src 0:79:92 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:79:92 \"contract C {}\" + function constructor_C_3() { + + /// @src 0:79:92 \"contract C {}\" + + } + /// @src 0:79:92 \"contract C {}\" + } + /// @use-src 0:\"A\" object \"C_3_deployed\" { code { - /// @src 0:79:92 + /// @src 0:79:92 \"contract C {}\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -52,6 +53,13 @@ object \"C_3\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + function allocate_unbounded() -> memPtr { memPtr := mload(64) } @@ -60,13 +68,6 @@ object \"C_3\" { revert(0, 0) } - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - - } - } data \".metadata\" hex\"\" @@ -82,10 +83,10 @@ object \"C_3\" { *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"D_16\" { code { - /// @src 0:93:146 + /// @src 0:93:146 \"contract D { function f() public { C c = new C(); } }\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -100,22 +101,23 @@ object \"D_16\" { memPtr := mload(64) } - /// @src 0:93:146 - function constructor_D_16() { - - /// @src 0:93:146 - - } - /// @src 0:93:146 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:93:146 \"contract D { function f() public { C c = new C(); } }\" + function constructor_D_16() { + + /// @src 0:93:146 \"contract D { function f() public { C c = new C(); } }\" + + } + /// @src 0:93:146 \"contract D { function f() public { C c = new C(); } }\" + } + /// @use-src 0:\"A\" object \"D_16_deployed\" { code { - /// @src 0:93:146 + /// @src 0:93:146 \"contract D { function f() public { C c = new C(); } }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -140,13 +142,10 @@ object \"D_16\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() - function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + function shift_right_224_unsigned(value) -> newValue { + newValue := - } - - function abi_encode_tuple__to__fromStack(headStart ) -> tail { - tail := add(headStart, 0) + shr(224, value) } @@ -154,36 +153,6 @@ object \"D_16\" { memPtr := mload(64) } - /// @src 0:106:144 - function fun_f_15() { - - /// @src 0:134:141 - let _1 := allocate_unbounded() - 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() } - - /// @src 0:128:141 - let var_c_8_address := expr_12_address - - } - /// @src 0:93:146 - - function panic_error_0x41() { - mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) - mstore(4, 0x41) - revert(0, 0x24) - } - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } @@ -192,18 +161,52 @@ object \"D_16\" { revert(0, 0) } + function abi_decode_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + function revert_forward_1() { let pos := allocate_unbounded() returndatacopy(pos, 0, returndatasize()) revert(pos, returndatasize()) } - function shift_right_224_unsigned(value) -> newValue { - newValue := + /// @ast-id 15 + /// @src 0:106:144 \"function f() public { C c = new C(); }\" + function fun_f_15() { - shr(224, value) + /// @src 0:134:141 \"new C()\" + let _1 := allocate_unbounded() + 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() } + + /// @src 0:128:141 \"C c = new C()\" + let var_c_8_address := expr_12_address } + /// @src 0:93:146 \"contract D { function f() public { C c = new C(); } }\" } /*=====================================================* @@ -213,10 +216,10 @@ object \"D_16\" { * !USE AT YOUR OWN RISK! * *=====================================================*/ - /// @use-src 0:\"A\", 1:\"#utility.yul\" + /// @use-src 0:\"A\" object \"C_3\" { code { - /// @src 0:79:92 + /// @src 0:79:92 \"contract C {}\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -231,22 +234,23 @@ object \"D_16\" { memPtr := mload(64) } - /// @src 0:79:92 - function constructor_C_3() { - - /// @src 0:79:92 - - } - /// @src 0:79:92 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:79:92 \"contract C {}\" + function constructor_C_3() { + + /// @src 0:79:92 \"contract C {}\" + + } + /// @src 0:79:92 \"contract C {}\" + } + /// @use-src 0:\"A\" object \"C_3_deployed\" { code { - /// @src 0:79:92 + /// @src 0:79:92 \"contract C {}\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -259,6 +263,13 @@ object \"D_16\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + function allocate_unbounded() -> memPtr { memPtr := mload(64) } @@ -267,13 +278,6 @@ object \"D_16\" { revert(0, 0) } - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - - } - } data \".metadata\" hex\"\" diff --git a/test/cmdlineTests/standard_yul_single_file_via_urls/args b/test/cmdlineTests/standard_yul_single_file_via_urls/args new file mode 100644 index 000000000..24d48faf2 --- /dev/null +++ b/test/cmdlineTests/standard_yul_single_file_via_urls/args @@ -0,0 +1 @@ +--pretty-json --json-indent 4 --allow-paths . diff --git a/test/cmdlineTests/standard_yul_single_file_via_urls/input.json b/test/cmdlineTests/standard_yul_single_file_via_urls/input.json new file mode 100644 index 000000000..0f4da43fc --- /dev/null +++ b/test/cmdlineTests/standard_yul_single_file_via_urls/input.json @@ -0,0 +1,6 @@ +{ + "language": "Yul", + "sources": { + "C": {"urls": ["in.yul"]} + } +} diff --git a/test/cmdlineTests/standard_yul_single_file_via_urls/output.json b/test/cmdlineTests/standard_yul_single_file_via_urls/output.json new file mode 100644 index 000000000..3f62fa0fe --- /dev/null +++ b/test/cmdlineTests/standard_yul_single_file_via_urls/output.json @@ -0,0 +1,19 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Cannot import url (\"in.yul\"): File not found.", + "message": "Cannot import url (\"in.yul\"): File not found.", + "severity": "error", + "type": "IOError" + }, + { + "component": "general", + "formattedMessage": "Yul mode only supports exactly one input file.", + "message": "Yul mode only supports exactly one input file.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/stdin_allowed_paths/args b/test/cmdlineTests/stdin_allowed_paths/args deleted file mode 100644 index 39cdd0ded..000000000 --- a/test/cmdlineTests/stdin_allowed_paths/args +++ /dev/null @@ -1 +0,0 @@ -- diff --git a/test/cmdlineTests/stdin_allowed_paths/err b/test/cmdlineTests/stdin_allowed_paths/err deleted file mode 100644 index e25a6567c..000000000 --- a/test/cmdlineTests/stdin_allowed_paths/err +++ /dev/null @@ -1,11 +0,0 @@ -Error: Source "too_long_line/input.sol" not found: File outside of allowed directories. - --> :4:1: - | -4 | import "../too_long_line/input.sol"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Error: Source "error_codes/input.sol" not found: File outside of allowed directories. - --> stdin_allowed_paths/input.sol:4:1: - | -4 | import "../error_codes/input.sol"; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/stdin_allowed_paths/input.sol b/test/cmdlineTests/stdin_allowed_paths/input.sol deleted file mode 100644 index dfe1f7070..000000000 --- a/test/cmdlineTests/stdin_allowed_paths/input.sol +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.0; - -import "../error_codes/input.sol"; diff --git a/test/cmdlineTests/stdin_allowed_paths/stdin b/test/cmdlineTests/stdin_allowed_paths/stdin deleted file mode 100644 index 776d152d7..000000000 --- a/test/cmdlineTests/stdin_allowed_paths/stdin +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.0; - -import "../too_long_line/input.sol"; diff --git a/test/cmdlineTests/storage_layout_user_defined/args b/test/cmdlineTests/storage_layout_user_defined/args new file mode 100644 index 000000000..f69ac4e39 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/args @@ -0,0 +1 @@ +--storage-layout diff --git a/test/cmdlineTests/storage_layout_user_defined/err b/test/cmdlineTests/storage_layout_user_defined/err new file mode 100644 index 000000000..a9b5f07b1 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/err @@ -0,0 +1,2 @@ +Warning: Source file does not specify required compiler version! +--> storage_layout_user_defined/input.sol diff --git a/test/cmdlineTests/storage_layout_user_defined/input.sol b/test/cmdlineTests/storage_layout_user_defined/input.sol new file mode 100644 index 000000000..f07020081 --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/input.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL v3 +type MyInt128 is int128; +type MyInt8 is int8; +contract C { + // slot 0 + MyInt128 a; + MyInt128 b; + // slot 1 + MyInt128 c; + MyInt8 d; + MyInt8 e; + MyInt8 f; + MyInt8 g; + // slot 2 + MyInt8 h; +} diff --git a/test/cmdlineTests/storage_layout_user_defined/output b/test/cmdlineTests/storage_layout_user_defined/output new file mode 100644 index 000000000..0f6890dae --- /dev/null +++ b/test/cmdlineTests/storage_layout_user_defined/output @@ -0,0 +1,4 @@ + +======= storage_layout_user_defined/input.sol:C ======= +Contract Storage Layout: +{"storage":[{"astId":7,"contract":"storage_layout_user_defined/input.sol:C","label":"a","offset":0,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":10,"contract":"storage_layout_user_defined/input.sol:C","label":"b","offset":16,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":13,"contract":"storage_layout_user_defined/input.sol:C","label":"c","offset":0,"slot":"1","type":"t_userDefinedValueType(MyInt128)2"},{"astId":16,"contract":"storage_layout_user_defined/input.sol:C","label":"d","offset":16,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":19,"contract":"storage_layout_user_defined/input.sol:C","label":"e","offset":17,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":22,"contract":"storage_layout_user_defined/input.sol:C","label":"f","offset":18,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":25,"contract":"storage_layout_user_defined/input.sol:C","label":"g","offset":19,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":28,"contract":"storage_layout_user_defined/input.sol:C","label":"h","offset":20,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"}],"types":{"t_userDefinedValueType(MyInt128)2":{"encoding":"inplace","label":"MyInt128","numberOfBytes":"16"},"t_userDefinedValueType(MyInt8)4":{"encoding":"inplace","label":"MyInt8","numberOfBytes":"1"}}} diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output index d42a032f8..18d02a0e9 100644 --- a/test/cmdlineTests/viair_abicoder_v1/output +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -7,10 +7,10 @@ IR: *=====================================================*/ -/// @use-src 0:"viair_abicoder_v1/input.sol", 1:"#utility.yul" +/// @use-src 0:"viair_abicoder_v1/input.sol" object "test_11" { code { - /// @src 0:79:169 + /// @src 0:79:169 "contract test {..." mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -25,22 +25,23 @@ object "test_11" { memPtr := mload(64) } - /// @src 0:79:169 - function constructor_test_11() { - - /// @src 0:79:169 - - } - /// @src 0:79:169 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:79:169 "contract test {..." + function constructor_test_11() { + + /// @src 0:79:169 "contract test {..." + + } + /// @src 0:79:169 "contract test {..." + } + /// @use-src 0:"viair_abicoder_v1/input.sol" object "test_11_deployed" { code { - /// @src 0:79:169 + /// @src 0:79:169 "contract test {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -65,11 +66,34 @@ object "test_11" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function abi_decode_tuple_(headStart, dataEnd) { if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } + function cleanup_t_bool(value) -> cleaned { + cleaned := iszero(iszero(value)) + } + function abi_encode_t_bool_to_t_bool_fromStack(value, pos) { mstore(pos, cleanup_t_bool(value)) } @@ -81,52 +105,30 @@ object "test_11" { } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) - } - - function cleanup_t_bool(value) -> cleaned { - cleaned := iszero(iszero(value)) - } - - /// @src 0:99:167 - function fun_f_10() -> var__5 { - /// @src 0:133:137 - let zero_t_bool_1 := zero_value_for_split_t_bool() - var__5 := zero_t_bool_1 - - /// @src 0:156:160 - let expr_7 := 0x01 - /// @src 0:149:160 - var__5 := expr_7 - leave - - } - /// @src 0:79:169 - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) } - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - - } - function zero_value_for_split_t_bool() -> ret { ret := 0 } + /// @ast-id 10 + /// @src 0:99:167 "function f() public pure returns (bool) {..." + function fun_f_10() -> var__5 { + /// @src 0:133:137 "bool" + let zero_t_bool_1 := zero_value_for_split_t_bool() + var__5 := zero_t_bool_1 + + /// @src 0:156:160 "true" + let expr_7 := 0x01 + /// @src 0:149:160 "return true" + var__5 := expr_7 + leave + + } + /// @src 0:79:169 "contract test {..." + } data ".metadata" hex"" diff --git a/test/cmdlineTests/viair_subobjects/output b/test/cmdlineTests/viair_subobjects/output index 9ca620d06..eef7d27e8 100644 --- a/test/cmdlineTests/viair_subobjects/output +++ b/test/cmdlineTests/viair_subobjects/output @@ -12,11 +12,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"viair_subobjects/input.sol", 1:"#utility.yul" +/// @use-src 0:"viair_subobjects/input.sol" object "C_3" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -24,10 +24,11 @@ object "C_3" { return(128, _1) } } + /// @use-src 0:"viair_subobjects/input.sol" object "C_3_deployed" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) revert(0, 0) } @@ -50,11 +51,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"viair_subobjects/input.sol", 1:"#utility.yul" +/// @use-src 0:"viair_subobjects/input.sol" object "D_16" { code { { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") @@ -62,10 +63,11 @@ object "D_16" { return(128, _1) } } + /// @use-src 0:"viair_subobjects/input.sol" object "D_16_deployed" { code { { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -74,22 +76,22 @@ object "D_16" { { if callvalue() { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } - /// @src 0:149:156 + /// @src 0:149:156 "new C()" let _2 := datasize("C_3") - let _3 := add(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ _2) - if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 */ 128)) - /// @src 0:149:156 + let _3 := add(/** @src 0:96:165 "contract D {..." */ 128, /** @src 0:149:156 "new C()" */ _2) + if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:96:165 "contract D {..." */ 128)) + /// @src 0:149:156 "new C()" { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." mstore(_1, shl(224, 0x4e487b71)) mstore(4, 0x41) revert(_1, 0x24) } - /// @src 0:149:156 - datacopy(/** @src 0:96:165 */ 128, /** @src 0:149:156 */ dataoffset("C_3"), _2) - if iszero(create(/** @src 0:96:165 */ _1, 128, /** @src 0:149:156 */ _2)) + /// @src 0:149:156 "new C()" + datacopy(/** @src 0:96:165 "contract D {..." */ 128, /** @src 0:149:156 "new C()" */ dataoffset("C_3"), _2) + if iszero(create(/** @src 0:96:165 "contract D {..." */ _1, 128, /** @src 0:149:156 "new C()" */ _2)) { - /// @src 0:96:165 + /// @src 0:96:165 "contract D {..." let pos := mload(64) returndatacopy(pos, _1, returndatasize()) revert(pos, returndatasize()) @@ -100,10 +102,11 @@ object "D_16" { revert(0, 0) } } + /// @use-src 0:"viair_subobjects/input.sol" object "C_3" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") @@ -111,10 +114,11 @@ object "D_16" { return(128, _1) } } + /// @use-src 0:"viair_subobjects/input.sol" object "C_3_deployed" { code { { - /// @src 0:82:95 + /// @src 0:82:95 "contract C {}" mstore(64, 128) revert(0, 0) } diff --git a/test/cmdlineTests/yul_optimizer_steps/output b/test/cmdlineTests/yul_optimizer_steps/output index af9ef0988..478c844db 100644 --- a/test/cmdlineTests/yul_optimizer_steps/output +++ b/test/cmdlineTests/yul_optimizer_steps/output @@ -6,11 +6,11 @@ Optimized IR: * !USE AT YOUR OWN RISK! * *=====================================================*/ -/// @use-src 0:"yul_optimizer_steps/input.sol", 1:"#utility.yul" +/// @use-src 0:"yul_optimizer_steps/input.sol" object "C_7" { code { { - /// @src 0:80:112 + /// @src 0:80:112 "contract C..." mstore(64, 128) if callvalue() { @@ -25,10 +25,11 @@ object "C_7" { function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } } + /// @use-src 0:"yul_optimizer_steps/input.sol" object "C_7_deployed" { code { { - /// @src 0:80:112 + /// @src 0:80:112 "contract C..." mstore(64, 128) if iszero(lt(calldatasize(), 4)) { @@ -38,10 +39,10 @@ object "C_7" { pop(iszero(calldatasize())) revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() } - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() - { revert(0, 0) } function shift_right_unsigned(value) -> newValue { newValue := shr(224, value) } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + { revert(0, 0) } } data ".metadata" hex"" } diff --git a/test/cmdlineTests/yul_source_locations/input.json b/test/cmdlineTests/yul_source_locations/input.json deleted file mode 100644 index ce5c4b626..000000000 --- a/test/cmdlineTests/yul_source_locations/input.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "language": "Solidity", - "sources": - { - "C": - { - "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\n\ncontract C\n{\n int constant constVar = 41;\n int immutable immutVar = 42;\n int public stateVar;\n\n constructor(int _init)\n {\n stateVar = _init;\n }\n\n function f() external pure returns (int)\n {\n return constVar + immutVar;\n }\n modifier m()\n {\n stateVar++;\n _;\n }\n function f2() m public returns (int)\n {\n return stateVar + this.f() + immutVar;\n }\n}\n" - }, - "D": - { - "content": "//SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\npragma abicoder v2;\nimport \"C\";\n\ncontract D is C(3)\n{\n constructor(int _init2)\n {\n stateVar += _init2;\n }\n}\n" - } - }, - "settings": - { - "outputSelection": - { - "*": { "*": ["ir"] } - } - } -} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index a48cc0d68..f68f984c7 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -6,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_11\" { code { - /// @src 0:78:164 + /// @src 0:78:164 \"contract C { function f() external pure returns (string memory) { return \\\"abcabc\\\"; } }\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,22 +24,23 @@ object \"C_11\" { memPtr := mload(64) } - /// @src 0:78:164 - function constructor_C_11() { - - /// @src 0:78:164 - - } - /// @src 0:78:164 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:78:164 \"contract C { function f() external pure returns (string memory) { return \\\"abcabc\\\"; } }\" + function constructor_C_11() { + + /// @src 0:78:164 \"contract C { function f() external pure returns (string memory) { return \\\"abcabc\\\"; } }\" + + } + /// @src 0:78:164 \"contract C { function f() external pure returns (string memory) { return \\\"abcabc\\\"; } }\" + } + /// @use-src 0:\"A\" object \"C_11_deployed\" { code { - /// @src 0:78:164 + /// @src 0:78:164 \"contract C { function f() external pure returns (string memory) { return \\\"abcabc\\\"; } }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -64,11 +65,58 @@ object \"C_11\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function abi_decode_tuple_(headStart, dataEnd) { if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } + function array_length_t_string_memory_ptr(value) -> length { + + length := mload(value) + + } + + function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { + mstore(pos, length) + updated_pos := add(pos, 0x20) + } + + function copy_memory_to_memory(src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + if gt(i, length) + { + // clear end + mstore(add(dst, length), 0) + } + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end { let length := array_length_t_string_memory_ptr(value) pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) @@ -84,23 +132,32 @@ object \"C_11\" { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + function allocate_memory(size) -> memPtr { memPtr := allocate_unbounded() finalize_allocation(memPtr, size) } - function allocate_memory_array_t_string_memory_ptr(length) -> memPtr { - let allocSize := array_allocation_size_t_string_memory_ptr(length) - memPtr := allocate_memory(allocSize) - - mstore(memPtr, length) - - } - - function allocate_unbounded() -> memPtr { - memPtr := mload(64) - } - function array_allocation_size_t_string_memory_ptr(length) -> size { // Make sure we can allocate memory without overflow if gt(length, 0xffffffffffffffff) { panic_error_0x41() } @@ -112,85 +169,11 @@ object \"C_11\" { } - function array_length_t_string_memory_ptr(value) -> length { + function allocate_memory_array_t_string_memory_ptr(length) -> memPtr { + let allocSize := array_allocation_size_t_string_memory_ptr(length) + memPtr := allocate_memory(allocSize) - length := mload(value) - - } - - function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { - mstore(pos, length) - updated_pos := add(pos, 0x20) - } - - function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() -> converted { - converted := copy_literal_to_memory_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21() - } - - function copy_literal_to_memory_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21() -> memPtr { - memPtr := allocate_memory_array_t_string_memory_ptr(6) - store_literal_in_memory_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21(add(memPtr, 32)) - } - - function copy_memory_to_memory(src, dst, length) { - let i := 0 - for { } lt(i, length) { i := add(i, 32) } - { - mstore(add(dst, i), mload(add(src, i))) - } - if gt(i, length) - { - // clear end - mstore(add(dst, length), 0) - } - } - - function finalize_allocation(memPtr, size) { - let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) - // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) - } - - /// @src 0:91:162 - function fun_f_10() -> var__5_mpos { - /// @src 0:127:140 - let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() - var__5_mpos := zero_t_string_memory_ptr_1_mpos - - /// @src 0:144:159 - var__5_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() - leave - - } - /// @src 0:78:164 - - function panic_error_0x41() { - mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) - mstore(4, 0x41) - revert(0, 0x24) - } - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function round_up_to_mul_of_32(value) -> result { - result := and(add(value, 31), not(31)) - } - - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) + mstore(memPtr, length) } @@ -200,10 +183,29 @@ object \"C_11\" { } - function zero_value_for_split_t_string_memory_ptr() -> ret { - ret := 96 + function copy_literal_to_memory_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21() -> memPtr { + memPtr := allocate_memory_array_t_string_memory_ptr(6) + store_literal_in_memory_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21(add(memPtr, 32)) } + function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() -> converted { + converted := copy_literal_to_memory_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21() + } + + /// @ast-id 10 + /// @src 0:91:162 \"function f() external pure returns (string memory) { return \\\"abcabc\\\"; }\" + function fun_f_10() -> var__5_mpos { + /// @src 0:127:140 \"string memory\" + let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + var__5_mpos := zero_t_string_memory_ptr_1_mpos + + /// @src 0:144:159 \"return \\\"abcabc\\\"\" + var__5_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() + leave + + } + /// @src 0:78:164 \"contract C { function f() external pure returns (string memory) { return \\\"abcabc\\\"; } }\" + } data \".metadata\" hex\"\" diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 6dabd7fdd..e2757135e 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -6,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_11\" { code { - /// @src 0:78:158 + /// @src 0:78:158 \"contract C { function f() external pure returns (bytes32) { return \\\"abcabc\\\"; } }\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,22 +24,23 @@ object \"C_11\" { memPtr := mload(64) } - /// @src 0:78:158 - function constructor_C_11() { - - /// @src 0:78:158 - - } - /// @src 0:78:158 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:78:158 \"contract C { function f() external pure returns (bytes32) { return \\\"abcabc\\\"; } }\" + function constructor_C_11() { + + /// @src 0:78:158 \"contract C { function f() external pure returns (bytes32) { return \\\"abcabc\\\"; } }\" + + } + /// @src 0:78:158 \"contract C { function f() external pure returns (bytes32) { return \\\"abcabc\\\"; } }\" + } + /// @use-src 0:\"A\" object \"C_11_deployed\" { code { - /// @src 0:78:158 + /// @src 0:78:158 \"contract C { function f() external pure returns (bytes32) { return \\\"abcabc\\\"; } }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -64,11 +65,34 @@ object \"C_11\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function abi_decode_tuple_(headStart, dataEnd) { if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } + function cleanup_t_bytes32(value) -> cleaned { + cleaned := value + } + function abi_encode_t_bytes32_to_t_bytes32_fromStack(value, pos) { mstore(pos, cleanup_t_bytes32(value)) } @@ -80,53 +104,31 @@ object \"C_11\" { } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) } - function cleanup_t_bytes32(value) -> cleaned { - cleaned := value + function zero_value_for_split_t_bytes32() -> ret { + ret := 0 } function convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() -> converted { converted := 0x6162636162630000000000000000000000000000000000000000000000000000 } - /// @src 0:91:156 + /// @ast-id 10 + /// @src 0:91:156 \"function f() external pure returns (bytes32) { return \\\"abcabc\\\"; }\" function fun_f_10() -> var__5 { - /// @src 0:127:134 + /// @src 0:127:134 \"bytes32\" let zero_t_bytes32_1 := zero_value_for_split_t_bytes32() var__5 := zero_t_bytes32_1 - /// @src 0:138:153 + /// @src 0:138:153 \"return \\\"abcabc\\\"\" var__5 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave } - /// @src 0:78:158 - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - - } - - function zero_value_for_split_t_bytes32() -> ret { - ret := 0 - } + /// @src 0:78:158 \"contract C { function f() external pure returns (bytes32) { return \\\"abcabc\\\"; } }\" } 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 e02016c0e..c977d791d 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,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_11\" { code { - /// @src 0:78:159 + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0x61626364; } }\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,22 +24,23 @@ object \"C_11\" { memPtr := mload(64) } - /// @src 0:78:159 - function constructor_C_11() { - - /// @src 0:78:159 - - } - /// @src 0:78:159 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0x61626364; } }\" + function constructor_C_11() { + + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0x61626364; } }\" + + } + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0x61626364; } }\" + } + /// @use-src 0:\"A\" object \"C_11_deployed\" { code { - /// @src 0:78:159 + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0x61626364; } }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -64,11 +65,34 @@ object \"C_11\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function abi_decode_tuple_(headStart, dataEnd) { if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } + function cleanup_t_bytes4(value) -> cleaned { + cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) + } + function abi_encode_t_bytes4_to_t_bytes4_fromStack(value, pos) { mstore(pos, cleanup_t_bytes4(value)) } @@ -80,47 +104,16 @@ object \"C_11\" { } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) - } - - function cleanup_t_bytes4(value) -> cleaned { - cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) - } - - function cleanup_t_rational_1633837924_by_1(value) -> cleaned { - cleaned := value - } - - function convert_t_rational_1633837924_by_1_to_t_bytes4(value) -> converted { - converted := cleanup_t_bytes4(shift_left_224(cleanup_t_rational_1633837924_by_1(value))) - } - - /// @src 0:91:157 - function fun_f_10() -> var__5 { - /// @src 0:127:133 - let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() - var__5 := zero_t_bytes4_1 - - /// @src 0:144:154 - let expr_7 := 0x61626364 - /// @src 0:137:154 - var__5 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_7) - leave - - } - /// @src 0:78:159 - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) } - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 } - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) + function cleanup_t_rational_1633837924_by_1(value) -> cleaned { + cleaned := value } function shift_left_224(value) -> newValue { @@ -130,16 +123,25 @@ object \"C_11\" { } - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - + function convert_t_rational_1633837924_by_1_to_t_bytes4(value) -> converted { + converted := cleanup_t_bytes4(shift_left_224(cleanup_t_rational_1633837924_by_1(value))) } - function zero_value_for_split_t_bytes4() -> ret { - ret := 0 + /// @ast-id 10 + /// @src 0:91:157 \"function f() external pure returns (bytes4) { return 0x61626364; }\" + function fun_f_10() -> var__5 { + /// @src 0:127:133 \"bytes4\" + let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() + var__5 := zero_t_bytes4_1 + + /// @src 0:144:154 \"0x61626364\" + let expr_7 := 0x61626364 + /// @src 0:137:154 \"return 0x61626364\" + var__5 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_7) + leave + } + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0x61626364; } }\" } diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index f9110a134..19428f29d 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -6,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_11\" { code { - /// @src 0:78:243 + /// @src 0:78:243 \"contract C { function f() external pure returns (string memory) { return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"; } }\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,22 +24,23 @@ object \"C_11\" { memPtr := mload(64) } - /// @src 0:78:243 - function constructor_C_11() { - - /// @src 0:78:243 - - } - /// @src 0:78:243 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:78:243 \"contract C { function f() external pure returns (string memory) { return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"; } }\" + function constructor_C_11() { + + /// @src 0:78:243 \"contract C { function f() external pure returns (string memory) { return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"; } }\" + + } + /// @src 0:78:243 \"contract C { function f() external pure returns (string memory) { return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"; } }\" + } + /// @use-src 0:\"A\" object \"C_11_deployed\" { code { - /// @src 0:78:243 + /// @src 0:78:243 \"contract C { function f() external pure returns (string memory) { return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"; } }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -64,11 +65,58 @@ object \"C_11\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function abi_decode_tuple_(headStart, dataEnd) { if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } + function array_length_t_string_memory_ptr(value) -> length { + + length := mload(value) + + } + + function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { + mstore(pos, length) + updated_pos := add(pos, 0x20) + } + + function copy_memory_to_memory(src, dst, length) { + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(dst, i), mload(add(src, i))) + } + if gt(i, length) + { + // clear end + mstore(add(dst, length), 0) + } + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + function abi_encode_t_string_memory_ptr_to_t_string_memory_ptr_fromStack(value, pos) -> end { let length := array_length_t_string_memory_ptr(value) pos := array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) @@ -84,23 +132,32 @@ object \"C_11\" { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + function allocate_memory(size) -> memPtr { memPtr := allocate_unbounded() finalize_allocation(memPtr, size) } - function allocate_memory_array_t_string_memory_ptr(length) -> memPtr { - let allocSize := array_allocation_size_t_string_memory_ptr(length) - memPtr := allocate_memory(allocSize) - - mstore(memPtr, length) - - } - - function allocate_unbounded() -> memPtr { - memPtr := mload(64) - } - function array_allocation_size_t_string_memory_ptr(length) -> size { // Make sure we can allocate memory without overflow if gt(length, 0xffffffffffffffff) { panic_error_0x41() } @@ -112,85 +169,11 @@ object \"C_11\" { } - function array_length_t_string_memory_ptr(value) -> length { + function allocate_memory_array_t_string_memory_ptr(length) -> memPtr { + let allocSize := array_allocation_size_t_string_memory_ptr(length) + memPtr := allocate_memory(allocSize) - length := mload(value) - - } - - function array_storeLengthForEncoding_t_string_memory_ptr_fromStack(pos, length) -> updated_pos { - mstore(pos, length) - updated_pos := add(pos, 0x20) - } - - function convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() -> converted { - converted := copy_literal_to_memory_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571() - } - - function copy_literal_to_memory_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571() -> memPtr { - memPtr := allocate_memory_array_t_string_memory_ptr(85) - store_literal_in_memory_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571(add(memPtr, 32)) - } - - function copy_memory_to_memory(src, dst, length) { - let i := 0 - for { } lt(i, length) { i := add(i, 32) } - { - mstore(add(dst, i), mload(add(src, i))) - } - if gt(i, length) - { - // clear end - mstore(add(dst, length), 0) - } - } - - function finalize_allocation(memPtr, size) { - let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) - // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } - mstore(64, newFreePtr) - } - - /// @src 0:91:241 - function fun_f_10() -> var__5_mpos { - /// @src 0:127:140 - let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() - var__5_mpos := zero_t_string_memory_ptr_1_mpos - - /// @src 0:144:238 - var__5_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() - leave - - } - /// @src 0:78:243 - - function panic_error_0x41() { - mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) - mstore(4, 0x41) - revert(0, 0x24) - } - - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { - revert(0, 0) - } - - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) - } - - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) - } - - function round_up_to_mul_of_32(value) -> result { - result := and(add(value, 31), not(31)) - } - - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) + mstore(memPtr, length) } @@ -204,10 +187,29 @@ object \"C_11\" { } - function zero_value_for_split_t_string_memory_ptr() -> ret { - ret := 96 + function copy_literal_to_memory_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571() -> memPtr { + memPtr := allocate_memory_array_t_string_memory_ptr(85) + store_literal_in_memory_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571(add(memPtr, 32)) } + function convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() -> converted { + converted := copy_literal_to_memory_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571() + } + + /// @ast-id 10 + /// @src 0:91:241 \"function f() external pure returns (string memory) { return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"; }\" + function fun_f_10() -> var__5_mpos { + /// @src 0:127:140 \"string memory\" + let zero_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + var__5_mpos := zero_t_string_memory_ptr_1_mpos + + /// @src 0:144:238 \"return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"\" + var__5_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() + leave + + } + /// @src 0:78:243 \"contract C { function f() external pure returns (string memory) { return \\\"abcdabcdcafecafeabcdabcdcafecafeffffzzzzoooo0123456789,.<,>.?:;'[{]}|`~!@#$%^&*()-_=+\\\"; } }\" + } data \".metadata\" hex\"\" diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 0bce08e79..b17922a55 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -6,10 +6,10 @@ *=====================================================*/ -/// @use-src 0:\"A\", 1:\"#utility.yul\" +/// @use-src 0:\"A\" object \"C_11\" { code { - /// @src 0:78:159 + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }\" mstore(64, 128) if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } @@ -24,22 +24,23 @@ object \"C_11\" { memPtr := mload(64) } - /// @src 0:78:159 - function constructor_C_11() { - - /// @src 0:78:159 - - } - /// @src 0:78:159 - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { revert(0, 0) } + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }\" + function constructor_C_11() { + + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }\" + + } + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }\" + } + /// @use-src 0:\"A\" object \"C_11_deployed\" { code { - /// @src 0:78:159 + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }\" mstore(64, 128) if iszero(lt(calldatasize(), 4)) @@ -64,11 +65,34 @@ object \"C_11\" { if iszero(calldatasize()) { } revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function abi_decode_tuple_(headStart, dataEnd) { if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } + function cleanup_t_bytes4(value) -> cleaned { + cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) + } + function abi_encode_t_bytes4_to_t_bytes4_fromStack(value, pos) { mstore(pos, cleanup_t_bytes4(value)) } @@ -80,47 +104,16 @@ object \"C_11\" { } - function allocate_unbounded() -> memPtr { - memPtr := mload(64) - } - - function cleanup_t_bytes4(value) -> cleaned { - cleaned := and(value, 0xffffffff00000000000000000000000000000000000000000000000000000000) - } - - function cleanup_t_rational_2864434397_by_1(value) -> cleaned { - cleaned := value - } - - function convert_t_rational_2864434397_by_1_to_t_bytes4(value) -> converted { - converted := cleanup_t_bytes4(shift_left_224(cleanup_t_rational_2864434397_by_1(value))) - } - - /// @src 0:91:157 - function fun_f_10() -> var__5 { - /// @src 0:127:133 - let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() - var__5 := zero_t_bytes4_1 - - /// @src 0:144:154 - let expr_7 := 0xaabbccdd - /// @src 0:137:154 - var__5 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_7) - leave - - } - /// @src 0:78:159 - function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { revert(0, 0) } - function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { - revert(0, 0) + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 } - function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { - revert(0, 0) + function cleanup_t_rational_2864434397_by_1(value) -> cleaned { + cleaned := value } function shift_left_224(value) -> newValue { @@ -130,16 +123,25 @@ object \"C_11\" { } - function shift_right_224_unsigned(value) -> newValue { - newValue := - - shr(224, value) - + function convert_t_rational_2864434397_by_1_to_t_bytes4(value) -> converted { + converted := cleanup_t_bytes4(shift_left_224(cleanup_t_rational_2864434397_by_1(value))) } - function zero_value_for_split_t_bytes4() -> ret { - ret := 0 + /// @ast-id 10 + /// @src 0:91:157 \"function f() external pure returns (bytes4) { return 0xaabbccdd; }\" + function fun_f_10() -> var__5 { + /// @src 0:127:133 \"bytes4\" + let zero_t_bytes4_1 := zero_value_for_split_t_bytes4() + var__5 := zero_t_bytes4_1 + + /// @src 0:144:154 \"0xaabbccdd\" + let expr_7 := 0xaabbccdd + /// @src 0:137:154 \"return 0xaabbccdd\" + var__5 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_7) + leave + } + /// @src 0:78:159 \"contract C { function f() external pure returns (bytes4) { return 0xaabbccdd; } }\" } diff --git a/test/libsolidity/ABIJson/user_defined_value_type.sol b/test/libsolidity/ABIJson/user_defined_value_type.sol new file mode 100644 index 000000000..3bd034e6a --- /dev/null +++ b/test/libsolidity/ABIJson/user_defined_value_type.sol @@ -0,0 +1,173 @@ +type MyInt is int; +type MyByte1 is bytes1; +contract C { + type MyAddress is address; + type MyUInt8 is uint8; + type MyBytes32 is bytes32; + + MyInt public myInt; + MyByte1 public myByte1; + MyAddress public myAddress; + MyUInt8 public myUInt8; + MyBytes32 public myBytes32; + + function setMyInt(MyInt a) external { + myInt = a; + } + function setMyByte1(MyByte1 a) external { + myByte1 = a; + } + function setMyAddress(MyAddress a) external { + myAddress = a; + } + function setMyUInt8(MyUInt8 a) external { + myUInt8 = a; + } + function setMyBytes32(MyBytes32 a) external { + myBytes32 = a; + } +} +// ---- +// :C +// [ +// { +// "inputs": [], +// "name": "myAddress", +// "outputs": +// [ +// { +// "internalType": "C.MyAddress", +// "name": "", +// "type": "address" +// } +// ], +// "stateMutability": "view", +// "type": "function" +// }, +// { +// "inputs": [], +// "name": "myByte1", +// "outputs": +// [ +// { +// "internalType": "MyByte1", +// "name": "", +// "type": "bytes1" +// } +// ], +// "stateMutability": "view", +// "type": "function" +// }, +// { +// "inputs": [], +// "name": "myBytes32", +// "outputs": +// [ +// { +// "internalType": "C.MyBytes32", +// "name": "", +// "type": "bytes32" +// } +// ], +// "stateMutability": "view", +// "type": "function" +// }, +// { +// "inputs": [], +// "name": "myInt", +// "outputs": +// [ +// { +// "internalType": "MyInt", +// "name": "", +// "type": "int256" +// } +// ], +// "stateMutability": "view", +// "type": "function" +// }, +// { +// "inputs": [], +// "name": "myUInt8", +// "outputs": +// [ +// { +// "internalType": "C.MyUInt8", +// "name": "", +// "type": "uint8" +// } +// ], +// "stateMutability": "view", +// "type": "function" +// }, +// { +// "inputs": +// [ +// { +// "internalType": "C.MyAddress", +// "name": "a", +// "type": "address" +// } +// ], +// "name": "setMyAddress", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// }, +// { +// "inputs": +// [ +// { +// "internalType": "MyByte1", +// "name": "a", +// "type": "bytes1" +// } +// ], +// "name": "setMyByte1", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// }, +// { +// "inputs": +// [ +// { +// "internalType": "C.MyBytes32", +// "name": "a", +// "type": "bytes32" +// } +// ], +// "name": "setMyBytes32", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// }, +// { +// "inputs": +// [ +// { +// "internalType": "MyInt", +// "name": "a", +// "type": "int256" +// } +// ], +// "name": "setMyInt", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// }, +// { +// "inputs": +// [ +// { +// "internalType": "C.MyUInt8", +// "name": "a", +// "type": "uint8" +// } +// ], +// "name": "setMyUInt8", +// "outputs": [], +// "stateMutability": "nonpayable", +// "type": "function" +// } +// ] diff --git a/test/libsolidity/ASTJSON/abstract_contract.json b/test/libsolidity/ASTJSON/abstract_contract.json index ab0764cd8..168433eb9 100644 --- a/test/libsolidity/ASTJSON/abstract_contract.json +++ b/test/libsolidity/ASTJSON/abstract_contract.json @@ -14,6 +14,7 @@ { "abstract": true, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index 37692508b..22090ee29 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/array_type_name.json b/test/libsolidity/ASTJSON/array_type_name.json index 25a54712d..c1bcaa985 100644 --- a/test/libsolidity/ASTJSON/array_type_name.json +++ b/test/libsolidity/ASTJSON/array_type_name.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/call.json b/test/libsolidity/ASTJSON/assembly/call.json index 43a401558..c6e0561ed 100644 --- a/test/libsolidity/ASTJSON/assembly/call.json +++ b/test/libsolidity/ASTJSON/assembly/call.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/empty_block.json b/test/libsolidity/ASTJSON/assembly/empty_block.json index 9e0867656..19779b076 100644 --- a/test/libsolidity/ASTJSON/assembly/empty_block.json +++ b/test/libsolidity/ASTJSON/assembly/empty_block.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/function.json b/test/libsolidity/ASTJSON/assembly/function.json index f78550fd1..e528baa4c 100644 --- a/test/libsolidity/ASTJSON/assembly/function.json +++ b/test/libsolidity/ASTJSON/assembly/function.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/leave.json b/test/libsolidity/ASTJSON/assembly/leave.json index 12b042b51..eefc5105f 100644 --- a/test/libsolidity/ASTJSON/assembly/leave.json +++ b/test/libsolidity/ASTJSON/assembly/leave.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/loop.json b/test/libsolidity/ASTJSON/assembly/loop.json index a0c475ae1..8a887a80f 100644 --- a/test/libsolidity/ASTJSON/assembly/loop.json +++ b/test/libsolidity/ASTJSON/assembly/loop.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/nested_functions.json b/test/libsolidity/ASTJSON/assembly/nested_functions.json index f4cef95e0..aea5e54d0 100644 --- a/test/libsolidity/ASTJSON/assembly/nested_functions.json +++ b/test/libsolidity/ASTJSON/assembly/nested_functions.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "id": 8, diff --git a/test/libsolidity/ASTJSON/assembly/slot_offset.json b/test/libsolidity/ASTJSON/assembly/slot_offset.json index 8b8fb1a25..cc22c76e8 100644 --- a/test/libsolidity/ASTJSON/assembly/slot_offset.json +++ b/test/libsolidity/ASTJSON/assembly/slot_offset.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/stringlit.json b/test/libsolidity/ASTJSON/assembly/stringlit.json index 9ca0ad8e5..64777e947 100644 --- a/test/libsolidity/ASTJSON/assembly/stringlit.json +++ b/test/libsolidity/ASTJSON/assembly/stringlit.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/switch.json b/test/libsolidity/ASTJSON/assembly/switch.json index af02d3423..85dcb313f 100644 --- a/test/libsolidity/ASTJSON/assembly/switch.json +++ b/test/libsolidity/ASTJSON/assembly/switch.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "id": 6, diff --git a/test/libsolidity/ASTJSON/assembly/switch_default.json b/test/libsolidity/ASTJSON/assembly/switch_default.json index 63f105679..16e1f1bf4 100644 --- a/test/libsolidity/ASTJSON/assembly/switch_default.json +++ b/test/libsolidity/ASTJSON/assembly/switch_default.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/assembly/var_access.json b/test/libsolidity/ASTJSON/assembly/var_access.json index 0ae3bf008..9cddf4460 100644 --- a/test/libsolidity/ASTJSON/assembly/var_access.json +++ b/test/libsolidity/ASTJSON/assembly/var_access.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/base_constructor_call.json b/test/libsolidity/ASTJSON/base_constructor_call.json index e2b9686cb..bfbd716da 100644 --- a/test/libsolidity/ASTJSON/base_constructor_call.json +++ b/test/libsolidity/ASTJSON/base_constructor_call.json @@ -18,6 +18,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "A", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -121,6 +122,7 @@ "src": "50:1:1" } ], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/constructor.json b/test/libsolidity/ASTJSON/constructor.json index b1ca1503c..9ac4ac9c8 100644 --- a/test/libsolidity/ASTJSON/constructor.json +++ b/test/libsolidity/ASTJSON/constructor.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/contract_dep_order.json b/test/libsolidity/ASTJSON/contract_dep_order.json index fda09e177..754e14f4d 100644 --- a/test/libsolidity/ASTJSON/contract_dep_order.json +++ b/test/libsolidity/ASTJSON/contract_dep_order.json @@ -30,6 +30,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "A", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -64,6 +65,7 @@ "src": "29:1:1" } ], + "canonicalName": "B", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -99,6 +101,7 @@ "src": "49:1:1" } ], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -135,6 +138,7 @@ "src": "69:1:1" } ], + "canonicalName": "D", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -172,6 +176,7 @@ "src": "89:1:1" } ], + "canonicalName": "E", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/documentation.json b/test/libsolidity/ASTJSON/documentation.json index 751b6bcca..de512181f 100644 --- a/test/libsolidity/ASTJSON/documentation.json +++ b/test/libsolidity/ASTJSON/documentation.json @@ -15,6 +15,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "documentation": @@ -57,6 +58,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "documentation": @@ -99,6 +101,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/documentation_local_variable.json b/test/libsolidity/ASTJSON/documentation_local_variable.json index b21f719bf..24e38eb1a 100644 --- a/test/libsolidity/ASTJSON/documentation_local_variable.json +++ b/test/libsolidity/ASTJSON/documentation_local_variable.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/documentation_on_statements.json b/test/libsolidity/ASTJSON/documentation_on_statements.json index 5253c1115..bb3ee28d1 100644 --- a/test/libsolidity/ASTJSON/documentation_on_statements.json +++ b/test/libsolidity/ASTJSON/documentation_on_statements.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/documentation_triple.json b/test/libsolidity/ASTJSON/documentation_triple.json index 977006c3a..9124e68b1 100644 --- a/test/libsolidity/ASTJSON/documentation_triple.json +++ b/test/libsolidity/ASTJSON/documentation_triple.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/enum_value.json b/test/libsolidity/ASTJSON/enum_value.json index 90d5e498c..2cca00902 100644 --- a/test/libsolidity/ASTJSON/enum_value.json +++ b/test/libsolidity/ASTJSON/enum_value.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/event_definition.json b/test/libsolidity/ASTJSON/event_definition.json index e2187bbe3..627fd13ba 100644 --- a/test/libsolidity/ASTJSON/event_definition.json +++ b/test/libsolidity/ASTJSON/event_definition.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/fallback.json b/test/libsolidity/ASTJSON/fallback.json index 4b9e22e44..25a9b8588 100644 --- a/test/libsolidity/ASTJSON/fallback.json +++ b/test/libsolidity/ASTJSON/fallback.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json b/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json index 0f3b1b69e..26b4ada77 100644 --- a/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json +++ b/test/libsolidity/ASTJSON/fallback_and_reveice_ether.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/fallback_payable.json b/test/libsolidity/ASTJSON/fallback_payable.json index 1d0f3e627..4331b5edf 100644 --- a/test/libsolidity/ASTJSON/fallback_payable.json +++ b/test/libsolidity/ASTJSON/fallback_payable.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/function_type.json b/test/libsolidity/ASTJSON/function_type.json index 304812905..cef79e70b 100644 --- a/test/libsolidity/ASTJSON/function_type.json +++ b/test/libsolidity/ASTJSON/function_type.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/inheritance_specifier.json b/test/libsolidity/ASTJSON/inheritance_specifier.json index debcfdb10..b74604265 100644 --- a/test/libsolidity/ASTJSON/inheritance_specifier.json +++ b/test/libsolidity/ASTJSON/inheritance_specifier.json @@ -18,6 +18,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C1", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -52,6 +53,7 @@ "src": "30:2:1" } ], + "canonicalName": "C2", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/license.json b/test/libsolidity/ASTJSON/license.json index 2afd9a017..9f996e051 100644 --- a/test/libsolidity/ASTJSON/license.json +++ b/test/libsolidity/ASTJSON/license.json @@ -15,6 +15,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json index a2dbdc41c..8340eaf40 100644 --- a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "c", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.json b/test/libsolidity/ASTJSON/long_type_name_identifier.json index 8263dcda2..c810283d2 100644 --- a/test/libsolidity/ASTJSON/long_type_name_identifier.json +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "c", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/mappings.json b/test/libsolidity/ASTJSON/mappings.json index f3951853e..16904ec8a 100644 --- a/test/libsolidity/ASTJSON/mappings.json +++ b/test/libsolidity/ASTJSON/mappings.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/modifier_definition.json b/test/libsolidity/ASTJSON/modifier_definition.json index cddfdd273..67aa3e1ea 100644 --- a/test/libsolidity/ASTJSON/modifier_definition.json +++ b/test/libsolidity/ASTJSON/modifier_definition.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/modifier_invocation.json b/test/libsolidity/ASTJSON/modifier_invocation.json index cddfdd273..67aa3e1ea 100644 --- a/test/libsolidity/ASTJSON/modifier_invocation.json +++ b/test/libsolidity/ASTJSON/modifier_invocation.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/mutability.json b/test/libsolidity/ASTJSON/mutability.json index 0adce73a7..90bbf05d5 100644 --- a/test/libsolidity/ASTJSON/mutability.json +++ b/test/libsolidity/ASTJSON/mutability.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/non_utf8.json b/test/libsolidity/ASTJSON/non_utf8.json index 5c433531a..2f7145fda 100644 --- a/test/libsolidity/ASTJSON/non_utf8.json +++ b/test/libsolidity/ASTJSON/non_utf8.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/override.json b/test/libsolidity/ASTJSON/override.json index 1eda7ba09..639572c54 100644 --- a/test/libsolidity/ASTJSON/override.json +++ b/test/libsolidity/ASTJSON/override.json @@ -22,6 +22,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "A", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -94,6 +95,7 @@ "src": "72:1:1" } ], + "canonicalName": "B", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": false, @@ -207,6 +209,7 @@ "src": "167:1:1" } ], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/placeholder_statement.json b/test/libsolidity/ASTJSON/placeholder_statement.json index 62631f003..5a6d51b21 100644 --- a/test/libsolidity/ASTJSON/placeholder_statement.json +++ b/test/libsolidity/ASTJSON/placeholder_statement.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/receive_ether.json b/test/libsolidity/ASTJSON/receive_ether.json index b36dcd338..bfcb33f6c 100644 --- a/test/libsolidity/ASTJSON/receive_ether.json +++ b/test/libsolidity/ASTJSON/receive_ether.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/short_type_name.json b/test/libsolidity/ASTJSON/short_type_name.json index a4d25b46f..b7409fbbc 100644 --- a/test/libsolidity/ASTJSON/short_type_name.json +++ b/test/libsolidity/ASTJSON/short_type_name.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "c", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json index eba4ae96d..6a0f249f8 100644 --- a/test/libsolidity/ASTJSON/short_type_name_ref.json +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "c", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/smoke.json b/test/libsolidity/ASTJSON/smoke.json index b32ff7673..4d4a02e24 100644 --- a/test/libsolidity/ASTJSON/smoke.json +++ b/test/libsolidity/ASTJSON/smoke.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/source_location.json b/test/libsolidity/ASTJSON/source_location.json index 0518c8a1b..0591a30cc 100644 --- a/test/libsolidity/ASTJSON/source_location.json +++ b/test/libsolidity/ASTJSON/source_location.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/string.json b/test/libsolidity/ASTJSON/string.json index 67f5c16f7..438f760c4 100644 --- a/test/libsolidity/ASTJSON/string.json +++ b/test/libsolidity/ASTJSON/string.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/two_base_functions.json b/test/libsolidity/ASTJSON/two_base_functions.json index dde1c5d61..2098b3cd5 100644 --- a/test/libsolidity/ASTJSON/two_base_functions.json +++ b/test/libsolidity/ASTJSON/two_base_functions.json @@ -22,6 +22,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "A", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -79,6 +80,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "B", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, @@ -164,6 +166,7 @@ "src": "117:1:1" } ], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/unicode.json b/test/libsolidity/ASTJSON/unicode.json index 937a40943..dcb267b3f 100644 --- a/test/libsolidity/ASTJSON/unicode.json +++ b/test/libsolidity/ASTJSON/unicode.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/used_errors.json b/test/libsolidity/ASTJSON/used_errors.json index 5e7d0bc3a..a8ca24a28 100644 --- a/test/libsolidity/ASTJSON/used_errors.json +++ b/test/libsolidity/ASTJSON/used_errors.json @@ -112,6 +112,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/userDefinedValueType.json b/test/libsolidity/ASTJSON/userDefinedValueType.json new file mode 100644 index 000000000..df7aa69c8 --- /dev/null +++ b/test/libsolidity/ASTJSON/userDefinedValueType.json @@ -0,0 +1,339 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 27 + ], + "MyAddress": + [ + 2 + ], + "MyUInt": + [ + 4 + ], + "f": + [ + 16 + ] + }, + "id": 28, + "nodeType": "SourceUnit", + "nodes": + [ + { + "canonicalName": "MyAddress", + "id": 2, + "name": "MyAddress", + "nameLocation": "5:9:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "0:26:1", + "underlyingType": + { + "id": 1, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "18:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + } + }, + { + "canonicalName": "MyUInt", + "id": 4, + "name": "MyUInt", + "nameLocation": "32:6:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "27:20:1", + "underlyingType": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "42:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + }, + { + "body": + { + "id": 15, + "nodeType": "Block", + "src": "61:34:1", + "statements": + [ + { + "assignments": + [ + 9 + ], + "declarations": + [ + { + "constant": false, + "id": 9, + "mutability": "mutable", + "name": "a", + "nameLocation": "77:1:1", + "nodeType": "VariableDeclaration", + "scope": 15, + "src": "67:11:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_userDefinedValueType$_MyAddress_$2", + "typeString": "MyAddress" + }, + "typeName": + { + "id": 8, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 7, + "name": "MyAddress", + "nodeType": "IdentifierPath", + "referencedDeclaration": 2, + "src": "67:9:1" + }, + "referencedDeclaration": 2, + "src": "67:9:1", + "typeDescriptions": + { + "typeIdentifier": "t_userDefinedValueType$_MyAddress_$2", + "typeString": "MyAddress" + } + }, + "visibility": "internal" + } + ], + "id": 10, + "nodeType": "VariableDeclarationStatement", + "src": "67:11:1" + }, + { + "assignments": + [ + 13 + ], + "declarations": + [ + { + "constant": false, + "id": 13, + "mutability": "mutable", + "name": "b", + "nameLocation": "91:1:1", + "nodeType": "VariableDeclaration", + "scope": 15, + "src": "84:8:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_userDefinedValueType$_MyUInt_$4", + "typeString": "MyUInt" + }, + "typeName": + { + "id": 12, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 11, + "name": "MyUInt", + "nodeType": "IdentifierPath", + "referencedDeclaration": 4, + "src": "84:6:1" + }, + "referencedDeclaration": 4, + "src": "84:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_userDefinedValueType$_MyUInt_$4", + "typeString": "MyUInt" + } + }, + "visibility": "internal" + } + ], + "id": 14, + "nodeType": "VariableDeclarationStatement", + "src": "84:8:1" + } + ] + }, + "id": 16, + "implemented": true, + "kind": "freeFunction", + "modifiers": [], + "name": "f", + "nameLocation": "57:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 5, + "nodeType": "ParameterList", + "parameters": [], + "src": "58:2:1" + }, + "returnParameters": + { + "id": 6, + "nodeType": "ParameterList", + "parameters": [], + "src": "61:0:1" + }, + "scope": 28, + "src": "48:47:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "internal" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "C", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 27, + "linearizedBaseContracts": + [ + 27 + ], + "name": "C", + "nameLocation": "105:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "canonicalName": "C.MyAddress", + "id": 18, + "name": "MyAddress", + "nameLocation": "118:9:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "113:26:1", + "underlyingType": + { + "id": 17, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "131:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": + { + "typeIdentifier": "t_address", + "typeString": "address" + } + } + }, + { + "canonicalName": "C.MyUInt", + "id": 20, + "name": "MyUInt", + "nameLocation": "149:6:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "144:20:1", + "underlyingType": + { + "id": 19, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "159:4:1", + "typeDescriptions": + { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + } + }, + { + "constant": false, + "functionSelector": "97682884", + "id": 26, + "mutability": "mutable", + "name": "m", + "nameLocation": "205:1:1", + "nodeType": "VariableDeclaration", + "scope": 27, + "src": "169:37:1", + "stateVariable": true, + "storageLocation": "default", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_userDefinedValueType$_MyAddress_$18_$_t_userDefinedValueType$_MyUInt_$20_$", + "typeString": "mapping(C.MyAddress => C.MyUInt)" + }, + "typeName": + { + "id": 25, + "keyType": + { + "id": 22, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 21, + "name": "MyAddress", + "nodeType": "IdentifierPath", + "referencedDeclaration": 18, + "src": "177:9:1" + }, + "referencedDeclaration": 18, + "src": "177:9:1", + "typeDescriptions": + { + "typeIdentifier": "t_userDefinedValueType$_MyAddress_$18", + "typeString": "C.MyAddress" + } + }, + "nodeType": "Mapping", + "src": "169:28:1", + "typeDescriptions": + { + "typeIdentifier": "t_mapping$_t_userDefinedValueType$_MyAddress_$18_$_t_userDefinedValueType$_MyUInt_$20_$", + "typeString": "mapping(C.MyAddress => C.MyUInt)" + }, + "valueType": + { + "id": 24, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 23, + "name": "MyUInt", + "nodeType": "IdentifierPath", + "referencedDeclaration": 20, + "src": "190:6:1" + }, + "referencedDeclaration": 20, + "src": "190:6:1", + "typeDescriptions": + { + "typeIdentifier": "t_userDefinedValueType$_MyUInt_$20", + "typeString": "C.MyUInt" + } + } + }, + "visibility": "public" + } + ], + "scope": 28, + "src": "96:113:1", + "usedErrors": [] + } + ], + "src": "0:210:1" +} diff --git a/test/libsolidity/ASTJSON/userDefinedValueType.sol b/test/libsolidity/ASTJSON/userDefinedValueType.sol new file mode 100644 index 000000000..f1124f8cd --- /dev/null +++ b/test/libsolidity/ASTJSON/userDefinedValueType.sol @@ -0,0 +1,13 @@ +type MyAddress is address; +type MyUInt is uint; +function f() { + MyAddress a; + MyUInt b; +} +contract C { + type MyAddress is address; + type MyUInt is uint; + mapping(MyAddress => MyUInt) public m; +} + +// ---- diff --git a/test/libsolidity/ASTJSON/userDefinedValueType_parseOnly.json b/test/libsolidity/ASTJSON/userDefinedValueType_parseOnly.json new file mode 100644 index 000000000..36315b443 --- /dev/null +++ b/test/libsolidity/ASTJSON/userDefinedValueType_parseOnly.json @@ -0,0 +1,248 @@ +{ + "absolutePath": "a", + "id": 28, + "nodeType": "SourceUnit", + "nodes": + [ + { + "id": 2, + "name": "MyAddress", + "nameLocation": "5:9:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "0:26:1", + "underlyingType": + { + "id": 1, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "18:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": {} + } + }, + { + "id": 4, + "name": "MyUInt", + "nameLocation": "32:6:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "27:20:1", + "underlyingType": + { + "id": 3, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "42:4:1", + "typeDescriptions": {} + } + }, + { + "body": + { + "id": 15, + "nodeType": "Block", + "src": "61:34:1", + "statements": + [ + { + "assignments": + [ + 9 + ], + "declarations": + [ + { + "constant": false, + "id": 9, + "mutability": "mutable", + "name": "a", + "nameLocation": "77:1:1", + "nodeType": "VariableDeclaration", + "src": "67:11:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 8, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 7, + "name": "MyAddress", + "nodeType": "IdentifierPath", + "src": "67:9:1" + }, + "src": "67:9:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 10, + "nodeType": "VariableDeclarationStatement", + "src": "67:11:1" + }, + { + "assignments": + [ + 13 + ], + "declarations": + [ + { + "constant": false, + "id": 13, + "mutability": "mutable", + "name": "b", + "nameLocation": "91:1:1", + "nodeType": "VariableDeclaration", + "src": "84:8:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 12, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 11, + "name": "MyUInt", + "nodeType": "IdentifierPath", + "src": "84:6:1" + }, + "src": "84:6:1", + "typeDescriptions": {} + }, + "visibility": "internal" + } + ], + "id": 14, + "nodeType": "VariableDeclarationStatement", + "src": "84:8:1" + } + ] + }, + "id": 16, + "implemented": true, + "kind": "freeFunction", + "modifiers": [], + "name": "f", + "nameLocation": "57:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 5, + "nodeType": "ParameterList", + "parameters": [], + "src": "58:2:1" + }, + "returnParameters": + { + "id": 6, + "nodeType": "ParameterList", + "parameters": [], + "src": "61:0:1" + }, + "src": "48:47:1", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "internal" + }, + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 27, + "name": "C", + "nameLocation": "105:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "id": 18, + "name": "MyAddress", + "nameLocation": "118:9:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "113:26:1", + "underlyingType": + { + "id": 17, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "131:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": {} + } + }, + { + "id": 20, + "name": "MyUInt", + "nameLocation": "149:6:1", + "nodeType": "UserDefinedValueTypeDefinition", + "src": "144:20:1", + "underlyingType": + { + "id": 19, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "159:4:1", + "typeDescriptions": {} + } + }, + { + "constant": false, + "id": 26, + "mutability": "mutable", + "name": "m", + "nameLocation": "205:1:1", + "nodeType": "VariableDeclaration", + "src": "169:37:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": {}, + "typeName": + { + "id": 25, + "keyType": + { + "id": 22, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 21, + "name": "MyAddress", + "nodeType": "IdentifierPath", + "src": "177:9:1" + }, + "src": "177:9:1", + "typeDescriptions": {} + }, + "nodeType": "Mapping", + "src": "169:28:1", + "typeDescriptions": {}, + "valueType": + { + "id": 24, + "nodeType": "UserDefinedTypeName", + "pathNode": + { + "id": 23, + "name": "MyUInt", + "nodeType": "IdentifierPath", + "src": "190:6:1" + }, + "src": "190:6:1", + "typeDescriptions": {} + } + }, + "visibility": "public" + } + ], + "src": "96:113:1", + "usedErrors": [] + } + ], + "src": "0:210:1" +} diff --git a/test/libsolidity/ASTJSON/using_for_directive.json b/test/libsolidity/ASTJSON/using_for_directive.json index b8a8fd3b7..b935b262b 100644 --- a/test/libsolidity/ASTJSON/using_for_directive.json +++ b/test/libsolidity/ASTJSON/using_for_directive.json @@ -18,6 +18,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "L", "contractDependencies": [], "contractKind": "library", "fullyImplemented": true, @@ -37,6 +38,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "C", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/ASTJSON/yul_hex_literal.json b/test/libsolidity/ASTJSON/yul_hex_literal.json index 54d37da00..fc1cc39db 100644 --- a/test/libsolidity/ASTJSON/yul_hex_literal.json +++ b/test/libsolidity/ASTJSON/yul_hex_literal.json @@ -14,6 +14,7 @@ { "abstract": false, "baseContracts": [], + "canonicalName": "Sample", "contractDependencies": [], "contractKind": "contract", "fullyImplemented": true, diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index bc1a5372d..84c7a5e0b 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -75,24 +75,24 @@ AnalysisFramework::parseAnalyseAndReturnError( return make_pair(&compiler().ast(""), std::move(errors)); } -ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarnings) const +ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarningsAndInfos) const { ErrorList errors; for (auto const& currentError: _errorList) { solAssert(currentError->comment(), ""); - if (currentError->type() == Error::Type::Warning) + if (!Error::isError(currentError->type())) { - if (!_includeWarnings) + if (!_includeWarningsAndInfos) continue; - bool ignoreWarning = false; + bool ignoreWarningsAndInfos = false; for (auto const& filter: m_warningsToFilter) if (currentError->comment()->find(filter) == 0) { - ignoreWarning = true; + ignoreWarningsAndInfos = true; break; } - if (ignoreWarning) + if (ignoreWarningsAndInfos) continue; } diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index 17554b9ac..c4b4eda33 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -66,8 +66,8 @@ protected: std::string const& _signature ); - // filter out the warnings in m_warningsToFilter or all warnings if _includeWarnings is false - langutil::ErrorList filterErrors(langutil::ErrorList const& _errorList, bool _includeWarnings) const; + // filter out the warnings in m_warningsToFilter or all warnings and infos if _includeWarningsAndInfos is false + langutil::ErrorList filterErrors(langutil::ErrorList const& _errorList, bool _includeWarningsAndInfos) 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"}; diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 76b1ba2a6..f12c12ca9 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -67,20 +67,20 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) GlobalContext globalContext; NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); - solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); + solAssert(!Error::containsErrors(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*sourceUnit)); - if (!Error::containsOnlyWarnings(errorReporter.errors())) + if (Error::containsErrors(errorReporter.errors())) return AssemblyItems(); for (ASTPointer const& node: sourceUnit->nodes()) { BOOST_REQUIRE_NO_THROW(declarationTypeChecker.check(*node)); - if (!Error::containsOnlyWarnings(errorReporter.errors())) + if (Error::containsErrors(errorReporter.errors())) return AssemblyItems(); } TypeChecker checker(solidity::test::CommonOptions::get().evmVersion(), errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*sourceUnit)); - if (!Error::containsOnlyWarnings(errorReporter.errors())) + if (Error::containsErrors(errorReporter.errors())) return AssemblyItems(); for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) diff --git a/test/libsolidity/ErrorCheck.cpp b/test/libsolidity/ErrorCheck.cpp index 42b527e5b..418d7cc99 100644 --- a/test/libsolidity/ErrorCheck.cpp +++ b/test/libsolidity/ErrorCheck.cpp @@ -25,6 +25,7 @@ #include #include +#include using namespace std; using namespace solidity; diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index a03ccbb1e..b3fb9d8b7 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(string_storage) if (CommonOptions::get().useABIEncoderV1) CHECK_DEPLOY_GAS(133045, 129731, evmVersion); else - CHECK_DEPLOY_GAS(144679, 121229, evmVersion); + CHECK_DEPLOY_GAS(144999, 121229, evmVersion); } // This is only correct on >=Constantinople. else if (!CommonOptions::get().useABIEncoderV1) @@ -117,9 +117,9 @@ BOOST_AUTO_TEST_CASE(string_storage) else { if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(138693, 123969, evmVersion); + CHECK_DEPLOY_GAS(139013, 123969, evmVersion); else - CHECK_DEPLOY_GAS(123301, 110969, evmVersion); + CHECK_DEPLOY_GAS(123361, 110969, evmVersion); } } else if (evmVersion < EVMVersion::istanbul()) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 19b2da3b3..0ec9b50a2 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -128,7 +128,7 @@ void parsePrintCompare(string const& _source, bool _canWarn = false) AssemblyStack stack(solidity::test::CommonOptions::get().evmVersion(), AssemblyStack::Language::Assembly, OptimiserSettings::none()); BOOST_REQUIRE(stack.parseAndAnalyze("", _source)); if (_canWarn) - BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); + BOOST_REQUIRE(!Error::containsErrors(stack.errors())); else BOOST_REQUIRE(stack.errors().empty()); string expectation = "object \"object\" {\n code " + boost::replace_all_copy(_source, "\n", "\n ") + "\n}\n"; diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index ae2dd85db..d37398637 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -58,9 +58,9 @@ optional compileAndCheckLicenseMetadata(string const& _contractName, cha BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata(_contractName); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); BOOST_REQUIRE(metadata["sources"].isMember("A.sol")); @@ -250,9 +250,9 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("A"); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); BOOST_CHECK(metadata["sources"].isMember("A")); @@ -291,9 +291,9 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("C"); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 3); BOOST_CHECK(metadata["sources"].isMember("A")); @@ -320,8 +320,8 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent) 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_REQUIRE(util::jsonParseStrict(metadata_str, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK(metadata.isMember("settings")); BOOST_CHECK(metadata["settings"].isMember("metadata")); BOOST_CHECK(metadata["settings"]["metadata"].isMember("bytecodeHash")); @@ -344,24 +344,42 @@ BOOST_AUTO_TEST_CASE(metadata_viair) } )"; - auto check = [](char const* _src, bool _viair) + 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); + 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_REQUIRE(util::jsonParseStrict(compilerStack.metadata("test"), metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK(metadata.isMember("settings")); - if (_viair) + if (_viaIR) { BOOST_CHECK(metadata["settings"].isMember("viaIR")); BOOST_CHECK(metadata["settings"]["viaIR"].asBool()); } + else + BOOST_CHECK(!metadata["settings"].isMember("viaIR")); + + BOOST_CHECK(compilerStack.cborMetadata("test") == compilerStack.cborMetadata("test", _viaIR)); + BOOST_CHECK(compilerStack.cborMetadata("test") != compilerStack.cborMetadata("test", !_viaIR)); + + map const parsedCBORMetadata = requireParsedCBORMetadata( + compilerStack.runtimeObject("test").bytecode, + CompilerStack::MetadataFormat::WithReleaseVersionTag + ); + + if (_viaIR) + { + BOOST_CHECK(parsedCBORMetadata.count("experimental") == 1); + BOOST_CHECK(parsedCBORMetadata.at("experimental") == "true"); + } + else + BOOST_CHECK(parsedCBORMetadata.count("experimental") == 0); }; check(sourceCode, true); @@ -381,9 +399,9 @@ BOOST_AUTO_TEST_CASE(metadata_revert_strings) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("A"); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["settings"]["debug"]["revertStrings"], "strip"); } @@ -439,28 +457,6 @@ BOOST_AUTO_TEST_CASE(metadata_license_gpl3_or_apache2) BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0 OR Apache-2.0"); } -BOOST_AUTO_TEST_CASE(metadata_license_ignored_unicode) -{ - char const* sourceCode = R"( - // SPDX-License-Identifier: ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ - pragma solidity >=0.0; - contract C { - } - )"; - BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt); -} - -BOOST_AUTO_TEST_CASE(metadata_license_ignored_stray_unicode) -{ - char const* sourceCode = R"( - // SPDX-License-Identifier: GPL-3.0 ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ - pragma solidity >=0.0; - contract C { - } - )"; - BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); -} - BOOST_AUTO_TEST_CASE(metadata_license_bidi_marks) { char const* sourceCode = @@ -552,15 +548,6 @@ BOOST_AUTO_TEST_CASE(metadata_license_natspec_multiline) BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); } -BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace) -{ - char const* sourceCode = R"( - //SPDX-License-Identifier:GPL-3.0 - contract C {} - )"; - BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); -} - BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace_multiline) { char const* sourceCode = R"( @@ -579,6 +566,15 @@ BOOST_AUTO_TEST_CASE(metadata_license_nonempty_line) BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); } +BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace) +{ + char const* sourceCode = R"( + //SPDX-License-Identifier:GPL-3.0 + contract C {} + )"; + BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SMTCheckerTest.cpp b/test/libsolidity/SMTCheckerTest.cpp index 67a2f7919..5606b0bfe 100644 --- a/test/libsolidity/SMTCheckerTest.cpp +++ b/test/libsolidity/SMTCheckerTest.cpp @@ -35,6 +35,7 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E else BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT \"show unproved\" choice.")); + m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::None(); auto const& choice = m_reader.stringSetting("SMTSolvers", "any"); if (choice == "any") m_modelCheckerSettings.solvers = smtutil::SMTSolverChoice::All(); @@ -65,6 +66,21 @@ SMTCheckerTest::SMTCheckerTest(string const& _filename): SyntaxTest(_filename, E m_ignoreCex = true; else BOOST_THROW_EXCEPTION(runtime_error("Invalid SMT counterexample choice.")); + + auto const& ignoreOSSetting = m_reader.stringSetting("SMTIgnoreOS", "none"); + for (string const& os: ignoreOSSetting | ranges::views::split(',') | ranges::to>()) + { +#ifdef __APPLE__ + if (os == "macos") + m_shouldRun = false; +#elif _WIN32 + if (os == "windows") + m_shouldRun = false; +#elif __linux__ + if (os == "linux") + m_shouldRun = false; +#endif + } } TestCase::TestResult SMTCheckerTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 50d7dfcc0..56e1b1a8b 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -131,7 +131,7 @@ map SemanticTest::makeBuiltins() "isoltest_builtin_test", [](FunctionCall const&) -> optional { - return util::toBigEndian(u256(0x1234)); + return toBigEndian(u256(0x1234)); } }, { @@ -139,7 +139,7 @@ map SemanticTest::makeBuiltins() [](FunctionCall const& _call) -> optional { if (_call.arguments.parameters.empty()) - return util::toBigEndian(0); + return toBigEndian(0); else return _call.arguments.rawBytes(); } @@ -154,7 +154,7 @@ map SemanticTest::makeBuiltins() address = h160(_call.arguments.parameters.at(0).rawString); else address = m_contractAddress; - return util::toBigEndian(balanceAt(address)); + return toBigEndian(balanceAt(address)); } }, { diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index b295b8091..6ee55d586 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -63,8 +63,8 @@ public: BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, - "Expected:\n" << expectedDocumentation.toStyledString() << - "\n but got:\n" << generatedDocumentation.toStyledString() + "Expected:\n" << util::jsonPrettyPrint(expectedDocumentation) << + "\n but got:\n" << util::jsonPrettyPrint(generatedDocumentation) ); } @@ -2089,8 +2089,8 @@ BOOST_AUTO_TEST_CASE(dev_explicit_inehrit_complex) BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, - "Expected:\n" << expectedDocumentation.toStyledString() << - "\n but got:\n" << generatedDocumentation.toStyledString() + "Expected:\n" << util::jsonPrettyPrint(expectedDocumentation) << + "\n but got:\n" << util::jsonPrettyPrint(generatedDocumentation) ); } diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index b9accf7bb..9c9d9a7e6 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -32,6 +32,7 @@ #include #include #include +#include using namespace std; using namespace solidity::util; diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 58024d9a6..77c01f1ea 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -74,7 +74,7 @@ bool successParse(std::string const& _source) if (Error::containsErrorOfType(errors, Error::Type::ParserError)) return false; - BOOST_CHECK(Error::containsOnlyWarnings(errors)); + BOOST_CHECK(!Error::containsErrors(errors)); return true; } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 8787d1173..8497fc0c0 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -41,6 +41,19 @@ namespace solidity::frontend::test namespace { +langutil::Error::Severity str2Severity(string const& _cat) +{ + map cats{ + {"info", langutil::Error::Severity::Info}, + {"Info", langutil::Error::Severity::Info}, + {"warning", langutil::Error::Severity::Warning}, + {"Warning", langutil::Error::Severity::Warning}, + {"error", langutil::Error::Severity::Error}, + {"Error", langutil::Error::Severity::Error} + }; + return cats.at(_cat); +} + /// Helper to match a specific error type and message bool containsError(Json::Value const& _compilerResult, string const& _type, string const& _message) { @@ -68,7 +81,7 @@ bool containsAtMostWarnings(Json::Value const& _compilerResult) { BOOST_REQUIRE(error.isObject()); BOOST_REQUIRE(error["severity"].isString()); - if (error["severity"].asString() != "warning") + if (langutil::Error::isError(str2Severity(error["severity"].asString()))) return false; } @@ -472,7 +485,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK_EQUAL( 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," + "\"baseContracts\":[],\"canonicalName\":\"A\",\"contractDependencies\":[],\"contractKind\":\"contract\",\"fullyImplemented\":true,\"id\":1," "\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nameLocation\":\"9:1:0\",\"nodeType\":\"ContractDefinition\",\"nodes\":[],\"scope\":2," "\"src\":\"0:14:0\",\"usedErrors\":[]}],\"src\":\"0:14:0\"}" ); diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index bc0f6a7d8..2a70c35ab 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -91,7 +91,7 @@ void SyntaxTest::parseAndAnalyze() return error->type() == Error::Type::CodeGenerationError; }); auto errorCount = count_if(errors.cbegin(), errors.cend(), [](auto const& error) { - return error->type() != Error::Type::Warning; + return Error::isError(error->type()); }); // failing compilation after successful analysis is a rare case, // it assumes that errors contain exactly one error, and the error is of type Error::Type::CodeGenerationError diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index af6eac2c4..30477aa2e 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -1114,7 +1114,6 @@ BOOST_AUTO_TEST_CASE(interfaces_and_abstract_contracts) unique_ptr compilerStack = parseAndAnalyzeContracts(R"( interface I { event Ev(uint); - modifier m() virtual; function ext1() external; function ext2() external; @@ -1126,6 +1125,8 @@ BOOST_AUTO_TEST_CASE(interfaces_and_abstract_contracts) } abstract contract C is J { + modifier m() virtual; + function ext3() external override virtual; function ext4() external { inr2();} function inr1() internal virtual; @@ -1167,7 +1168,7 @@ BOOST_AUTO_TEST_CASE(interfaces_and_abstract_contracts) {"Entry", "function C.ext4()"}, {"function C.ext4()", "function C.inr2()"}, {"function C.inr2()", "function C.inr1()"}, - {"function C.inr2()", "modifier I.m"}, + {"function C.inr2()", "modifier C.m"}, }}, {"D", { {"Entry", "function D.ext1()"}, diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index 8d84e0a29..2dbe334b3 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1259800 -// executionCost: 1308 -// totalCost: 1261108 +// codeDepositCost: 1243000 +// executionCost: 1295 +// totalCost: 1244295 // external: // a(): 2430 // b(uint256): infinite diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index 093acf49a..ca344afd0 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 680600 -// executionCost: 715 -// totalCost: 681315 +// codeDepositCost: 660800 +// executionCost: 696 +// totalCost: 661496 // external: // a(): 2285 // b(uint256): 4652 diff --git a/test/libsolidity/interface/FileReader.cpp b/test/libsolidity/interface/FileReader.cpp index fd1eb52cc..866a4435a 100644 --- a/test/libsolidity/interface/FileReader.cpp +++ b/test/libsolidity/interface/FileReader.cpp @@ -36,31 +36,36 @@ using namespace solidity::test; namespace solidity::frontend::test { +using SymlinkResolution = FileReader::SymlinkResolution; + BOOST_AUTO_TEST_SUITE(FileReaderTest) BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_absolute_path) { - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./."), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/.", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./.", resolveSymlinks), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/"), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/."), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/"), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/."), "/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b"), "/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/"), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/.", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/./a/.", resolveSymlinks), "/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b", resolveSymlinks), "/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/", resolveSymlinks), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/./b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../a/b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/.."), "/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../"), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/./b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../a/b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/..", resolveSymlinks), "/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../", resolveSymlinks), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../../"), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b/c/../../../", resolveSymlinks), "/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_relative_path) @@ -75,54 +80,60 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_relative_path) expectedPrefix = "/" / expectedPrefix.relative_path(); soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("."), expectedPrefix / "x/y/z/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./"), expectedPrefix / "x/y/z/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".//"), expectedPrefix / "x/y/z/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".."), expectedPrefix / "x/y"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../"), expectedPrefix / "x/y/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..//"), expectedPrefix / "x/y/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".", resolveSymlinks), expectedPrefix / "x/y/z/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./", resolveSymlinks), expectedPrefix / "x/y/z/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".//", resolveSymlinks), expectedPrefix / "x/y/z/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..", resolveSymlinks), expectedPrefix / "x/y"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../", resolveSymlinks), expectedPrefix / "x/y/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..//", resolveSymlinks), expectedPrefix / "x/y/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a"), expectedPrefix / "x/y/z/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/."), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a"), expectedPrefix / "x/y/z/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/."), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/.//"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./."), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././/"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b"), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/"), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a", resolveSymlinks), expectedPrefix / "x/y/z/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/.", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a", resolveSymlinks), expectedPrefix / "x/y/z/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/.", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/.//", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/./.", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/././/", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/b"), expectedPrefix / "x/y/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/b"), expectedPrefix / "x/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/b"), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("././a/b"), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/b", resolveSymlinks), expectedPrefix / "x/y/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/b", resolveSymlinks), expectedPrefix / "x/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("./a/b", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("././a/b", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/./b/"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../a/b/"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/.."), expectedPrefix / "x/y/z/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/..//"), expectedPrefix / "x/y/z/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../.."), expectedPrefix / "x/y/z/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../../"), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../..//"), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/./b/", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../a/b/", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/..", resolveSymlinks), expectedPrefix / "x/y/z/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/..//", resolveSymlinks), expectedPrefix / "x/y/z/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../..", resolveSymlinks), expectedPrefix / "x/y/z/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../../", resolveSymlinks), expectedPrefix / "x/y/z/a/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/b/c/../..//", resolveSymlinks), expectedPrefix / "x/y/z/a/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/.././../p/../q/../a/b"), expectedPrefix / "a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/.././../p/../q/../a/b", resolveSymlinks), expectedPrefix / "a/b"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_redundant_slashes) { - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("///"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////"), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("///", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////", resolveSymlinks), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////a/b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a//b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a////b/"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b//"), "/a/b/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b////"), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("////a/b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a//b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a////b/", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b//", resolveSymlinks), "/a/b/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/b////", resolveSymlinks), "/a/b/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_unc_path) @@ -134,23 +145,26 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_unc_path) boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::current_path().relative_path(); soltestAssert(expectedWorkDir.is_absolute() || expectedWorkDir.root_path() == "/", ""); - // UNC paths start with // or \\ followed by a name. They are used for network shares on Windows. - // On UNIX systems they are not supported but still treated in a special way. - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/"), "//host/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b"), "//host/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b/"), "//host/a/b/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + // UNC paths start with // or \\ followed by a name. They are used for network shares on Windows. + // On UNIX systems they are not supported but still treated in a special way. + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/", resolveSymlinks), "//host/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b", resolveSymlinks), "//host/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host/a/b/", resolveSymlinks), "//host/a/b/"); #if defined(_WIN32) - // On Windows an UNC path can also start with \\ instead of // - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/"), "//host/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b"), "//host/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/"), "//host/a/b/"); + // On Windows an UNC path can also start with \\ instead of // + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/", resolveSymlinks), "//host/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b", resolveSymlinks), "//host/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/", resolveSymlinks), "//host/a/b/"); #else - // On UNIX systems it's just a fancy relative path instead - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/"), expectedWorkDir / "\\\\host/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b"), expectedWorkDir / "\\\\host/a/b"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/"), expectedWorkDir / "\\\\host/a/b/"); + // On UNIX systems it's just a fancy relative path instead + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/", resolveSymlinks), expectedWorkDir / "\\\\host/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b", resolveSymlinks), expectedWorkDir / "\\\\host/a/b"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("\\\\host/a/b/", resolveSymlinks), expectedWorkDir / "\\\\host/a/b/"); #endif + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_root_name_only) @@ -167,20 +181,23 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_root_name_only) // C:\ represents the root directory of drive C: but C: on its own refers to the current working // directory. - // UNC paths - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//"), "//" / expectedWorkDir); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host"), "//host" / expectedWorkDir); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + // UNC paths + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//", resolveSymlinks), "//" / expectedWorkDir); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("//host", resolveSymlinks), "//host" / expectedWorkDir); - // On UNIX systems root name is empty. - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(""), expectedWorkDir); + // On UNIX systems root name is empty. + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("", resolveSymlinks), expectedWorkDir); #if defined(_WIN32) - boost::filesystem::path driveLetter = boost::filesystem::current_path().root_name(); - solAssert(!driveLetter.empty(), ""); - solAssert(driveLetter.is_relative(), ""); + boost::filesystem::path driveLetter = boost::filesystem::current_path().root_name(); + solAssert(!driveLetter.empty(), ""); + solAssert(driveLetter.is_relative(), ""); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(driveLetter), expectedWorkDir); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(driveLetter, resolveSymlinks), expectedWorkDir); #endif + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_stripping_root_name) @@ -193,41 +210,50 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_stripping_root_name) soltestAssert(!boost::filesystem::current_path().root_name().empty(), ""); #endif - boost::filesystem::path normalizedPath = FileReader::normalizeCLIPathForVFS(boost::filesystem::current_path()); - BOOST_CHECK_EQUAL(normalizedPath, "/" / boost::filesystem::current_path().relative_path()); - BOOST_TEST(normalizedPath.root_name().empty()); - BOOST_CHECK_EQUAL(normalizedPath.root_directory(), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + boost::filesystem::path normalizedPath = FileReader::normalizeCLIPathForVFS( + boost::filesystem::current_path(), + resolveSymlinks + ); + BOOST_CHECK_EQUAL(normalizedPath, "/" / boost::filesystem::current_path().relative_path()); + BOOST_TEST(normalizedPath.root_name().empty()); + BOOST_CHECK_EQUAL(normalizedPath.root_directory(), "/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_path_beyond_root) { TemporaryWorkingDirectory tempWorkDir("/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../../b/../.."), "/"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../.", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/../../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("/a/../../b/../..", resolveSymlinks), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(".."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../"), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a"), "/a"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../.."), "/"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../../b/../.."), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../.", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a", resolveSymlinks), "/a"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("../../a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../..", resolveSymlinks), "/"); + BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("a/../../b/../..", resolveSymlinks), "/"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_case_sensitivity) @@ -235,22 +261,31 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_case_sensitivity) TemporaryDirectory tempDir(TEST_CASE_NAME); TemporaryWorkingDirectory tempWorkDir(tempDir); - boost::filesystem::path expectedPrefix = "/" / tempDir.path().relative_path(); - soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); + boost::filesystem::path workDirNoSymlinks = boost::filesystem::weakly_canonical(tempDir); + boost::filesystem::path expectedPrefix = "/" / workDirNoSymlinks.relative_path(); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc") == expectedPrefix / "abc"); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc") != expectedPrefix / "ABC"); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "ABC") != expectedPrefix / "abc"); - BOOST_TEST(FileReader::normalizeCLIPathForVFS(tempDir.path() / "ABC") == expectedPrefix / "ABC"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "abc", resolveSymlinks) == expectedPrefix / "abc"); + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "abc", resolveSymlinks) != expectedPrefix / "ABC"); + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "ABC", resolveSymlinks) != expectedPrefix / "abc"); + BOOST_TEST(FileReader::normalizeCLIPathForVFS(workDirNoSymlinks / "ABC", resolveSymlinks) == expectedPrefix / "ABC"); + } } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_path_separators) { - // Even on Windows we want / as a separator. - BOOST_TEST((FileReader::normalizeCLIPathForVFS("/a/b/c").native() == boost::filesystem::path("/a/b/c").native())); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + // Even on Windows we want / as a separator. + BOOST_TEST(( + FileReader::normalizeCLIPathForVFS("/a/b/c", resolveSymlinks).native() == + boost::filesystem::path("/a/b/c").native() + )); + } } -BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_not_resolve_symlinks) +BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_not_resolve_symlinks_unless_requested) { TemporaryDirectory tempDir({"abc/"}, TEST_CASE_NAME); soltestAssert(tempDir.path().is_absolute(), ""); @@ -258,11 +293,26 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_not_resolve_symlinks) if (!createSymlinkIfSupportedByFilesystem(tempDir.path() / "abc", tempDir.path() / "sym", true)) return; - boost::filesystem::path expectedPrefix = "/" / tempDir.path().relative_path(); - soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); + boost::filesystem::path expectedPrefixWithSymlinks = "/" / tempDir.path().relative_path(); + boost::filesystem::path expectedPrefixWithoutSymlinks = "/" / boost::filesystem::weakly_canonical(tempDir).relative_path(); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol"), expectedPrefix / "sym/contract.sol"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol"), expectedPrefix / "abc/contract.sol"); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Disabled), + expectedPrefixWithSymlinks / "sym/contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Disabled), + expectedPrefixWithSymlinks / "abc/contract.sol" + ); + + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Enabled), + expectedPrefixWithoutSymlinks / "abc/contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Enabled), + expectedPrefixWithoutSymlinks / "abc/contract.sol" + ); } BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_resolve_symlinks_in_workdir_when_path_is_relative) @@ -280,9 +330,31 @@ BOOST_AUTO_TEST_CASE(normalizeCLIPathForVFS_should_resolve_symlinks_in_workdir_w boost::filesystem::path expectedPrefix = "/" / tempDir.path().relative_path(); soltestAssert(expectedPrefix.is_absolute() || expectedPrefix.root_path() == "/", ""); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS("contract.sol"), expectedWorkDir / "contract.sol"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol"), expectedPrefix / "sym/contract.sol"); - BOOST_CHECK_EQUAL(FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol"), expectedPrefix / "abc/contract.sol"); + for (SymlinkResolution resolveSymlinks: {SymlinkResolution::Enabled, SymlinkResolution::Disabled}) + { + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS("contract.sol", resolveSymlinks), + expectedWorkDir / "contract.sol" + ); + } + + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Disabled), + expectedPrefix / "sym/contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Disabled), + expectedPrefix / "abc/contract.sol" + ); + + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "sym/contract.sol", SymlinkResolution::Enabled), + expectedWorkDir / "contract.sol" + ); + BOOST_CHECK_EQUAL( + FileReader::normalizeCLIPathForVFS(tempDir.path() / "abc/contract.sol", SymlinkResolution::Enabled), + expectedWorkDir / "contract.sol" + ); } BOOST_AUTO_TEST_CASE(isPathPrefix_file_prefix) diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol index 91d8447a9..4161cb7d8 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol @@ -24,6 +24,6 @@ contract C { // compileViaYul: also // ---- // f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb -// gas irOptimized: 203556 -// gas legacy: 206126 -// gas legacyOptimized: 203105 +// gas irOptimized: 203522 +// gas legacy: 206084 +// gas legacyOptimized: 203068 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 index f278d314a..bf57dbe6f 100644 --- 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 @@ -32,6 +32,6 @@ contract C is B { // compileViaYul: also // ---- // test() -> 77 -// gas irOptimized: 120952 -// gas legacy: 155249 -// gas legacyOptimized: 111743 +// gas irOptimized: 121699 +// gas legacy: 155221 +// gas legacyOptimized: 111678 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol index 95070c2fb..2841ef243 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol @@ -21,6 +21,6 @@ contract C { // f(uint256[][1]): 32, 32, 0 -> true // f(uint256[][1]): 32, 32, 1, 42 -> true // f(uint256[][1]): 32, 32, 8, 421, 422, 423, 424, 425, 426, 427, 428 -> true -// gas irOptimized: 177719 +// gas irOptimized: 177581 // gas legacy: 141900 // gas legacyOptimized: 121788 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 7a9eae673..596e76a66 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 @@ -14,9 +14,9 @@ contract Test { // 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 -// gas irOptimized: 189567 -// gas legacy: 211485 -// gas legacyOptimized: 206394 +// gas irOptimized: 189239 +// gas legacy: 211149 +// gas legacyOptimized: 206054 // data(uint256,uint256): 0x02, 0x02 -> 0x09 // data(uint256,uint256): 0x05, 0x01 -> 0x11 // data(uint256,uint256): 0x06, 0x00 -> FAILURE 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 index b28049ce7..05e038378 100644 --- 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 @@ -17,7 +17,7 @@ contract c { // ---- // setData1(uint256,uint256,uint256): 10, 5, 4 -> // copyStorageStorage() -> -// gas irOptimized: 111487 +// gas irOptimized: 111488 // gas legacy: 109278 // gas legacyOptimized: 109268 // getData2(uint256): 5 -> 10, 4 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 index f807b8e2c..707e25056 100644 --- a/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol @@ -46,6 +46,6 @@ contract Test { // test1() -> 3 // test2() -> 6 // test3() -> 24 -// gas irOptimized: 133742 +// gas irOptimized: 133753 // gas legacy: 134295 // gas legacyOptimized: 133383 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 index db32fc1d3..970a5dced 100644 --- 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 @@ -23,4 +23,4 @@ contract C { // compileViaYul: true // ---- // f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1 -// gas irOptimized: 330384 +// gas irOptimized: 330385 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 index 388a0d168..cee56c23b 100644 --- a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol @@ -12,9 +12,9 @@ contract Test { // 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 -// gas irOptimized: 100066 -// gas legacy: 103815 -// gas legacyOptimized: 101614 +// gas irOptimized: 99873 +// gas legacy: 103563 +// gas legacyOptimized: 101397 // data(uint256): 7 -> 8 // data(uint256): 15 -> 16 // data(uint256): 18 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol index a4c808b57..ab9ae1640 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -21,6 +21,6 @@ contract B { // compileViaYul: also // ---- // f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 -// gas irOptimized: 121038 -// gas legacy: 235339 -// gas legacyOptimized: 133299 +// gas irOptimized: 120848 +// gas legacy: 235199 +// gas legacyOptimized: 133119 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index b8b4d4e00..6545dc900 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -45,6 +45,6 @@ contract C { // compileViaYul: also // ---- // test() -> 5, 6, 7 -// gas irOptimized: 299965 -// gas legacy: 463662 -// gas legacyOptimized: 296513 +// gas irOptimized: 297690 +// gas legacy: 462080 +// gas legacyOptimized: 294938 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index bc0afd393..c55ce061d 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 -// gas irOptimized: 459815 -// gas legacy: 592626 -// gas legacyOptimized: 450224 +// gas irOptimized: 458295 +// gas legacy: 590939 +// gas legacyOptimized: 448582 diff --git a/test/libsolidity/semanticTests/enums/minmax.sol b/test/libsolidity/semanticTests/enums/minmax.sol new file mode 100644 index 000000000..3b298a6fa --- /dev/null +++ b/test/libsolidity/semanticTests/enums/minmax.sol @@ -0,0 +1,12 @@ +contract test { + enum MinMax { A, B, C, D } + + function min() public returns(uint) { return uint(type(MinMax).min); } + function max() public returns(uint) { return uint(type(MinMax).max); } +} + +// ==== +// compileViaYul: also +// ---- +// min() -> 0 +// max() -> 3 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index b15cf5deb..8a096f524 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -50,7 +50,7 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1770739 +// gas irOptimized: 1769431 // gas legacy: 2356230 // gas legacyOptimized: 1746528 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 @@ -58,7 +58,7 @@ contract test { // gas legacy: 22497 // gas legacyOptimized: 22010 // exp(uint256): 3141592653589793238 -> 23140692632779268978 -// gas irOptimized: 24245 +// gas irOptimized: 24234 // gas legacy: 25104 // gas legacyOptimized: 24258 // exp2(uint256): 3141592653589793238 -> 8824977827076287620 diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol index 98eb25470..2c9376b22 100644 --- a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -35,10 +35,10 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 526745 +// gas irOptimized: 528041 // gas legacy: 733634 -// gas legacyOptimized: 478742 +// gas legacyOptimized: 479606 // prb_pi() -> 3141592656369545286 -// gas irOptimized: 62867 +// gas irOptimized: 63027 // gas legacy: 98903 // gas legacyOptimized: 75735 diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol index b20d53f29..1fa79f382 100644 --- a/test/libsolidity/semanticTests/externalContracts/strings.sol +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -51,11 +51,11 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 776466 +// gas irOptimized: 778254 // gas legacy: 1188228 -// gas legacyOptimized: 749336 +// gas legacyOptimized: 750416 // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 -// gas irOptimized: 22723 +// gas irOptimized: 22734 // gas legacy: 23190 // gas legacyOptimized: 22508 // roundtrip(string): 0x20, 11, "hello world" -> 0x20, 11, "hello world" @@ -67,7 +67,7 @@ contract test { // gas legacy: 25716 // gas legacyOptimized: 24115 // multiconcat(string,uint256): 0x40, 3, 11, "hello world" -> 0x20, 0x58, 0x68656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f726c, 0x6468656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f72, 49027192869463622675296414541903001712009715982962058146354235762728281047040 # concatenating 3 times # -// gas irOptimized: 28962 +// gas irOptimized: 28958 // gas legacy: 31621 // gas legacyOptimized: 27914 // benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> 0x2020 diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index 9537fda7f..ddebc11b7 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -28,6 +28,6 @@ contract C { // compileViaYul: also // ---- // t() -> 9 -// gas irOptimized: 99004 +// gas irOptimized: 99010 // gas legacy: 159083 // gas legacyOptimized: 108916 diff --git a/test/libsolidity/semanticTests/immutable/immutable_signed.sol b/test/libsolidity/semanticTests/immutable/immutable_signed.sol new file mode 100644 index 000000000..752048739 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/immutable_signed.sol @@ -0,0 +1,15 @@ +contract C { + int8 immutable a = -2; + bytes2 immutable b = "ab"; + function() internal returns (uint) immutable f = g; + function viaasm() view external returns (bytes32 x, bytes32 y) { + int8 _a = a; + bytes2 _b = b; + assembly { x := _a y := _b } + } + function g() internal pure returns (uint) { return 2; } +} +// ==== +// compileViaYul: also +// ---- +// viaasm() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x6162000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/immutable/small_types_in_reverse.sol b/test/libsolidity/semanticTests/immutable/small_types_in_reverse.sol new file mode 100644 index 000000000..677b87d32 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/small_types_in_reverse.sol @@ -0,0 +1,21 @@ +contract A { + uint16 public immutable a; + uint16 public immutable b; + uint16 public immutable c; + uint16[3] public x; + constructor() { + c = 0xffff; + b = 0x0f0f; + a = 0x1234; + x = [a, b, c]; + } +} +// ==== +// compileViaYul: also +// ---- +// a() -> 4660 +// b() -> 0x0f0f +// c() -> 0xffff +// x(uint256): 0 -> 4660 +// x(uint256): 1 -> 0x0f0f +// x(uint256): 2 -> 0xffff diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol index c78627cbc..b4f03446c 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -25,6 +25,6 @@ contract B { // compileViaYul: also // ---- // g() -> 42 -// gas irOptimized: 110833 -// gas legacy: 186609 -// gas legacyOptimized: 116151 +// gas irOptimized: 105784 +// gas legacy: 185181 +// gas legacyOptimized: 114726 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign_dynamic.sol similarity index 100% rename from test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign.sol rename to test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign_dynamic.sol diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign_static.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign_static.sol new file mode 100644 index 000000000..3c0437819 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_array_assign_static.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint[2][2] calldata x) public returns (uint[2][2] memory r) { + assembly { x := 0x24 } + r = x; + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256[2][2]): 0x0, 8, 7, 6, 5 -> 8, 7, 6, 5 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_struct_assign.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_struct_assign.sol new file mode 100644 index 000000000..bc8f9ffb7 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_struct_assign.sol @@ -0,0 +1,18 @@ +pragma abicoder v2; + +contract C { + struct S { uint256 x; } + struct S2 { uint256 x; uint256 y; } + function f(S calldata s, S2 calldata s2) public pure returns (uint256 r, uint256 r2) { + assembly { + s := s2 + s2 := 4 + } + r = s.x; + r2 = s2.x; + } +} +// ==== +// compileViaYul: also +// ---- +// f((uint256),(uint256,uint256)): 0x42, 0x07, 0x77 -> 0x07, 0x42 diff --git a/test/libsolidity/semanticTests/inlineAssembly/calldata_struct_assign_and_return.sol b/test/libsolidity/semanticTests/inlineAssembly/calldata_struct_assign_and_return.sol new file mode 100644 index 000000000..56a67f383 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/calldata_struct_assign_and_return.sol @@ -0,0 +1,23 @@ +pragma abicoder v2; +contract C { + struct S { int8 x; int8 y; } + function f() internal pure returns(S calldata s) { + assembly { + s := 0x24 + } + } + function g() public pure returns(int8, int8) { + S calldata s = f(); + return (s.x, s.y); + } + function h() public pure returns(uint256) { f(); return 0x42; } + function i() public pure returns(uint256) { abi.decode(msg.data[4:], (S)); return 0x42; } +} +// ==== +// compileViaYul: also +// ---- +// g(): 0xCAFFEE, 0x42, 0x21 -> 0x42, 0x21 +// g(): 0xCAFFEE, 0x4242, 0x2121 -> FAILURE +// g(): 0xCAFFEE, 0x42 -> 0x42, 0 +// h() -> 0x42 +// i() -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_address.sol b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_address.sol new file mode 100644 index 000000000..7c3ad6d50 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_address.sol @@ -0,0 +1,19 @@ +contract C { + function testFunction() external {} + + function testYul() public returns (address adr) { + function() external fp = this.testFunction; + + assembly { + adr := fp.address + } + } + function testSol() public returns (address) { + return this.testFunction.address; + } +} +// ==== +// compileViaYul: also +// ---- +// testYul() -> 0x0fdd67305928fcac8d213d1e47bfa6165cd0b87b +// testSol() -> 0x0fdd67305928fcac8d213d1e47bfa6165cd0b87b diff --git a/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_address_assignment.sol b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_address_assignment.sol new file mode 100644 index 000000000..7550e7785 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_address_assignment.sol @@ -0,0 +1,18 @@ +contract C { + function testFunction() external {} + + function testYul(address newAddress) view public returns (address adr) { + function() external fp = this.testFunction; + + assembly { + fp.address := newAddress + } + + return fp.address; + } +} +// ==== +// compileViaYul: also +// ---- +// testYul(address): 0x1234567890 -> 0x1234567890 +// testYul(address): 0xC0FFEE3EA7 -> 0xC0FFEE3EA7 diff --git a/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_selector.sol b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_selector.sol new file mode 100644 index 000000000..99ba2370f --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_selector.sol @@ -0,0 +1,23 @@ +contract C { + function testFunction() external {} + + function testYul() public returns (uint32) { + function() external fp = this.testFunction; + uint selectorValue = 0; + + assembly { + selectorValue := fp.selector + } + + // Value is right-aligned, we shift it so it can be compared + return uint32(bytes4(bytes32(selectorValue << (256 - 32)))); + } + function testSol() public returns (uint32) { + return uint32(this.testFunction.selector); + } +} +// ==== +// compileViaYul: also +// ---- +// testYul() -> 0xe16b4a9b +// testSol() -> 0xe16b4a9b diff --git a/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_selector_assignment.sol b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_selector_assignment.sol new file mode 100644 index 000000000..3bec26382 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/external_function_pointer_selector_assignment.sol @@ -0,0 +1,18 @@ +contract C { + function testFunction() external {} + + function testYul(uint32 newSelector) view public returns (uint32) { + function() external fp = this.testFunction; + + assembly { + fp.selector := newSelector + } + + return uint32(fp.selector); + } +} +// ==== +// compileViaYul: also +// ---- +// testYul(uint32): 0x12345678 -> 0x12345678 +// testYul(uint32): 0xABCDEF00 -> 0xABCDEF00 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol index b0e6b7b10..838539628 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol @@ -33,4 +33,4 @@ contract C { // compileViaYul: true // ---- // f() -> 0, 0, 0 -// gas irOptimized: 117648 +// gas irOptimized: 117388 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/abicodec.sol b/test/libsolidity/semanticTests/userDefinedValueType/abicodec.sol new file mode 100644 index 000000000..65c604d1b --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/abicodec.sol @@ -0,0 +1,36 @@ +// A test to see if `abi.encodeWithSelector(..., (CustomType))` works as intended. +contract C { + type MyInt is int; + function f(MyInt x) external returns(MyInt a, MyInt b, MyInt c, MyInt d) { + a = MyInt.wrap(-1); + b = MyInt.wrap(0); + c = MyInt.wrap(1); + d = x; + } + function g() external returns(bool) { + (bool success1, bytes memory ret1) = address(this).call(abi.encodeWithSelector(this.f.selector, MyInt.wrap(5))); + assert(success1); + + (MyInt a1, MyInt b1, MyInt c1, MyInt d1) = abi.decode(ret1, (MyInt, MyInt, MyInt, MyInt)); + assert(MyInt.unwrap(a1) == -1); + assert(MyInt.unwrap(b1) == 0); + assert(MyInt.unwrap(c1) == 1); + assert(MyInt.unwrap(d1) == 5); + + (bool success2, bytes memory ret2) = address(this).call(abi.encodeWithSelector(this.f.selector, int(-5))); + assert(success2); + + (int a2, int b2, int c2, int d2) = abi.decode(ret2, (int, int, int, int)); + assert(a2 == -1); + assert(b2 == 0); + assert(c2 == 1); + assert(d2 == -5); + + return true; + } +} +// ==== +// compileViaYul: also +// EVMVersion: >=byzantium +// ---- +// g() -> true diff --git a/test/libsolidity/semanticTests/userDefinedValueType/assembly_access_bytes2_abicoder_v1.sol b/test/libsolidity/semanticTests/userDefinedValueType/assembly_access_bytes2_abicoder_v1.sol new file mode 100644 index 000000000..f41a9205d --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/assembly_access_bytes2_abicoder_v1.sol @@ -0,0 +1,35 @@ +pragma abicoder v1; + +type MyBytes2 is bytes2; + +contract C { + function f(MyBytes2 val) external returns (bytes2 ret) { + assembly { + ret := val + } + } + + function g(bytes2 val) external returns (bytes2 ret) { + assembly { + ret := val + } + } + + function h(uint256 val) external returns (MyBytes2) { + MyBytes2 ret; + assembly { + ret := val + } + return ret; + } + +} +// ==== +// compileViaYul: false +// ---- +// f(bytes2): "ab" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// g(bytes2): "ab" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// f(bytes2): "abcdef" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// g(bytes2): "abcdef" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// h(uint256): "ab" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// h(uint256): "abcdef" -> 0x6162000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/assembly_access_bytes2_abicoder_v2.sol b/test/libsolidity/semanticTests/userDefinedValueType/assembly_access_bytes2_abicoder_v2.sol new file mode 100644 index 000000000..6af03b847 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/assembly_access_bytes2_abicoder_v2.sol @@ -0,0 +1,35 @@ +pragma abicoder v2; + +type MyBytes2 is bytes2; + +contract C { + function f(MyBytes2 val) external returns (bytes2 ret) { + assembly { + ret := val + } + } + + function g(bytes2 val) external returns (bytes2 ret) { + assembly { + ret := val + } + } + + function h(uint256 val) external returns (MyBytes2) { + MyBytes2 ret; + assembly { + ret := val + } + return ret; + } + +} +// ==== +// compileViaYul: also +// ---- +// f(bytes2): "ab" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// g(bytes2): "ab" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// f(bytes2): "abcdef" -> FAILURE +// g(bytes2): "abcdef" -> FAILURE +// h(uint256): "ab" -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// h(uint256): "abcdef" -> 0x6162000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol new file mode 100644 index 000000000..3596d2c99 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol @@ -0,0 +1,65 @@ +pragma abicoder v2; +type MyAddress is address; + +contract C { + MyAddress[] public addresses; + function f(MyAddress[] calldata _addresses) external { + for (uint i = 0; i < _addresses.length; i++) { + MyAddress.unwrap(_addresses[i]).call(""); + } + addresses = _addresses; + } + function g(MyAddress[] memory _addresses) external { + for (uint i = 0; i < _addresses.length; i++) { + MyAddress.unwrap(_addresses[i]).call(""); + } + addresses = _addresses; + } + function test_f() external returns (bool) { + clean(); + MyAddress[] memory test = new MyAddress[](3); + test[0] = MyAddress.wrap(address(21)); + test[1] = MyAddress.wrap(address(22)); + test[2] = MyAddress.wrap(address(23)); + this.f(test); + test_equality(test); + return true; + } + function test_g() external returns (bool) { + clean(); + MyAddress[] memory test = new MyAddress[](5); + test[0] = MyAddress.wrap(address(24)); + test[1] = MyAddress.wrap(address(25)); + test[2] = MyAddress.wrap(address(26)); + test[3] = MyAddress.wrap(address(27)); + test[4] = MyAddress.wrap(address(28)); + this.g(test); + test_equality(test); + return true; + } + function clean() internal { + delete addresses; + } + function test_equality(MyAddress[] memory _addresses) internal view { + require (_addresses.length == addresses.length); + for (uint i = 0; i < _addresses.length; i++) { + require(MyAddress.unwrap(_addresses[i]) == MyAddress.unwrap(addresses[i])); + } + } +} +// ==== +// compileViaYul: also +// ---- +// test_f() -> true +// gas irOptimized: 122887 +// gas legacy: 126168 +// gas legacyOptimized: 123199 +// test_g() -> true +// gas irOptimized: 96673 +// gas legacy: 101311 +// gas legacyOptimized: 96566 +// addresses(uint256): 0 -> 0x18 +// addresses(uint256): 1 -> 0x19 +// addresses(uint256): 3 -> 0x1b +// addresses(uint256): 4 -> 0x1c +// addresses(uint256): 5 -> FAILURE diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol new file mode 100644 index 000000000..9827d6cd1 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol @@ -0,0 +1,43 @@ +pragma abicoder v2; + +type Small is uint16; +type Left is bytes2; +struct S { uint8 a; Small b; Left c; uint8 d; } + +contract C { + S public s; + Small[] public small; + Left[] public l; + function f(S calldata _s) external { + s = _s; + } + function g(Small[] calldata _small) external returns (Small[] memory) { + small = _small; + return small; + } + function h(Left[] calldata _left) external returns (Left[] memory) { + l = _left; + return l; + } +} +// ==== +// compileViaYul: also +// ---- +// s() -> 0, 0, 0x00, 0 +// f((uint8,uint16,bytes2,uint8)): 1, 0xff, "ab", 15 -> +// gas irOptimized: 44860 +// gas legacy: 47200 +// gas legacyOptimized: 44923 +// s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 +// g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 +// gas irOptimized: 69306 +// gas legacy: 75466 +// gas legacyOptimized: 74255 +// small(uint256): 0 -> 1 +// small(uint256): 1 -> 2 +// h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" +// gas irOptimized: 69510 +// gas legacy: 75156 +// gas legacyOptimized: 74342 +// l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/cleanup.sol b/test/libsolidity/semanticTests/userDefinedValueType/cleanup.sol new file mode 100644 index 000000000..771e0f80c --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/cleanup.sol @@ -0,0 +1,44 @@ +pragma abicoder v2; +type MyUInt8 is uint8; + +// Note that this wraps from a uint256 +function wrap(uint x) pure returns (MyUInt8 y) { assembly { y := x } } +function unwrap(MyUInt8 x) pure returns (uint8 y) { assembly { y := x } } + +contract C { + uint8 a; + MyUInt8 b; + uint8 c; + function ret() external returns(MyUInt8) { + return wrap(0x1ff); + } + function f(MyUInt8 x) external returns(MyUInt8) { + return x; + } + function mem() external returns (MyUInt8[] memory) { + MyUInt8[] memory x = new MyUInt8[](2); + x[0] = wrap(0x1ff); + x[1] = wrap(0xff); + require(unwrap(x[0]) == unwrap(x[1])); + assembly { + mstore(add(x, 0x20), 0x1ff) + } + require(unwrap(x[0]) == unwrap(x[1])); + return x; + } + function stor() external returns (uint8, MyUInt8, uint8) { + a = 1; + c = 2; + b = wrap(0x1ff); + return (a, b, c); + } +} + +// ==== +// compileViaYul: also +// ---- +// ret() -> 0xff +// f(uint8): 0x1ff -> FAILURE +// f(uint8): 0xff -> 0xff +// mem() -> 0x20, 2, 0xff, 0xff +// stor() -> 1, 0xff, 2 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/cleanup_abicoderv1.sol b/test/libsolidity/semanticTests/userDefinedValueType/cleanup_abicoderv1.sol new file mode 100644 index 000000000..f5de5e501 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/cleanup_abicoderv1.sol @@ -0,0 +1,44 @@ +pragma abicoder v1; +type MyUInt8 is uint8; + +// Note that this wraps from a uint256 +function wrap(uint x) pure returns (MyUInt8 y) { assembly { y := x } } +function unwrap(MyUInt8 x) pure returns (uint8 y) { assembly { y := x } } + +contract C { + uint8 a; + MyUInt8 b; + uint8 c; + function ret() external returns(MyUInt8) { + return wrap(0x1ff); + } + function f(MyUInt8 x) external returns(MyUInt8) { + return x; + } + function mem() external returns (MyUInt8[] memory) { + MyUInt8[] memory x = new MyUInt8[](2); + x[0] = wrap(0x1ff); + x[1] = wrap(0xff); + require(unwrap(x[0]) == unwrap(x[1])); + assembly { + mstore(add(x, 0x20), 0x1ff) + } + require(unwrap(x[0]) == unwrap(x[1])); + return x; + } + function stor() external returns (uint8, MyUInt8, uint8) { + a = 1; + c = 2; + b = wrap(0x1ff); + return (a, b, c); + } +} + +// ==== +// compileViaYul: false +// ---- +// ret() -> 0xff +// f(uint8): 0x1ff -> 0xff +// f(uint8): 0xff -> 0xff +// mem() -> 0x20, 2, 0x01ff, 0xff +// stor() -> 1, 0xff, 2 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/constant.sol b/test/libsolidity/semanticTests/userDefinedValueType/constant.sol new file mode 100644 index 000000000..9559c3d7a --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/constant.sol @@ -0,0 +1,13 @@ +type T is int224; +pragma solidity >= 0.0.0; +contract C { + T constant public s = T.wrap(int224(165521356710917456517261742455526507355687727119203895813322792776)); + T constant public t = s; + int224 constant public u = T.unwrap(t); +} +// ==== +// compileViaYul: also +// ---- +// s() -> 165521356710917456517261742455526507355687727119203895813322792776 +// t() -> 165521356710917456517261742455526507355687727119203895813322792776 +// u() -> 165521356710917456517261742455526507355687727119203895813322792776 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/conversion.sol b/test/libsolidity/semanticTests/userDefinedValueType/conversion.sol new file mode 100644 index 000000000..bbf5dcf29 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/conversion.sol @@ -0,0 +1,57 @@ +pragma abicoder v2; + +type MyUInt8 is uint8; +type MyInt8 is int8; +type MyUInt16 is uint16; + +contract C { + function f(uint a) external returns(MyUInt8) { + return MyUInt8.wrap(uint8(a)); + } + function g(uint a) external returns(MyInt8) { + return MyInt8.wrap(int8(int(a))); + } + function h(MyUInt8 a) external returns (MyInt8) { + return MyInt8.wrap(int8(MyUInt8.unwrap(a))); + } + function i(MyUInt8 a) external returns(MyUInt16) { + return MyUInt16.wrap(MyUInt8.unwrap(a)); + } + function j(MyUInt8 a) external returns (uint) { + return MyUInt8.unwrap(a); + } + function k(MyUInt8 a) external returns (MyUInt16) { + return MyUInt16.wrap(MyUInt8.unwrap(a)); + } + function m(MyUInt16 a) external returns (MyUInt8) { + return MyUInt8.wrap(uint8(MyUInt16.unwrap(a))); + } +} + +// ==== +// compileViaYul: also +// ---- +// f(uint256): 1 -> 1 +// f(uint256): 2 -> 2 +// f(uint256): 257 -> 1 +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 2 +// g(uint256): 255 -> -1 +// g(uint256): 257 -> 1 +// h(uint8): 1 -> 1 +// h(uint8): 2 -> 2 +// h(uint8): 255 -> -1 +// h(uint8): 257 -> FAILURE +// i(uint8): 250 -> 250 +// j(uint8): 1 -> 1 +// j(uint8): 2 -> 2 +// j(uint8): 255 -> 0xff +// j(uint8): 257 -> FAILURE +// k(uint8): 1 -> 1 +// k(uint8): 2 -> 2 +// k(uint8): 255 -> 0xff +// k(uint8): 257 -> FAILURE +// m(uint16): 1 -> 1 +// m(uint16): 2 -> 2 +// m(uint16): 255 -> 0xff +// m(uint16): 257 -> 1 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/conversion_abicoderv1.sol b/test/libsolidity/semanticTests/userDefinedValueType/conversion_abicoderv1.sol new file mode 100644 index 000000000..af91aab0c --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/conversion_abicoderv1.sol @@ -0,0 +1,57 @@ +pragma abicoder v1; + +type MyUInt8 is uint8; +type MyInt8 is int8; +type MyUInt16 is uint16; + +contract C { + function f(uint a) external returns(MyUInt8) { + return MyUInt8.wrap(uint8(a)); + } + function g(uint a) external returns(MyInt8) { + return MyInt8.wrap(int8(int(a))); + } + function h(MyUInt8 a) external returns (MyInt8) { + return MyInt8.wrap(int8(MyUInt8.unwrap(a))); + } + function i(MyUInt8 a) external returns(MyUInt16) { + return MyUInt16.wrap(MyUInt8.unwrap(a)); + } + function j(MyUInt8 a) external returns (uint) { + return MyUInt8.unwrap(a); + } + function k(MyUInt8 a) external returns (MyUInt16) { + return MyUInt16.wrap(MyUInt8.unwrap(a)); + } + function m(MyUInt16 a) external returns (MyUInt8) { + return MyUInt8.wrap(uint8(MyUInt16.unwrap(a))); + } +} + +// ==== +// compileViaYul: false +// ---- +// f(uint256): 1 -> 1 +// f(uint256): 2 -> 2 +// f(uint256): 257 -> 1 +// g(uint256): 1 -> 1 +// g(uint256): 2 -> 2 +// g(uint256): 255 -> -1 +// g(uint256): 257 -> 1 +// h(uint8): 1 -> 1 +// h(uint8): 2 -> 2 +// h(uint8): 255 -> -1 +// h(uint8): 257 -> 1 +// i(uint8): 250 -> 250 +// j(uint8): 1 -> 1 +// j(uint8): 2 -> 2 +// j(uint8): 255 -> 0xff +// j(uint8): 257 -> 1 +// k(uint8): 1 -> 1 +// k(uint8): 2 -> 2 +// k(uint8): 255 -> 0xff +// k(uint8): 257 -> 1 +// m(uint16): 1 -> 1 +// m(uint16): 2 -> 2 +// m(uint16): 255 -> 0xff +// m(uint16): 257 -> 1 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/dirty_slot.sol b/test/libsolidity/semanticTests/userDefinedValueType/dirty_slot.sol new file mode 100644 index 000000000..a74d02ff1 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/dirty_slot.sol @@ -0,0 +1,37 @@ +type MyUInt16 is uint16; +type MyBytes2 is bytes2; +contract C { + MyUInt16 public a = MyUInt16.wrap(13); + MyBytes2 public b = MyBytes2.wrap(bytes2(uint16(1025))); + bytes2 public x; + function write_a() external { + uint max = 0xf00e0bbc0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0c0ba098076054032001; + assembly { + sstore(a.slot, max) + } + } + function write_b() external { + uint max = 0xf00e0bbc0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0e0c0ba098076054032001; + assembly { + sstore(b.slot, max) + } + } + function get_b(uint index) public returns (bytes1) { + return MyBytes2.unwrap(b)[index]; + } +} +// ==== +// compileViaYul: also +// ---- +// a() -> 13 +// b() -> 0x0401000000000000000000000000000000000000000000000000000000000000 +// get_b(uint256): 0 -> 0x0400000000000000000000000000000000000000000000000000000000000000 +// get_b(uint256): 1 -> 0x0100000000000000000000000000000000000000000000000000000000000000 +// get_b(uint256): 2 -> FAILURE, hex"4e487b71", 0x32 +// write_a() -> +// a() -> 0x2001 +// write_b() -> +// b() -> 0x5403000000000000000000000000000000000000000000000000000000000000 +// get_b(uint256): 0 -> 0x5400000000000000000000000000000000000000000000000000000000000000 +// get_b(uint256): 1 -> 0x0300000000000000000000000000000000000000000000000000000000000000 +// get_b(uint256): 2 -> FAILURE, hex"4e487b71", 0x32 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/dirty_uint8_read.sol b/test/libsolidity/semanticTests/userDefinedValueType/dirty_uint8_read.sol new file mode 100644 index 000000000..4100e17f8 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/dirty_uint8_read.sol @@ -0,0 +1,26 @@ +type MyInt8 is int8; +contract C { + MyInt8 public x = MyInt8.wrap(-5); + + /// The most significant bit is flipped to 0 + function create_dirty_slot() external { + uint mask = 2**255 -1; + assembly { + let value := sload(x.slot) + sstore(x.slot, and(mask, value)) + } + } + + function read_unclean_value() external returns (bytes32 ret) { + MyInt8 value = x; + assembly { + ret := value + } + } +} +// ==== +// compileViaYul: also +// ---- +// x() -> -5 +// create_dirty_slot() -> +// read_unclean_value() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb diff --git a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol new file mode 100644 index 000000000..155a4bd87 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol @@ -0,0 +1,147 @@ +pragma abicoder v2; +// A rewrite of the test/libsolidity/semanticTests/various/erc20.sol, but using user defined value +// types. + +// User defined type name. Indicating a type with 18 decimals. +type UFixed18 is uint256; + +library FixedMath +{ + function add(UFixed18 a, UFixed18 b) internal pure returns (UFixed18 c) { + return UFixed18.wrap(UFixed18.unwrap(a) + UFixed18.unwrap(b)); + } + function sub(UFixed18 a, UFixed18 b) internal pure returns (UFixed18 c) { + return UFixed18.wrap(UFixed18.unwrap(a) - UFixed18.unwrap(b)); + } +} + +contract ERC20 { + using FixedMath for UFixed18; + + event Transfer(address indexed from, address indexed to, UFixed18 value); + event Approval(address indexed owner, address indexed spender, UFixed18 value); + + mapping (address => UFixed18) private _balances; + mapping (address => mapping (address => UFixed18)) private _allowances; + UFixed18 private _totalSupply; + + constructor() { + _mint(msg.sender, UFixed18.wrap(20)); + } + + function totalSupply() public view returns (UFixed18) { + return _totalSupply; + } + + function balanceOf(address owner) public view returns (UFixed18) { + return _balances[owner]; + } + + function allowance(address owner, address spender) public view returns (UFixed18) { + return _allowances[owner][spender]; + } + + function transfer(address to, UFixed18 value) public returns (bool) { + _transfer(msg.sender, to, value); + return true; + } + + function approve(address spender, UFixed18 value) public returns (bool) { + _approve(msg.sender, spender, value); + return true; + } + + function transferFrom(address from, address to, UFixed18 value) public returns (bool) { + _transfer(from, to, value); + // The subtraction here will revert on overflow. + _approve(from, msg.sender, _allowances[from][msg.sender].sub(value)); + return true; + } + + function increaseAllowance(address spender, UFixed18 addedValue) public returns (bool) { + // The addition here will revert on overflow. + _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); + return true; + } + + function decreaseAllowance(address spender, UFixed18 subtractedValue) public returns (bool) { + // The subtraction here will revert on overflow. + _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue)); + return true; + } + + function _transfer(address from, address to, UFixed18 value) internal { + require(to != address(0), "ERC20: transfer to the zero address"); + + // The subtraction and addition here will revert on overflow. + _balances[from] = _balances[from].sub(value); + _balances[to] = _balances[to].add(value); + emit Transfer(from, to, value); + } + + function _mint(address account, UFixed18 value) internal { + require(account != address(0), "ERC20: mint to the zero address"); + + // The additions here will revert on overflow. + _totalSupply = _totalSupply.add(value); + _balances[account] = _balances[account].add(value); + emit Transfer(address(0), account, value); + } + + function _burn(address account, UFixed18 value) internal { + require(account != address(0), "ERC20: burn from the zero address"); + + // The subtractions here will revert on overflow. + _totalSupply = _totalSupply.sub(value); + _balances[account] = _balances[account].sub(value); + emit Transfer(account, address(0), value); + } + + function _approve(address owner, address spender, UFixed18 value) internal { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = value; + emit Approval(owner, spender, value); + } + + function _burnFrom(address account, UFixed18 value) internal { + _burn(account, value); + _approve(account, msg.sender, _allowances[account][msg.sender].sub(value)); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 +// gas irOptimized: 462361 +// gas legacy: 861547 +// gas legacyOptimized: 420959 +// totalSupply() -> 20 +// gas irOptimized: 23378 +// gas legacy: 23653 +// gas legacyOptimized: 23368 +// transfer(address,uint256): 2, 5 -> true +// ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05 +// gas irOptimized: 48503 +// gas legacy: 49572 +// gas legacyOptimized: 48575 +// decreaseAllowance(address,uint256): 2, 0 -> true +// ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00 +// gas irOptimized: 26327 +// gas legacy: 27204 +// gas legacyOptimized: 26317 +// decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11 +// gas irOptimized: 24040 +// gas legacy: 24506 +// gas legacyOptimized: 24077 +// transfer(address,uint256): 2, 14 -> true +// ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e +// gas irOptimized: 28603 +// gas legacy: 29672 +// gas legacyOptimized: 28675 +// transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11 +// gas irOptimized: 24052 +// gas legacy: 24492 +// gas legacyOptimized: 24074 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/fixedpoint.sol b/test/libsolidity/semanticTests/userDefinedValueType/fixedpoint.sol new file mode 100644 index 000000000..47140a773 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/fixedpoint.sol @@ -0,0 +1,56 @@ +// Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type. +type UFixed256x18 is uint256; + +/// A minimal library to do fixed point operations on UFixed256x18. +library FixedMath { + uint constant multiplier = 10**18; + /// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked arithmetic on + /// uint256. + function add(UFixed256x18 a, UFixed256x18 b) internal returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b)); + } + /// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked arithmetic on + /// uint256. + function mul(UFixed256x18 a, uint256 b) internal returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b); + } + /// Take the floor of a UFixed256x18 number. + /// @return the largest integer that does not exceed `a`. + function floor(UFixed256x18 a) internal returns (uint256) { + return UFixed256x18.unwrap(a) / multiplier; + } + /// Turns a uint256 into a UFixed256x18 of the same value. + /// Reverts if the integer is too large. + function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(a * multiplier); + } +} + +contract TestFixedMath { + function add(UFixed256x18 a, UFixed256x18 b) external returns (UFixed256x18) { + return FixedMath.add(a, b); + } + function mul(UFixed256x18 a, uint256 b) external returns (UFixed256x18) { + return FixedMath.mul(a, b); + } + function floor(UFixed256x18 a) external returns (uint256) { + return FixedMath.floor(a); + } + function toUFixed256x18(uint256 a) external returns (UFixed256x18) { + return FixedMath.toUFixed256x18(a); + } +} +// ==== +// compileViaYul: also +// ---- +// add(uint256,uint256): 0, 0 -> 0 +// add(uint256,uint256): 25, 45 -> 0x46 +// add(uint256,uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935, 10 -> FAILURE, hex"4e487b71", 0x11 +// mul(uint256,uint256): 340282366920938463463374607431768211456, 45671926166590716193865151022383844364247891968 -> FAILURE, hex"4e487b71", 0x11 +// mul(uint256,uint256): 340282366920938463463374607431768211456, 20 -> 6805647338418769269267492148635364229120 +// floor(uint256): 11579208923731619542357098500868790785326998665640564039457584007913129639930 -> 11579208923731619542357098500868790785326998665640564039457 +// floor(uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935 -> 115792089237316195423570985008687907853269984665640564039457 +// toUFixed256x18(uint256): 0 -> 0 +// toUFixed256x18(uint256): 5 -> 5000000000000000000 +// toUFixed256x18(uint256): 115792089237316195423570985008687907853269984665640564039457 -> 115792089237316195423570985008687907853269984665640564039457000000000000000000 +// toUFixed256x18(uint256): 115792089237316195423570985008687907853269984665640564039458 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/immutable_signed.sol b/test/libsolidity/semanticTests/userDefinedValueType/immutable_signed.sol new file mode 100644 index 000000000..8923984a4 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/immutable_signed.sol @@ -0,0 +1,21 @@ +type MyInt is int16; +type MyBytes is bytes2; +contract C { + MyInt immutable a = MyInt.wrap(-2); + MyBytes immutable b = MyBytes.wrap("ab"); + function() internal returns (uint) immutable f = g; + function direct() view external returns (MyInt, MyBytes) { + return (a, b); + } + function viaasm() view external returns (bytes32 x, bytes32 y) { + MyInt _a = a; + MyBytes _b = b; + assembly { x := _a y := _b } + } + function g() internal pure returns (uint) { return 2; } +} +// ==== +// compileViaYul: also +// ---- +// direct() -> -2, 0x6162000000000000000000000000000000000000000000000000000000000000 +// viaasm() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x6162000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/in_parenthesis.sol b/test/libsolidity/semanticTests/userDefinedValueType/in_parenthesis.sol new file mode 100644 index 000000000..0691a4c3a --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/in_parenthesis.sol @@ -0,0 +1,13 @@ +type MyInt is int; +contract C { + function f() public returns (MyInt a, int b) { + (MyInt).wrap; + a = (MyInt).wrap(5); + (MyInt).unwrap; + b = (MyInt).unwrap((MyInt).wrap(10)); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 5, 10 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/mapping_key.sol b/test/libsolidity/semanticTests/userDefinedValueType/mapping_key.sol new file mode 100644 index 000000000..e698308b4 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/mapping_key.sol @@ -0,0 +1,18 @@ +type MyInt is int; +contract C { + mapping(MyInt => int) public m; + function set(MyInt key, int value) external { + m[key] = value; + } + function set_unwrapped(int key, int value) external { + m[MyInt.wrap(key)] = value; + } +} +// ==== +// compileViaYul: also +// ---- +// set(int256,int256): 1, 1 -> +// m(int256): 1 -> 1 +// set_unwrapped(int256,int256): 1, 2 -> +// m(int256): 1 -> 2 +// m(int256): 2 -> 0 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol new file mode 100644 index 000000000..e1a7c1c5b --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol @@ -0,0 +1,43 @@ +pragma abicoder v2; + +type Small is uint16; +type Left is bytes2; +struct S { uint8 a; Small b; Left c; uint8 d; } + +contract C { + S public s; + Small[] public small; + Left[] public l; + function f(S memory _s) public { + s = _s; + } + function g(Small[] memory _small) public returns (Small[] memory) { + small = _small; + return small; + } + function h(Left[] memory _left) public returns (Left[] memory) { + l = _left; + return l; + } +} +// ==== +// compileViaYul: also +// ---- +// s() -> 0, 0, 0x00, 0 +// f((uint8,uint16,bytes2,uint8)): 1, 0xff, "ab", 15 -> +// gas irOptimized: 44551 +// gas legacy: 46213 +// gas legacyOptimized: 44671 +// s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 +// g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 +// gas irOptimized: 69671 +// gas legacy: 76557 +// gas legacyOptimized: 74834 +// small(uint256): 0 -> 1 +// small(uint256): 1 -> 2 +// h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" +// gas irOptimized: 69928 +// gas legacy: 76238 +// gas legacyOptimized: 74921 +// l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 +// l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/multisource.sol b/test/libsolidity/semanticTests/userDefinedValueType/multisource.sol new file mode 100644 index 000000000..67069157c --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/multisource.sol @@ -0,0 +1,14 @@ +==== Source: A ==== +type MyInt is int; +type MyAddress is address; +==== Source: B ==== +import {MyInt, MyAddress as OurAddress} from "A"; +contract A { + function f(int x) external view returns(MyInt) { return MyInt.wrap(x); } + function f(address x) external view returns(OurAddress) { return OurAddress.wrap(x); } +} +// ==== +// compileViaYul: also +// ---- +// f(int256): 5 -> 5 +// f(address): 1 -> 1 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/multisource_module.sol b/test/libsolidity/semanticTests/userDefinedValueType/multisource_module.sol new file mode 100644 index 000000000..2d2833059 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/multisource_module.sol @@ -0,0 +1,13 @@ +==== Source: s1.sol ==== +type MyInt is int; +==== Source: s2.sol ==== +import "s1.sol" as M; +contract C { + function f(int x) public pure returns (M.MyInt) { return M.MyInt.wrap(x); } + function g(M.MyInt x) public pure returns (int) { return M.MyInt.unwrap(x); } +} +// ==== +// compileViaYul: also +// ---- +// f(int256): 5 -> 5 +// g(int256): 1 -> 1 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/ownable.sol b/test/libsolidity/semanticTests/userDefinedValueType/ownable.sol new file mode 100644 index 000000000..3f31acaca --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/ownable.sol @@ -0,0 +1,32 @@ +// Implementation of OpenZepplin's +// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol +// using user defined value types. + +contract Ownable { + type Owner is address; + Owner public owner = Owner.wrap(msg.sender); + error OnlyOwner(); + modifier onlyOwner() { + if (Owner.unwrap(owner) != msg.sender) + revert OnlyOwner(); + + _; + } + event OwnershipTransferred(Owner indexed previousOwner, Owner indexed newOwner); + function setOwner(Owner newOwner) onlyOwner external { + emit OwnershipTransferred({previousOwner: owner, newOwner: newOwner}); + owner = newOwner; + } + function renounceOwnership() onlyOwner external { + owner = Owner.wrap(address(0)); + } +} +// ==== +// compileViaYul: also +// ---- +// owner() -> 0x1212121212121212121212121212120000000012 +// setOwner(address): 0x1212121212121212121212121212120000000012 -> +// ~ emit OwnershipTransferred(address,address): #0x1212121212121212121212121212120000000012, #0x1212121212121212121212121212120000000012 +// renounceOwnership() -> +// owner() -> 0 +// setOwner(address): 0x1212121212121212121212121212120000000012 -> FAILURE, hex"5fc483c5" diff --git a/test/libsolidity/semanticTests/userDefinedValueType/parameter.sol b/test/libsolidity/semanticTests/userDefinedValueType/parameter.sol new file mode 100644 index 000000000..56cebc923 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/parameter.sol @@ -0,0 +1,42 @@ +pragma abicoder v2; + +type MyAddress is address; +contract C { + function id(MyAddress a) external returns (MyAddress b) { + b = a; + } + + function unwrap_assembly(MyAddress a) external returns (address b) { + assembly { b := a } + } + + function wrap_assembly(address a) external returns (MyAddress b) { + assembly { b := a } + } + + function unwrap(MyAddress a) external returns (address b) { + b = MyAddress.unwrap(a); + } + function wrap(address a) external returns (MyAddress b) { + b = MyAddress.wrap(a); + } + +} +// ==== +// compileViaYul: also +// ---- +// id(address): 5 -> 5 +// id(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff +// id(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE +// unwrap(address): 5 -> 5 +// unwrap(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff +// unwrap(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE +// wrap(address): 5 -> 5 +// wrap(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff +// wrap(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE +// unwrap_assembly(address): 5 -> 5 +// unwrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff +// unwrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE +// wrap_assembly(address): 5 -> 5 +// wrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffff +// wrap_assembly(address): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE diff --git a/test/libsolidity/semanticTests/userDefinedValueType/simple.sol b/test/libsolidity/semanticTests/userDefinedValueType/simple.sol new file mode 100644 index 000000000..fd5cdd07a --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/simple.sol @@ -0,0 +1,14 @@ +type MyInt is int; +contract C { + function f() external pure returns (MyInt a) { + } + function g() external pure returns (MyInt b, MyInt c) { + b = MyInt.wrap(int(1)); + c = MyInt.wrap(1); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 0 +// g() -> 1, 1 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/storage_layout.sol b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout.sol new file mode 100644 index 000000000..aab0b86d8 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout.sol @@ -0,0 +1,74 @@ +type MyInt8 is int8; +type MyAddress is address; +type MyInt96 is int96; + +contract C { + MyInt8 a; + MyInt8 b; + MyInt8 c; + MyAddress d; + + MyAddress e; + + MyAddress f; + MyInt96 g; + + function storage_a() pure external returns(uint slot, uint offset) { + assembly { + slot := a.slot + offset := a.offset + } + } + + function storage_b() pure external returns(uint slot, uint offset) { + assembly { + slot := b.slot + offset := b.offset + } + } + + function storage_c() pure external returns(uint slot, uint offset) { + assembly { + slot := d.slot + offset := c.offset + } + } + function storage_d() pure external returns(uint slot, uint offset) { + assembly { + slot := d.slot + offset := d.offset + } + } + + function storage_e() pure external returns(uint slot, uint offset) { + assembly { + slot := e.slot + offset := e.offset + } + } + + function storage_f() pure external returns(uint slot, uint offset) { + assembly { + slot := f.slot + offset := f.offset + } + } + + function storage_g() pure external returns(uint slot, uint offset) { + assembly { + slot := g.slot + offset := g.offset + } + } + +} +// ==== +// compileViaYul: also +// ---- +// storage_a() -> 0, 0 +// storage_b() -> 0, 1 +// storage_c() -> 0, 2 +// storage_d() -> 0, 3 +// storage_e() -> 1, 0 +// storage_f() -> 2, 0 +// storage_g() -> 2, 0x14 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/storage_layout_struct.sol b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout_struct.sol new file mode 100644 index 000000000..219145a57 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/storage_layout_struct.sol @@ -0,0 +1,181 @@ +type MyInt64 is int64; +struct HalfSlot { + MyInt64 a; + MyInt64 b; +} + +struct RegularHalfSlot { + int64 a; + int64 b; +} + +type MyAddress is address; +type MyInt96 is int96; +struct FullSlot { + MyInt96 a; + MyAddress b; +} +struct RegularFullSlot { + int96 a; + address b; +} + +contract C { + HalfSlot public a; + RegularHalfSlot public ra; + + HalfSlot public b; + RegularHalfSlot public rb; + + HalfSlot public c; + RegularHalfSlot public rc; + + FullSlot public d; + RegularFullSlot public rd; + + function storage_a() pure external returns(uint slot, uint offset) { + assembly { + slot := a.slot + offset := a.offset + } + } + + function storage_ra() pure external returns(uint slot, uint offset) { + assembly { + slot := ra.slot + offset := ra.offset + } + } + + function storage_b() pure external returns(uint slot, uint offset) { + assembly { + slot := b.slot + offset := b.offset + } + } + + function storage_rb() pure external returns(uint slot, uint offset) { + assembly { + slot := rb.slot + offset := rb.offset + } + } + + function storage_c() pure external returns(uint slot, uint offset) { + assembly { + slot := c.slot + offset := c.offset + } + } + + function storage_rc() pure external returns(uint slot, uint offset) { + assembly { + slot := rc.slot + offset := rc.offset + } + } + + function storage_d() pure external returns(uint slot, uint offset) { + assembly { + slot := d.slot + offset := d.offset + } + } + + function storage_rd() pure external returns(uint slot, uint offset) { + assembly { + slot := rd.slot + offset := rd.offset + } + } + + + function set_a(MyInt64 _a, MyInt64 _b) external { + a.a = _a; + a.b = _b; + } + + function set_ra(int64 _a, int64 _b) external { + ra.a = _a; + ra.b = _b; + } + + function set_b(MyInt64 _a, MyInt64 _b) external { + b.a = _a; + b.b = _b; + } + + function set_rb(int64 _a, int64 _b) external { + rb.a = _a; + rb.b = _b; + } + + function set_c(MyInt64 _a, MyInt64 _b) external { + c.a = _a; + c.b = _b; + } + + function set_rc(int64 _a, int64 _b) external { + rc.a = _a; + rc.b = _b; + } + + function set_d(MyInt96 _a, MyAddress _b) external { + d.a = _a; + d.b = _b; + } + + function set_rd(int96 _a, address _b) external { + rd.a = _a; + rd.b = _b; + } + + function read_slot(uint slot) view external returns (uint value) { + assembly { + value := sload(slot) + } + } + + function read_contents_asm() external returns (bytes32 rxa, bytes32 rya, bytes32 rxb, bytes32 ryb) { + b.a = MyInt64.wrap(-2); + b.b = MyInt64.wrap(-3); + HalfSlot memory x = b; + MyInt64 y = b.a; + MyInt64 z = b.b; + assembly { + rxa := mload(x) + rya := y + rxb := mload(add(x, 0x20)) + ryb := z + } + } +} + +// ==== +// compileViaYul: also +// ---- +// storage_a() -> 0, 0 +// set_a(int64,int64): 100, 200 -> +// read_slot(uint256): 0 -> 0xc80000000000000064 +// storage_ra() -> 1, 0 +// set_ra(int64,int64): 100, 200 -> +// read_slot(uint256): 1 -> 0xc80000000000000064 +// storage_b() -> 2, 0 +// set_b(int64,int64): 0, 200 -> +// read_slot(uint256): 2 -> 3689348814741910323200 +// storage_rb() -> 3, 0 +// set_rb(int64,int64): 0, 200 -> +// read_slot(uint256): 3 -> 3689348814741910323200 +// storage_c() -> 4, 0 +// set_c(int64,int64): 100, 0 -> +// read_slot(uint256): 4 -> 0x64 +// storage_rc() -> 5, 0 +// set_rc(int64,int64): 100, 0 -> +// read_slot(uint256): 5 -> 0x64 +// storage_d() -> 6, 0 +// set_d(int96,address): 39614081257132168796771975167, 1461501637330902918203684832716283019655932542975 -> +// read_slot(uint256): 6 -> -39614081257132168796771975169 +// storage_rd() -> 7, 0 +// set_rd(int96,address): 39614081257132168796771975167, 1461501637330902918203684832716283019655932542975 -> +// read_slot(uint256): 7 -> -39614081257132168796771975169 +// read_contents_asm() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd diff --git a/test/libsolidity/semanticTests/userDefinedValueType/storage_signed.sol b/test/libsolidity/semanticTests/userDefinedValueType/storage_signed.sol new file mode 100644 index 000000000..80745866a --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/storage_signed.sol @@ -0,0 +1,35 @@ +type MyInt is int16; +contract C { + bytes2 first = "ab"; + MyInt public a = MyInt.wrap(-2); + bytes2 third = "ef"; + function direct() external returns (MyInt) { + return a; + } + function indirect() external returns (int16) { + return MyInt.unwrap(a); + } + function toMemDirect() external returns (MyInt[1] memory) { + return [a]; + } + function toMemIndirect() external returns (int16[1] memory) { + return [MyInt.unwrap(a)]; + } + function div() external returns (int16) { + return MyInt.unwrap(a) / 2; + } + function viaasm() external returns (bytes32 x) { + MyInt st = a; + assembly { x := st } + } +} +// ==== +// compileViaYul: also +// ---- +// a() -> -2 +// direct() -> -2 +// indirect() -> -2 +// toMemDirect() -> -2 +// toMemIndirect() -> -2 +// div() -> -1 +// viaasm() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe diff --git a/test/libsolidity/semanticTests/userDefinedValueType/wrap_unwrap.sol b/test/libsolidity/semanticTests/userDefinedValueType/wrap_unwrap.sol new file mode 100644 index 000000000..c3b68e7c3 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/wrap_unwrap.sol @@ -0,0 +1,11 @@ +type MyAddress is address; +contract C { + function f() pure public { + MyAddress.wrap; + MyAddress.unwrap; + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> diff --git a/test/libsolidity/semanticTests/userDefinedValueType/wrap_unwrap_via_contract_name.sol b/test/libsolidity/semanticTests/userDefinedValueType/wrap_unwrap_via_contract_name.sol new file mode 100644 index 000000000..f6103c596 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/wrap_unwrap_via_contract_name.sol @@ -0,0 +1,24 @@ +contract C { + type T is uint; +} +contract D { + function f(C.T x) public pure returns(uint) { + return C.T.unwrap(x); + } + function g(uint x) public pure returns(C.T) { + return C.T.wrap(x); + } + function h(uint x) public pure returns(uint) { + return f(g(x)); + } + function i(C.T x) public pure returns(C.T) { + return g(f(x)); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 0x42 -> 0x42 +// g(uint256): 0x42 -> 0x42 +// h(uint256): 0x42 -> 0x42 +// i(uint256): 0x42 -> 0x42 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_elementary.sol b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_elementary.sol new file mode 100644 index 000000000..d7b9db30b --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_elementary.sol @@ -0,0 +1,37 @@ +// a test to compare the cost between using user defined value types and elementary type. See the +// test zero_cost_abstraction_userdefined.sol for a comparison. + +pragma abicoder v2; + +contract C { + int x; + function setX(int _x) external { + x = _x; + } + function getX() view external returns (int) { + return x; + } + function add(int a, int b) view external returns (int) { + return a + b; + } +} + +// ==== +// compileViaYul: also +// ---- +// getX() -> 0 +// gas irOptimized: 23353 +// gas legacy: 23479 +// gas legacyOptimized: 23311 +// setX(int256): 5 -> +// gas irOptimized: 43511 +// gas legacy: 43724 +// gas legacyOptimized: 43516 +// getX() -> 5 +// gas irOptimized: 23353 +// gas legacy: 23479 +// gas legacyOptimized: 23311 +// add(int256,int256): 200, 99 -> 299 +// gas irOptimized: 21794 +// gas legacy: 22394 +// gas legacyOptimized: 21813 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_userdefined.sol b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_userdefined.sol new file mode 100644 index 000000000..29038a861 --- /dev/null +++ b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_userdefined.sol @@ -0,0 +1,38 @@ +// a test to compare the cost between using user defined value types and elementary type. See the +// test zero_cost_abstraction_elementary.sol for comparison. + +pragma abicoder v2; + +type MyInt is int; +contract C { + int x; + function setX(MyInt _x) external { + x = MyInt.unwrap(_x); + } + function getX() view external returns (MyInt) { + return MyInt.wrap(x); + } + function add(MyInt a, MyInt b) pure external returns(MyInt) { + return MyInt.wrap(MyInt.unwrap(a) + MyInt.unwrap(b)); + } +} + +// ==== +// compileViaYul: also +// ---- +// getX() -> 0 +// gas irOptimized: 23353 +// gas legacy: 23608 +// gas legacyOptimized: 23311 +// setX(int256): 5 -> +// gas irOptimized: 43511 +// gas legacy: 43724 +// gas legacyOptimized: 43516 +// getX() -> 5 +// gas irOptimized: 23353 +// gas legacy: 23608 +// gas legacyOptimized: 23311 +// add(int256,int256): 200, 99 -> 299 +// gas irOptimized: 21794 +// gas legacy: 22523 +// gas legacyOptimized: 21813 diff --git a/test/libsolidity/semanticTests/various/erc20.sol b/test/libsolidity/semanticTests/various/erc20.sol index 61c1f0bab..6e7487ee5 100644 --- a/test/libsolidity/semanticTests/various/erc20.sol +++ b/test/libsolidity/semanticTests/various/erc20.sol @@ -98,7 +98,7 @@ contract ERC20 { // ---- // constructor() // ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 -// gas irOptimized: 460447 +// gas irOptimized: 459547 // gas legacy: 833310 // gas legacyOptimized: 416135 // totalSupply() -> 20 @@ -107,12 +107,12 @@ contract ERC20 { // gas legacyOptimized: 23368 // transfer(address,uint256): 2, 5 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05 -// gas irOptimized: 48514 +// gas irOptimized: 48503 // gas legacy: 49317 // gas legacyOptimized: 48491 // decreaseAllowance(address,uint256): 2, 0 -> true // ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00 -// gas irOptimized: 26316 +// gas irOptimized: 26327 // gas legacy: 27012 // gas legacyOptimized: 26275 // decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11 @@ -121,7 +121,7 @@ contract ERC20 { // gas legacyOptimized: 24056 // transfer(address,uint256): 2, 14 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e -// gas irOptimized: 28614 +// gas irOptimized: 28603 // gas legacy: 29417 // gas legacyOptimized: 28591 // transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/smtCheckerTests/abi/abi_decode_1_tuple.sol b/test/libsolidity/smtCheckerTests/abi/abi_decode_1_tuple.sol index e9c4e2043..d43f11b68 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_decode_1_tuple.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_decode_1_tuple.sol @@ -6,5 +6,3 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 8364: (116-125): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (115-126): Assertion checker does not yet implement type type(uint256[] memory) diff --git a/test/libsolidity/smtCheckerTests/abi/abi_decode_array.sol b/test/libsolidity/smtCheckerTests/abi/abi_decode_array.sol index 998503eb8..3c88329a3 100644 --- a/test/libsolidity/smtCheckerTests/abi/abi_decode_array.sol +++ b/test/libsolidity/smtCheckerTests/abi/abi_decode_array.sol @@ -32,20 +32,6 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 8364: (162-168): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (170-176): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (283-289): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (291-297): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (532-538): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (540-546): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (548-554): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (769-775): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (769-777): Assertion checker does not yet implement type type(uint256[] memory[] memory) -// Warning 8364: (779-785): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (779-787): Assertion checker does not yet implement type type(uint256[] memory[] memory) -// Warning 8364: (779-789): Assertion checker does not yet implement type type(uint256[] memory[] memory[] memory) -// Warning 8364: (989-995): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (997-1003): Assertion checker does not yet implement type type(uint256[] memory) // Warning 1218: (1009-1037): CHC: Error trying to invoke SMT solver. // Warning 1218: (1056-1084): CHC: Error trying to invoke SMT solver. // Warning 1218: (1103-1131): CHC: Error trying to invoke SMT solver. diff --git a/test/libsolidity/smtCheckerTests/array_members/array_push_string_literal.sol b/test/libsolidity/smtCheckerTests/array_members/array_push_string_literal.sol index f401bc11e..55b2160c4 100644 --- a/test/libsolidity/smtCheckerTests/array_members/array_push_string_literal.sol +++ b/test/libsolidity/smtCheckerTests/array_members/array_push_string_literal.sol @@ -15,4 +15,4 @@ contract C { // SMTEngine: all // ---- // Warning 6328: (139-161): CHC: Assertion violation happens here. -// Warning 6328: (263-290): CHC: Assertion violation happens here.\nCounterexample:\ndata = [1]\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.g() +// Warning 6328: (263-290): CHC: Assertion violation happens here.\nCounterexample:\ndata = [0x01]\n\nTransaction trace:\nC.constructor()\nState: data = []\nC.g() diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero.sol index 22d9ae86a..bf7fc6de9 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero.sol @@ -7,5 +7,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (40-74): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor() -// Warning 6328: (93-126): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor() +// Warning 6328: (40-74): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor(){ msg.value: 0 } +// Warning 6328: (93-126): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor(){ msg.value: 0 } diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero_2.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero_2.sol index 1bf99fca3..100f11066 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero_2.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_non_zero_2.sol @@ -10,4 +10,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (153-188): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor(){ value: 101 }\nC.f() +// Warning 6328: (153-188): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor(){ msg.value: 101 }\nC.f() diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_2.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_2.sol index f9f0093b0..c561179ec 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_2.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_2.sol @@ -15,5 +15,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4984: (266-272): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639926, once = true\n\nTransaction trace:\nC.constructor(){ value: 28100 }\nState: x = 115792089237316195423570985008687907853269984665640564039457584007913129639926, once = false\nC.f(){ value: 8 } -// Warning 6328: (235-273): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, once = true\n\nTransaction trace:\nC.constructor()\nState: x = 0, once = false\nC.f(){ value: 8 } +// Warning 4984: (266-272): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639926, once = true\n\nTransaction trace:\nC.constructor(){ msg.value: 28100 }\nState: x = 115792089237316195423570985008687907853269984665640564039457584007913129639926, once = false\nC.f(){ msg.value: 8 } +// Warning 6328: (235-273): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, once = true\n\nTransaction trace:\nC.constructor(){ msg.value: 0 }\nState: x = 0, once = false\nC.f(){ msg.value: 8 } diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_3.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_3.sol index b581a537d..169e1a066 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_3.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_3.sol @@ -6,4 +6,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (40-82): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor() +// Warning 6328: (40-82): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor(){ msg.value: 0 } diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_4.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_4.sol index 47eade523..183e60d95 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_4.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_4.sol @@ -15,7 +15,7 @@ contract C { // Warning 4984: (82-85): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 4984: (154-160): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. // Warning 4984: (212-218): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. -// Warning 6328: (180-219): CHC: Assertion violation happens here.\nCounterexample:\nc = 1\n\nTransaction trace:\nC.constructor()\nState: c = 0\nC.f(){ value: 11 }\nState: c = 1\nC.inv() +// Warning 6328: (180-219): CHC: Assertion violation happens here.\nCounterexample:\nc = 1\n\nTransaction trace:\nC.constructor()\nState: c = 0\nC.f(){ msg.value: 11 }\nState: c = 1\nC.inv() // Warning 2661: (82-85): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 2661: (154-160): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. // Warning 2661: (212-218): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_calls.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_calls.sol index 68bae9ac5..3bc569997 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_calls.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_receive_calls.sol @@ -21,6 +21,6 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (173-208): CHC: Assertion violation happens here.\nCounterexample:\nonce = true\n\nTransaction trace:\nC.constructor()\nState: once = false\nC.f(){ value: 10 } -// Warning 6328: (321-356): CHC: Assertion violation happens here.\nCounterexample:\nonce = true\n\nTransaction trace:\nC.constructor()\nState: once = false\nC.f(){ value: 10 }\n C.g(){ value: 10 } -- internal call -// Warning 6328: (469-504): CHC: Assertion violation happens here.\nCounterexample:\nonce = true\n\nTransaction trace:\nC.constructor()\nState: once = false\nC.f(){ value: 10 }\n C.g(){ value: 10 } -- internal call\n C.h(){ value: 10 } -- internal call +// Warning 6328: (173-208): CHC: Assertion violation happens here.\nCounterexample:\nonce = true\n\nTransaction trace:\nC.constructor()\nState: once = false\nC.f(){ msg.value: 10 } +// Warning 6328: (321-356): CHC: Assertion violation happens here.\nCounterexample:\nonce = true\n\nTransaction trace:\nC.constructor()\nState: once = false\nC.f(){ msg.value: 10 }\n C.g() -- internal call +// Warning 6328: (469-504): CHC: Assertion violation happens here.\nCounterexample:\nonce = true\n\nTransaction trace:\nC.constructor()\nState: once = false\nC.f(){ msg.value: 10 }\n C.g() -- internal call\n C.h() -- internal call diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_mutex_unsafe.sol b/test/libsolidity/smtCheckerTests/external_calls/call_mutex_unsafe.sol index f53178198..ad17e3d05 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/call_mutex_unsafe.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/call_mutex_unsafe.sol @@ -23,4 +23,4 @@ contract C { // SMTEngine: all // ---- // Warning 9302: (212-228): Return value of low-level calls not used. -// Warning 6328: (232-246): CHC: Assertion violation happens here.\nCounterexample:\nx = 1, lock = false\n_a = 0\ny = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, lock = false\nC.f(0)\n _a.call("aaaaa") -- untrusted external call, synthesized as:\n C.set(1) -- reentrant call +// Warning 6328: (232-246): CHC: Assertion violation happens here.\nCounterexample:\nx = 1, lock = false\n_a = 0x0\ny = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, lock = false\nC.f(0x0)\n _a.call("aaaaa") -- untrusted external call, synthesized as:\n C.set(1) -- reentrant call diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_with_value_1.sol b/test/libsolidity/smtCheckerTests/external_calls/call_with_value_1.sol new file mode 100644 index 000000000..256980225 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_with_value_1.sol @@ -0,0 +1,18 @@ +contract C { + function g(address payable i) public { + require(address(this).balance == 100); + i.call{value: 10}(""); + assert(address(this).balance == 90); // should hold + assert(address(this).balance == 100); // should fail + } +} +// ==== +// SMTEngine: all +// SMTIgnoreOS: macos +// ---- +// Warning 9302: (96-117): Return value of low-level calls not used. +// Warning 1218: (175-211): CHC: Error trying to invoke SMT solver. +// Warning 6328: (121-156): CHC: Assertion violation might happen here. +// Warning 6328: (175-211): CHC: Assertion violation might happen here. +// Warning 4661: (121-156): BMC: Assertion violation happens here. +// Warning 4661: (175-211): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_with_value_2.sol b/test/libsolidity/smtCheckerTests/external_calls/call_with_value_2.sol new file mode 100644 index 000000000..c38658080 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_with_value_2.sol @@ -0,0 +1,15 @@ +contract C { + function g(address payable i) public { + require(address(this).balance == 100); + i.call{value: 0}(""); + assert(address(this).balance == 100); // should hold + assert(address(this).balance == 20); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 9302: (96-116): Return value of low-level calls not used. +// Warning 6328: (120-156): CHC: Assertion violation might happen here. +// Warning 6328: (175-210): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 0x0\n\nTransaction trace:\nC.constructor()\nC.g(0x0)\n i.call{value: 0}("") -- untrusted external call +// Warning 4661: (120-156): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/call_with_value_3.sol b/test/libsolidity/smtCheckerTests/external_calls/call_with_value_3.sol new file mode 100644 index 000000000..6cbe24136 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/call_with_value_3.sol @@ -0,0 +1,15 @@ +contract C { + function g(address payable i) public { + require(address(this).balance > 100); + i.call{value: 20}(""); + assert(address(this).balance > 0); // should hold + // Disabled due to Spacer nondeterminism + //assert(address(this).balance == 0); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 9302: (95-116): Return value of low-level calls not used. +// Warning 6328: (120-153): CHC: Assertion violation might happen here. +// Warning 4661: (120-153): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_1.sol new file mode 100644 index 000000000..06390b12c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_1.sol @@ -0,0 +1,14 @@ +contract C { + function g() public { + require(address(this).balance == 100); + this.h{value: 10}(); + assert(address(this).balance == 100); // should hold + assert(address(this).balance == 90); // should fail + } + + function h() external payable {} +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (157-192): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_2.sol new file mode 100644 index 000000000..fc2d844d9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_this_with_value_2.sol @@ -0,0 +1,15 @@ +contract C { + function g(uint i) public { + require(address(this).balance == 100); + this.h{value: i}(); + assert(address(this).balance == 100); // should hold + assert(address(this).balance == 90); // should fail + } + + function h() external payable {} +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (162-197): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_1.sol new file mode 100644 index 000000000..a5ac8ded9 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_1.sol @@ -0,0 +1,18 @@ +interface I { + function f() external payable; +} + +contract C { + function g(I i) public { + require(address(this).balance == 100); + i.f{value: 10}(); + assert(address(this).balance == 90); // should hold + // Disabled due to Spacer nondeterminism + //assert(address(this).balance == 100); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (151-186): CHC: Assertion violation might happen here. +// Warning 4661: (151-186): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_2.sol new file mode 100644 index 000000000..4b51d9d9e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_2.sol @@ -0,0 +1,18 @@ +interface I { + function f() external payable; +} + +contract C { + function g(I i) public { + require(address(this).balance == 100); + i.f{value: 0}(); + assert(address(this).balance == 100); // should hold + assert(address(this).balance == 90); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (150-186): CHC: Assertion violation might happen here. +// Warning 6328: (205-240): CHC: Assertion violation happens here. +// Warning 4661: (150-186): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_3.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_3.sol new file mode 100644 index 000000000..d0e4c84aa --- /dev/null +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_with_value_3.sol @@ -0,0 +1,18 @@ +interface I { + function f() external payable; +} + +contract C { + function g(I i) public { + require(address(this).balance > 100); + i.f{value: 20}(); + assert(address(this).balance > 0); // should hold + assert(address(this).balance == 0); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (150-183): CHC: Assertion violation might happen here. +// Warning 6328: (202-236): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 0\n\nTransaction trace:\nC.constructor()\nC.g(0)\n i.f{value: 20}() -- untrusted external call +// Warning 4661: (150-183): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_1.sol b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_1.sol index d92de6f04..adde1eb61 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_1.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_1.sol @@ -16,4 +16,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (206-220): CHC: Assertion violation happens here.\nCounterexample:\nlocked = false\ntarget = 0\n\nTransaction trace:\nC.constructor()\nState: locked = true\nC.call(0)\n D(target).e() -- untrusted external call, synthesized as:\n C.broken() -- reentrant call +// Warning 6328: (206-220): CHC: Assertion violation happens here.\nCounterexample:\nlocked = false\ntarget = 0x0\n\nTransaction trace:\nC.constructor()\nState: locked = true\nC.call(0x0)\n D(target).e() -- untrusted external call, synthesized as:\n C.broken() -- reentrant call diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol index 92c53a5e8..19ff2bbad 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_reentrancy_2.sol @@ -13,4 +13,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (117-131): CHC: Assertion violation happens here.\nCounterexample:\nlocked = false\ntarget = 0\n\nTransaction trace:\nC.constructor()\nState: locked = true\nC.call(0)\n D(target).e() -- untrusted external call, synthesized as:\n C.call(0) -- reentrant call +// Warning 6328: (117-131): CHC: Assertion violation happens here.\nCounterexample:\nlocked = false\ntarget = 0x0\n\nTransaction trace:\nC.constructor()\nState: locked = true\nC.call(0x0)\n D(target).e() -- untrusted external call, synthesized as:\n C.call(0x0) -- reentrant call diff --git a/test/libsolidity/smtCheckerTests/file_level/constant_string_at_file_level.sol b/test/libsolidity/smtCheckerTests/file_level/constant_string_at_file_level.sol index a40fbc216..fe2ebfdde 100644 --- a/test/libsolidity/smtCheckerTests/file_level/constant_string_at_file_level.sol +++ b/test/libsolidity/smtCheckerTests/file_level/constant_string_at_file_level.sol @@ -38,4 +38,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (968-983): CHC: Assertion violation happens here.\nCounterexample:\n\nw = 56\nz = 1\nt = 44048180624707321370159228589897778088919435935156254407473833945046349512704\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n C.g() -- internal call\n C.h() -- internal call\n C.i() -- internal call +// Warning 6328: (968-983): CHC: Assertion violation happens here.\nCounterexample:\n\nw = 56\nz = 1\nt = 0x61626300ff5f5f00000000000000000000000000000000000000000000000000\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n C.g() -- internal call\n C.h() -- internal call\n C.i() -- internal call diff --git a/test/libsolidity/smtCheckerTests/file_level/file_level_call_via_module.sol b/test/libsolidity/smtCheckerTests/file_level/file_level_call_via_module.sol index 2392db620..0297428c6 100644 --- a/test/libsolidity/smtCheckerTests/file_level/file_level_call_via_module.sol +++ b/test/libsolidity/smtCheckerTests/file_level/file_level_call_via_module.sol @@ -18,7 +18,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 8364: (b.sol:95-96): Assertion checker does not yet implement type module "a.sol" -// Warning 8364: (b.sol:103-104): Assertion checker does not yet implement type module "a.sol" -// Warning 6328: (b.sol:208-222): CHC: Assertion violation happens here.\nCounterexample:\n\na = 7\nb = 3\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n a.sol:f(2) -- internal call\n a.sol:f([97, 98, 99]) -- internal call -// Warning 6328: (b.sol:274-288): CHC: Assertion violation happens here.\nCounterexample:\n\na = 7\nb = 3\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n a.sol:f(2) -- internal call\n a.sol:f([97, 98, 99]) -- internal call +// Warning 6328: (b.sol:208-222): CHC: Assertion violation happens here.\nCounterexample:\n\na = 7\nb = 3\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n a.sol:f(2) -- internal call\n a.sol:f([0x61, 0x62, 0x63]) -- internal call +// Warning 6328: (b.sol:274-288): CHC: Assertion violation happens here.\nCounterexample:\n\na = 7\nb = 3\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n a.sol:f(2) -- internal call\n a.sol:f([0x61, 0x62, 0x63]) -- internal call diff --git a/test/libsolidity/smtCheckerTests/file_level/import.sol b/test/libsolidity/smtCheckerTests/file_level/import.sol index 536e38fb1..a67ee4eb6 100644 --- a/test/libsolidity/smtCheckerTests/file_level/import.sol +++ b/test/libsolidity/smtCheckerTests/file_level/import.sol @@ -22,6 +22,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 8364: (B:115-116): Assertion checker does not yet implement type module "A" // Warning 6328: (B:238-252): CHC: Assertion violation happens here.\nCounterexample:\ndata = {x: 0}\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor()\nState: data = {x: 0}\nC.g()\n C.f(7) -- internal call\n A:set({x: 0}, 7) -- internal call\n A:set({x: 0}, 8) -- internal call // Warning 6328: (B:308-322): CHC: Assertion violation happens here.\nCounterexample:\ndata = {x: 0}\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor()\nState: data = {x: 0}\nC.g()\n C.f(7) -- internal call\n A:set({x: 0}, 7) -- internal call\n A:set({x: 0}, 8) -- internal call diff --git a/test/libsolidity/smtCheckerTests/file_level/module_constants_1.sol b/test/libsolidity/smtCheckerTests/file_level/module_constants_1.sol new file mode 100644 index 000000000..5993f5e94 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/module_constants_1.sol @@ -0,0 +1,26 @@ +==== Source: s1.sol ==== +uint constant a = 89; +==== Source: s2.sol ==== +uint constant a = 88; + +==== Source: s3.sol ==== +import "s1.sol" as M; +import "s2.sol" as N; + +contract C { + function f() internal pure returns (uint, uint) { + return (M.a, N.a); + } + function p() public pure { + (uint x, uint y) = f(); + assert(x == 89); // should hold + assert(x == 88); // should fail + assert(y == 88); // should hold + assert(y == 89); // should fail + } +} +// ==== +// SMTEngine: chc +// ---- +// Warning 6328: (s3.sol:223-238): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 89\ny = 88\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call +// Warning 6328: (s3.sol:291-306): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 89\ny = 88\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call diff --git a/test/libsolidity/smtCheckerTests/file_level/module_constants_functions_1.sol b/test/libsolidity/smtCheckerTests/file_level/module_constants_functions_1.sol new file mode 100644 index 000000000..d3b0e4c6b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/file_level/module_constants_functions_1.sol @@ -0,0 +1,46 @@ +==== Source: s1.sol ==== +uint constant a = 89; + +function fre() pure returns (uint) { + return a; +} + +==== Source: s2.sol ==== +function foo() pure returns (uint) { + return 42; +} + +==== Source: s3.sol ==== +import {fre as foo} from "s1.sol"; +import "s1.sol" as M; +import "s2.sol" as N; + +uint256 constant a = 13; + +contract C { + function f() internal pure returns (uint, uint, uint, uint) { + return (a, foo(), N.foo(), M.a); + } + function p() public pure { + (uint x, uint y, uint z, uint t) = f(); + + assert(x == 13); // should hold + assert(x == 89); // should fail + + assert(y == 89); // should hold + assert(y == 42); // should fail + + assert(z == 42); // should hold + assert(z == 89); // should fail + + assert(t == 89); // should hold + assert(t == 13); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (s3.sol:327-342): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 13\ny = 89\nz = 42\nt = 89\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n s1.sol:fre() -- internal call\n s2.sol:foo() -- internal call +// Warning 6328: (s3.sol:396-411): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 13\ny = 89\nz = 42\nt = 89\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n s1.sol:fre() -- internal call\n s2.sol:foo() -- internal call +// Warning 6328: (s3.sol:465-480): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 13\ny = 89\nz = 42\nt = 89\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n s1.sol:fre() -- internal call\n s2.sol:foo() -- internal call +// Warning 6328: (s3.sol:534-549): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 13\ny = 89\nz = 42\nt = 89\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n s1.sol:fre() -- internal call\n s2.sol:foo() -- internal call diff --git a/test/libsolidity/smtCheckerTests/file_level/overloads.sol b/test/libsolidity/smtCheckerTests/file_level/overloads.sol index 9f7e63466..7224464b4 100644 --- a/test/libsolidity/smtCheckerTests/file_level/overloads.sol +++ b/test/libsolidity/smtCheckerTests/file_level/overloads.sol @@ -15,4 +15,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (229-243): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\ny = 3\n\nTransaction trace:\nC.constructor()\nC.g()\n f(2) -- internal call\n f([97, 98, 99]) -- internal call +// Warning 6328: (229-243): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\ny = 3\n\nTransaction trace:\nC.constructor()\nC.g()\n f(2) -- internal call\n f([0x61, 0x62, 0x63]) -- internal call diff --git a/test/libsolidity/smtCheckerTests/file_level/same_constants_different_files.sol b/test/libsolidity/smtCheckerTests/file_level/same_constants_different_files.sol index 1d7760f25..948baf198 100644 --- a/test/libsolidity/smtCheckerTests/file_level/same_constants_different_files.sol +++ b/test/libsolidity/smtCheckerTests/file_level/same_constants_different_files.sol @@ -22,13 +22,10 @@ contract C { (uint x, uint y, uint z, uint t) = f(); assert(x == 13); // should hold assert(y == 89); // should hold - assert(z == 89); // should hold but the SMTChecker does not implement module access + assert(z == 89); // should hold assert(t == 89); // should hold } } // ==== // SMTEngine: all // ---- -// Warning 7650: (s2.sol:182-185): Assertion checker does not yet support this expression. -// Warning 8364: (s2.sol:182-183): Assertion checker does not yet implement type module "s1.sol" -// Warning 6328: (s2.sol:334-349): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 13\ny = 89\nz = 0\nt = 89\n\nTransaction trace:\nC.constructor()\nC.p()\n C.f() -- internal call\n s1.sol:fre() -- internal call diff --git a/test/libsolidity/smtCheckerTests/function_selector/homer.sol b/test/libsolidity/smtCheckerTests/function_selector/homer.sol index 4fec30995..552a4d895 100644 --- a/test/libsolidity/smtCheckerTests/function_selector/homer.sol +++ b/test/libsolidity/smtCheckerTests/function_selector/homer.sol @@ -43,4 +43,4 @@ contract Homer is ERC165, Simpson { // ==== // SMTEngine: all // ---- -// Warning 6328: (1340-1395): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nHomer.constructor()\nHomer.check()\n Homer.supportsInterface(1941353618) -- internal call\n Homer.supportsInterface(33540519) -- internal call\n Homer.supportsInterface(2342435274) -- internal call +// Warning 6328: (1340-1395): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nHomer.constructor()\nHomer.check()\n Homer.supportsInterface(0x73b6b492) -- internal call\n Homer.supportsInterface(0x01ffc9a7) -- internal call\n Homer.supportsInterface(0x8b9eb9ca) -- internal call diff --git a/test/libsolidity/smtCheckerTests/function_selector/selector_2.sol b/test/libsolidity/smtCheckerTests/function_selector/selector_2.sol index 7cabb7ade..c3a4b8866 100644 --- a/test/libsolidity/smtCheckerTests/function_selector/selector_2.sol +++ b/test/libsolidity/smtCheckerTests/function_selector/selector_2.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (92-126): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (92-126): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ msg.sig: 0x26121ff0 } diff --git a/test/libsolidity/smtCheckerTests/functions/getters/address.sol b/test/libsolidity/smtCheckerTests/functions/getters/address.sol index ff4032a65..92bb2761d 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/address.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/address.sol @@ -14,5 +14,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (171-197): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\na = 0\nb = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.f() -// Warning 6328: (249-275): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\na = 0\nb = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.f() +// Warning 6328: (171-197): CHC: Assertion violation happens here.\nCounterexample:\nx = 0x0, y = 0x0\na = 0x0\nb = 0x0\n\nTransaction trace:\nC.constructor()\nState: x = 0x0, y = 0x0\nC.f() +// Warning 6328: (249-275): CHC: Assertion violation happens here.\nCounterexample:\nx = 0x0, y = 0x0\na = 0x0\nb = 0x0\n\nTransaction trace:\nC.constructor()\nState: x = 0x0, y = 0x0\nC.f() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/fixed_bytes.sol b/test/libsolidity/smtCheckerTests/functions/getters/fixed_bytes.sol index fa56e15c8..ac47ded03 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/fixed_bytes.sol @@ -14,5 +14,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (159-175): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\na = 0\nb = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.f() -// Warning 6328: (227-245): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, y = 0\na = 0\nb = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0, y = 0\nC.f() +// Warning 6328: (159-175): CHC: Assertion violation happens here.\nCounterexample:\nx = 0x0, y = 0x0\na = 0x0\nb = 0x0\n\nTransaction trace:\nC.constructor()\nState: x = 0x0, y = 0x0\nC.f() +// Warning 6328: (227-245): CHC: Assertion violation happens here.\nCounterexample:\nx = 0x0, y = 0x0\na = 0x0\nb = 0x0\n\nTransaction trace:\nC.constructor()\nState: x = 0x0, y = 0x0\nC.f() diff --git a/test/libsolidity/smtCheckerTests/functions/payable_1.sol b/test/libsolidity/smtCheckerTests/functions/payable_1.sol new file mode 100644 index 000000000..c17691fa5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/payable_1.sol @@ -0,0 +1,15 @@ +contract C { + function g() external { + f(); + } + + function h() external payable { + f(); + } + + function f() internal { + require(msg.value == 0); + } +} +// ==== +// SMTEngine: all diff --git a/test/libsolidity/smtCheckerTests/functions/payable_2.sol b/test/libsolidity/smtCheckerTests/functions/payable_2.sol new file mode 100644 index 000000000..98717b133 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/payable_2.sol @@ -0,0 +1,27 @@ +contract C { + function g() external { + f(); + h(); + i(); + } + + function g2() external payable { + i(); + } + + function f() internal { + require(msg.value == 0); + } + + function h() internal { + assert(msg.value == 0); // should hold + } + + function i() internal { + assert(msg.value == 0); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (261-283): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g2(){ msg.value: 35 }\n C.i() -- internal call diff --git a/test/libsolidity/smtCheckerTests/imports/duplicated_errors_1.sol b/test/libsolidity/smtCheckerTests/imports/duplicated_errors_1.sol new file mode 100644 index 000000000..c8285a0bc --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/duplicated_errors_1.sol @@ -0,0 +1,24 @@ +==== Source: a.sol ==== +contract A { + uint x; +} +==== Source: b.sol ==== +import "a.sol"; +contract B is A { + function g() public view { assert(x > x); } +} +==== Source: c.sol ==== +import "b.sol"; +contract C is B { + function h(uint x) public pure { assert(x < x); } +} +// ==== +// SMTEngine: all +// SMTSolvers: smtlib2 +// ---- +// Warning 6328: (b.sol:62-75): CHC: Assertion violation might happen here. +// Warning 3996: CHC analysis was not possible. No Horn solver was available. None of the installed solvers was enabled. +// Warning 7812: (b.sol:62-75): BMC: Assertion violation might happen here. +// Warning 8084: BMC analysis was not possible. No SMT solver (Z3 or CVC4) was available. None of the installed solvers was enabled. +// Warning 6328: (c.sol:68-81): CHC: Assertion violation might happen here. +// Warning 7812: (c.sol:68-81): BMC: Assertion violation might happen here. diff --git a/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol b/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol index 7f109df23..8ddef2da9 100644 --- a/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol +++ b/test/libsolidity/smtCheckerTests/imports/import_as_module_2.sol @@ -18,7 +18,5 @@ function f(uint _x) pure { // ==== // SMTEngine: all // ---- -// Warning 8364: (A:118-119): Assertion checker does not yet implement type module "s1.sol" -// Warning 8364: (A:145-146): Assertion checker does not yet implement type module "s1.sol" // Warning 6328: (A:50-64): CHC: Assertion violation happens here.\nCounterexample:\n\n_y = 0\n\nTransaction trace:\nD.constructor()\nD.g(0)\n s1.sol:f(200) -- internal call\n s1.sol:f(0) -- internal call\n A:f(10) -- internal call\n A:f(0) -- internal call // Warning 6328: (s1.sol:28-44): CHC: Assertion violation happens here.\nCounterexample:\n\n_y = 0\n\nTransaction trace:\nD.constructor()\nD.g(0)\n s1.sol:f(200) -- internal call\n s1.sol:f(0) -- internal call diff --git a/test/libsolidity/smtCheckerTests/imports/import_base.sol b/test/libsolidity/smtCheckerTests/imports/import_base.sol index f0ca042be..4ad653357 100644 --- a/test/libsolidity/smtCheckerTests/imports/import_base.sol +++ b/test/libsolidity/smtCheckerTests/imports/import_base.sol @@ -20,4 +20,4 @@ contract Der is Base { // ==== // SMTEngine: all // ---- -// Warning 6328: (der:173-186): CHC: Assertion violation happens here.\nCounterexample:\nx = 3, a = 0\ny = 0\n\nTransaction trace:\nDer.constructor()\nState: x = 0, a = 0\nDer.g(0)\n Base.f() -- internal call +// Warning 6328: (der:173-186): CHC: Assertion violation happens here.\nCounterexample:\nx = 3, a = 0x0\ny = 0\n\nTransaction trace:\nDer.constructor()\nState: x = 0, a = 0x0\nDer.g(0)\n Base.f() -- internal call diff --git a/test/libsolidity/smtCheckerTests/imports/import_free_functions.sol b/test/libsolidity/smtCheckerTests/imports/import_free_functions.sol index 4eec2c76a..6e9621b30 100644 --- a/test/libsolidity/smtCheckerTests/imports/import_free_functions.sol +++ b/test/libsolidity/smtCheckerTests/imports/import_free_functions.sol @@ -20,4 +20,4 @@ contract ERC20 { // ==== // SMTEngine: all // ---- -// Warning 3944: (ERC20.sol:121-126): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\namount = 1\n\nTransaction trace:\nERC20.constructor()\nERC20.transferFrom(1)\n ERC20.sol:sub(0, 1) -- internal call +// Warning 3944: (ERC20.sol:121-126): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\namount = 1\n\nTransaction trace:\nERC20.constructor()\nERC20.transferFrom(1){ msg.sender: 0x52f6 }\n ERC20.sol:sub(0, 1) -- internal call diff --git a/test/libsolidity/smtCheckerTests/imports/import_library_2.sol b/test/libsolidity/smtCheckerTests/imports/import_library_2.sol index d5c42dad6..4d33d4002 100644 --- a/test/libsolidity/smtCheckerTests/imports/import_library_2.sol +++ b/test/libsolidity/smtCheckerTests/imports/import_library_2.sol @@ -25,4 +25,4 @@ contract ERC20 { // ==== // SMTEngine: all // ---- -// Warning 3944: (ERC20.sol:157-162): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\namount = 1\n\nTransaction trace:\nERC20.constructor()\nERC20.transferFrom(1)\n SafeMath.sub(0, 1) -- internal call +// Warning 3944: (ERC20.sol:157-162): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\namount = 1\n\nTransaction trace:\nERC20.constructor()\nERC20.transferFrom(1){ msg.sender: 0x52f6 }\n SafeMath.sub(0, 1) -- internal call diff --git a/test/libsolidity/smtCheckerTests/inheritance/receive_fallback.sol b/test/libsolidity/smtCheckerTests/inheritance/receive_fallback.sol index 9df407cd2..95ae12019 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (87-101): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.receive(){ value: 2 } +// Warning 6328: (87-101): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.receive(){ msg.value: 2 } // Warning 6328: (136-150): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nA.constructor()\nState: x = 0\nA.g() // Warning 6328: (255-269): CHC: Assertion violation happens here.\nCounterexample:\ny = 0, x = 0\n\nTransaction trace:\nB.constructor()\nState: y = 0, x = 0\nB.fallback() diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_inside_branch_assignment.sol index 8268d8520..f8091f9df 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (254-267): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, owner = 0\ny = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0, owner = 0\nC.g(1)\n C.f() -- internal call +// Warning 6328: (254-267): CHC: Assertion violation happens here.\nCounterexample:\nx = 0, owner = 0x0\ny = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0, owner = 0x0\nC.g(1)\n C.f() -- internal call 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 df6c12cba..9a578cf89 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,4 +34,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (540-554): CHC: Assertion violation happens here.\nCounterexample:\nx = 1, owner = 0\ny = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0, owner = 0\nC.g(1) +// Warning 6328: (540-554): CHC: Assertion violation happens here.\nCounterexample:\nx = 1, owner = 0x0\ny = 1\n\nTransaction trace:\nC.constructor()\nState: x = 0, owner = 0x0\nC.g(1){ msg.sender: 0x0 } diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol index e9e1dea53..6a9d4e90a 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_not_fixed_bytes.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (100-123): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 65535\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (100-123): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0xffff\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol index 125e54db5..acbe3fa6d 100644 --- a/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/bitwise_or_fixed_bytes.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (150-175): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0\nb = 255\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (150-175): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0x0\nb = 0xff\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/operators/bytes_new.sol b/test/libsolidity/smtCheckerTests/operators/bytes_new.sol index e41c8740f..b961c53d5 100644 --- a/test/libsolidity/smtCheckerTests/operators/bytes_new.sol +++ b/test/libsolidity/smtCheckerTests/operators/bytes_new.sol @@ -34,5 +34,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6368: (468-472): CHC: Out of bounds access happens here.\nCounterexample:\n\nx = [18, 52, 0]\n\nTransaction trace:\nC.constructor()\nC.h() -// Warning 6368: (490-494): CHC: Out of bounds access happens here.\nCounterexample:\n\nx = [18, 52, 0]\n\nTransaction trace:\nC.constructor()\nC.h() +// Warning 6368: (468-472): CHC: Out of bounds access happens here.\nCounterexample:\n\nx = [0x12, 0x34, 0x0]\n\nTransaction trace:\nC.constructor()\nC.h() +// Warning 6368: (490-494): CHC: Out of bounds access happens here.\nCounterexample:\n\nx = [0x12, 0x34, 0x0]\n\nTransaction trace:\nC.constructor()\nC.h() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_fixed_bytes.sol index 03e25854e..b634e8468 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_and_fixed_bytes.sol @@ -13,4 +13,4 @@ contract C { // SMTEngine: all // ---- // Warning 6321: (51-57): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. -// Warning 6328: (177-191): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0\na = 0\nb = 240\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (177-191): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0x0\na = 0x0\nb = 0xf0\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_fixed_bytes.sol index c80c3fb65..edcf3230b 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_or_fixed_bytes.sol @@ -13,4 +13,4 @@ contract C { // SMTEngine: all // ---- // Warning 6321: (51-57): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. -// Warning 6328: (177-191): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0\na = 255\nb = 255\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (177-191): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0x0\na = 0xff\nb = 0xff\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_2.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_2.sol index e142ee930..59bd6c7b9 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_2.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_2.sol @@ -14,4 +14,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (147-165): CHC: Assertion violation happens here.\nCounterexample:\n\ny = 6579559\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (147-165): CHC: Assertion violation happens here.\nCounterexample:\n\ny = 0x646567\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_3.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_3.sol index 933f15980..3ac248fb2 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_string_literal_3.sol @@ -17,5 +17,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (229-276): CHC: Assertion violation happens here.\nCounterexample:\n\ny = 43595849750559157961410616371195012376776328331498503227444818324475146035296\nz = 43595849750559157961410616371195012376776328331498503227444818324475146035296\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (394-437): CHC: Assertion violation happens here.\nCounterexample:\n\ny = 44959890247538927454655645290332771782915717053340361485195502024921998844258\nz = 44959890247538927454655645290332771782915717053340361485195502024921998844258\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (229-276): CHC: Assertion violation happens here.\nCounterexample:\n\ny = 0x6062606464666060606260646466606060626064646660606062606464666060\nz = 0x6062606464666060606260646466606060626064646660606062606464666060\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (394-437): CHC: Assertion violation happens here.\nCounterexample:\n\ny = 0x63666566676e616263666566676e616263666566676e616263666566676e6162\nz = 0x63666566676e616263666566676e616263666566676e616263666566676e6162\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_fixed_bytes.sol b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_fixed_bytes.sol index 5d53d9132..6856f72e0 100644 --- a/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_fixed_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/compound_bitwise_xor_fixed_bytes.sol @@ -13,4 +13,4 @@ contract C { // SMTEngine: all // ---- // Warning 6321: (51-57): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable. -// Warning 6328: (178-195): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0\na = 255\nb = 240\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (178-195): CHC: Assertion violation happens here.\nCounterexample:\n\n = 0x0\na = 0xff\nb = 0xf0\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol index 8a27f6f63..4cb34c49e 100644 --- a/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol +++ b/test/libsolidity/smtCheckerTests/operators/index_access_for_bytes.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (152-173): CHC: Assertion violation happens here.\nCounterexample:\n\nx = [0, 17, 34, 51]\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (152-173): CHC: Assertion violation happens here.\nCounterexample:\n\nx = [0x0, 0x11, 0x22, 0x33]\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/out_of_bounds/fixed_bytes_2.sol b/test/libsolidity/smtCheckerTests/out_of_bounds/fixed_bytes_2.sol index ab1275ab2..f4e90ddb7 100644 --- a/test/libsolidity/smtCheckerTests/out_of_bounds/fixed_bytes_2.sol +++ b/test/libsolidity/smtCheckerTests/out_of_bounds/fixed_bytes_2.sol @@ -6,4 +6,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6368: (83-87): CHC: Out of bounds access happens here.\nCounterexample:\n\nx = 0\ny = 4\n = 0\n\nTransaction trace:\nC.constructor()\nC.r(0, 4) +// Warning 6368: (83-87): CHC: Out of bounds access happens here.\nCounterexample:\n\nx = 0x0\ny = 4\n = 0x0\n\nTransaction trace:\nC.constructor()\nC.r(0x0, 4) diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol index a2d864b52..b9613a58c 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_memory_v2.sol @@ -9,6 +9,3 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 8364: (183-189): Assertion checker does not yet implement type type(uint256[] memory) -// Warning 8364: (183-192): Assertion checker does not yet implement type type(uint256[] memory[2] memory) -// Warning 8364: (173-174): Assertion checker does not yet implement type type(struct C.S storage pointer) diff --git a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol index 641b59440..618f76276 100644 --- a/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol +++ b/test/libsolidity/smtCheckerTests/special/abi_decode_simple.sol @@ -13,6 +13,4 @@ contract C { // Warning 2072: (82-86): Unused local variable. // Warning 2072: (140-150): Unused local variable. // Warning 2072: (152-156): Unused local variable. -// Warning 8364: (123-124): Assertion checker does not yet implement type type(contract C) -// Warning 8364: (193-194): Assertion checker does not yet implement type type(contract C) -// Warning 6328: (220-236): CHC: Assertion violation happens here.\nCounterexample:\n\na1 = 2437\nb1 = 10\nc1 = 9\na2 = 2437\nb2 = 10\nc2 = 9\n\nTransaction trace:\nC.constructor()\nC.f(data) +// Warning 6328: (220-236): CHC: Assertion violation happens here.\nCounterexample:\n\na1 = 2437\nb1 = 0x0a\nc1 = 9\na2 = 2437\nb2 = 0x0a\nc2 = 9\n\nTransaction trace:\nC.constructor()\nC.f(data) diff --git a/test/libsolidity/smtCheckerTests/special/block_vars_bmc_internal.sol b/test/libsolidity/smtCheckerTests/special/block_vars_bmc_internal.sol new file mode 100644 index 000000000..51a330d8e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/block_vars_bmc_internal.sol @@ -0,0 +1,37 @@ +contract C { + address coin; + uint dif; + uint gas; + uint number; + uint timestamp; + function f() public { + coin = block.coinbase; + dif = block.difficulty; + gas = block.gaslimit; + number = block.number; + timestamp = block.timestamp; + + g(); + } + function g() internal view { + assert(uint160(coin) >= 0); // should hold + assert(dif >= 0); // should hold + assert(gas >= 0); // should hold + assert(number >= 0); // should hold + assert(timestamp >= 0); // should hold + + assert(coin == block.coinbase); // should fail with BMC + assert(dif == block.difficulty); // should fail with BMC + assert(gas == block.gaslimit); // should fail with BMC + assert(number == block.number); // should fail with BMC + assert(timestamp == block.timestamp); // should fail with BMC + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (473-503): BMC: Assertion violation happens here. +// Warning 4661: (531-562): BMC: Assertion violation happens here. +// Warning 4661: (590-619): BMC: Assertion violation happens here. +// Warning 4661: (647-677): BMC: Assertion violation happens here. +// Warning 4661: (705-741): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/block_vars_chc_internal.sol b/test/libsolidity/smtCheckerTests/special/block_vars_chc_internal.sol new file mode 100644 index 000000000..2ebf4d326 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/block_vars_chc_internal.sol @@ -0,0 +1,35 @@ +contract C { + address coin; + uint dif; + uint gas; + uint number; + uint timestamp; + function f() public { + coin = block.coinbase; + dif = block.difficulty; + gas = block.gaslimit; + number = block.number; + timestamp = block.timestamp; + + g(); + } + function g() internal view { + assert(uint160(coin) >= 0); // should hold + assert(dif >= 0); // should hold + assert(gas >= 0); // should hold + assert(number >= 0); // should hold + assert(timestamp >= 0); // should hold + + assert(coin == block.coinbase); // should hold with CHC + assert(dif == block.difficulty); // should hold with CHC + assert(gas == block.gaslimit); // should hold with CHC + assert(number == block.number); // should hold with CHC + assert(timestamp == block.timestamp); // should hold with CHC + + assert(coin == address(this)); // should fail + } +} +// ==== +// SMTEngine: chc +// ---- +// Warning 6328: (770-799): CHC: Assertion violation happens here.\nCounterexample:\ncoin = 0x0, dif = 0, gas = 0, number = 0, timestamp = 0\n\nTransaction trace:\nC.constructor()\nState: coin = 0x0, dif = 0, gas = 0, number = 0, timestamp = 0\nC.f(){ block.coinbase: 0x0, block.difficulty: 0, block.gaslimit: 0, block.number: 0, block.timestamp: 0 }\n C.g() -- internal call diff --git a/test/libsolidity/smtCheckerTests/special/many_internal.sol b/test/libsolidity/smtCheckerTests/special/many_internal.sol new file mode 100644 index 000000000..eb68fd9ac --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/many_internal.sol @@ -0,0 +1,30 @@ +contract C +{ + function f() public payable { + g(); + } + function g() internal { + assert(msg.sender == block.coinbase); + assert(block.difficulty == block.gaslimit); + assert(block.number == block.timestamp); + assert(tx.gasprice == msg.value); + assert(tx.origin == msg.sender); + uint x = block.number; + unchecked { x += 2; } + assert(x > block.number); + assert(block.timestamp > 10); + assert(gasleft() > 100); + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (81-117): CHC: Assertion violation happens here. +// Warning 6328: (121-163): CHC: Assertion violation happens here. +// Warning 6328: (167-206): CHC: Assertion violation happens here. +// Warning 6328: (210-242): CHC: Assertion violation happens here. +// Warning 6328: (246-277): CHC: Assertion violation happens here. +// Warning 6328: (330-354): CHC: Assertion violation happens here. +// Warning 6328: (358-386): CHC: Assertion violation happens here. +// Warning 6328: (390-413): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/msg_parens_1.sol b/test/libsolidity/smtCheckerTests/special/msg_parens_1.sol new file mode 100644 index 000000000..6eb19e5e8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_parens_1.sol @@ -0,0 +1,13 @@ +contract C { + function f() public payable { + assert((msg).value == 10); + assert((true ? msg : msg).value == 12); + } +} +// ==== +// SMTEngine: all +// SMTIgnoreOS: macos +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (46-71): CHC: Assertion violation happens here. +// Warning 6328: (75-113): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/msg_sig.sol b/test/libsolidity/smtCheckerTests/special/msg_sig.sol index a87dc18b3..cf701f6d0 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_sig.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_sig.sol @@ -25,6 +25,6 @@ contract C // ==== // SMTEngine: all // ---- -// Warning 6328: (43-72): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (370-399): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f()\n C.fi() -- internal call\n C.gi() -- internal call -// Warning 6328: (510-539): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.h() +// Warning 6328: (43-72): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ msg.sig: 0x26121ff0 } +// Warning 6328: (370-399): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ msg.sig: 0x26121ff0 }\n C.fi() -- internal call\n C.gi() -- internal call +// Warning 6328: (510-539): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.h(){ msg.sig: 0xb8c9d365 } diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_1.sol b/test/libsolidity/smtCheckerTests/special/msg_value_1.sol index 9888d326d..51e696f56 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_1.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_1.sol @@ -6,5 +6,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4984: (55-68): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ value: 115792089237316195423570985008687907853269984665640564039457584007913129639931 } -// Warning 4984: (55-80): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ value: 57896044618658097711785492504343953926634992332820282019728792003956564819966 } +// Warning 4984: (55-68): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ msg.value: 115792089237316195423570985008687907853269984665640564039457584007913129639931 } +// Warning 4984: (55-80): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ msg.value: 57896044618658097711785492504343953926634992332820282019728792003956564819966 } diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_2.sol b/test/libsolidity/smtCheckerTests/special/msg_value_2.sol index 1c37ce029..acf1b9e5d 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_2.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_2.sol @@ -6,4 +6,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (46-67): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (46-67): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(){ msg.value: 0 } diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_4.sol b/test/libsolidity/smtCheckerTests/special/msg_value_4.sol index f9bfb0173..82edb4a39 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_4.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_4.sol @@ -13,4 +13,4 @@ contract B { // ==== // SMTEngine: all // ---- -// Warning 6328: (154-176): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nB.constructor(){ value: 39 } +// Warning 6328: (154-176): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nB.constructor(){ msg.value: 39 } diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_1.sol b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_1.sol index 00e183832..8b69352c2 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_1.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_1.sol @@ -14,4 +14,4 @@ contract C is A { // ==== // SMTEngine: all // ---- -// Warning 6328: (68-82): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nA.constructor(){ value: 1 } +// Warning 6328: (68-82): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nA.constructor(){ msg.value: 1 } diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol index efc59fcfc..10b8ef0c2 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_2.sol @@ -14,5 +14,5 @@ contract C is A { // ==== // SMTEngine: all // ---- -// Warning 6328: (60-74): CHC: Assertion violation happens here.\nCounterexample:\nv = 0, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } -// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\nv = 1, x = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } +// Warning 6328: (60-74): CHC: Assertion violation happens here.\nCounterexample:\nv = 0, x = 1\n\nTransaction trace:\nC.constructor(){ msg.value: 1 } +// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\nv = 1, x = 1\n\nTransaction trace:\nC.constructor(){ msg.value: 1 } diff --git a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_3.sol b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_3.sol index 08457c0ec..4e1d2a99f 100644 --- a/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_3.sol +++ b/test/libsolidity/smtCheckerTests/special/msg_value_inheritance_3.sol @@ -19,4 +19,4 @@ contract C is A, B { // ==== // SMTEngine: all // ---- -// Warning 6328: (60-74): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(){ value: 1 } +// Warning 6328: (60-74): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nC.constructor(){ msg.value: 1 } diff --git a/test/libsolidity/smtCheckerTests/special/msg_vars_bmc_internal.sol b/test/libsolidity/smtCheckerTests/special/msg_vars_bmc_internal.sol new file mode 100644 index 000000000..100284dd7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_vars_bmc_internal.sol @@ -0,0 +1,32 @@ +contract C { + bytes data; + address sender; + bytes4 sig; + uint value; + function f() public payable { + data = msg.data; + sender = msg.sender; + sig = msg.sig; + value = msg.value; + + g(); + } + function g() internal view { + assert(data.length >= 0); // should hold + assert(uint160(sender) >= 0); // should hold + assert(uint32(sig) >= 0); // should hold + assert(value >= 0); // should hold + + assert(data.length == msg.data.length); // should fail with BMC + assert(sender == msg.sender); // should fail with BMC + assert(sig == msg.sig); // should fail with BMC + assert(value == msg.value); // should fail with BMC + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (394-432): BMC: Assertion violation happens here. +// Warning 4661: (460-488): BMC: Assertion violation happens here. +// Warning 4661: (516-538): BMC: Assertion violation happens here. +// Warning 4661: (566-592): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/msg_vars_chc_internal.sol b/test/libsolidity/smtCheckerTests/special/msg_vars_chc_internal.sol new file mode 100644 index 000000000..b7984d21e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_vars_chc_internal.sol @@ -0,0 +1,31 @@ +contract C { + bytes data; + address sender; + bytes4 sig; + uint value; + function f() public payable { + data = msg.data; + sender = msg.sender; + sig = msg.sig; + value = msg.value; + + g(); + } + function g() internal view { + assert(data.length >= 0); // should hold + assert(uint160(sender) >= 0); // should hold + assert(uint32(sig) >= 0); // should hold + assert(value >= 0); // should hold + + assert(data.length == msg.data.length); // should hold with CHC + assert(sender == msg.sender); // should hold with CHC + assert(sig == msg.sig); // should hold with CHC + assert(value == msg.value); // should hold with CHC + + assert(msg.value == 10); // should fail + } +} +// ==== +// SMTEngine: chc +// ---- +// Warning 6328: (621-644): CHC: Assertion violation happens here.\nCounterexample:\ndata = [0x26, 0x12, 0x1f, 0xf0], sender = 0x0, sig = 0x26121ff0, value = 0\n\nTransaction trace:\nC.constructor()\nState: data = [], sender = 0x0, sig = 0x0, value = 0\nC.f(){ msg.data: [0x26, 0x12, 0x1f, 0xf0], msg.sender: 0x0, msg.sig: 0x26121ff0, msg.value: 0 }\n C.g() -- internal call diff --git a/test/libsolidity/smtCheckerTests/special/shadowing_1.sol b/test/libsolidity/smtCheckerTests/special/shadowing_1.sol new file mode 100644 index 000000000..d54548314 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/shadowing_1.sol @@ -0,0 +1,24 @@ +contract C { + struct S { + uint value; + address origin; + uint number; + } + function f() public payable { + S memory msg = S(42, address(0), 666); + S memory tx = S(42, address(0), 666); + S memory block = S(42, address(0), 666); + assert(msg.value == 42); // should hold + assert(msg.value == 41); // should fail + assert(tx.origin == address(0)); // should hold + assert(block.number == 666); // should hold + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 2319: (108-120): This declaration shadows a builtin symbol. +// Warning 2319: (149-160): This declaration shadows a builtin symbol. +// Warning 2319: (189-203): This declaration shadows a builtin symbol. +// Warning 6328: (274-297): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/timestamp_2.sol b/test/libsolidity/smtCheckerTests/special/timestamp_2.sol index 73af1d4da..7173185da 100644 --- a/test/libsolidity/smtCheckerTests/special/timestamp_2.sol +++ b/test/libsolidity/smtCheckerTests/special/timestamp_2.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 4984: (107-126): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\nTransaction trace:\nC.constructor() +// Warning 4984: (107-126): CHC: Overflow (resulting value larger than 2**256 - 1) happens here.\nCounterexample:\nx = 115792089237316195423570985008687907853269984665640564039457584007913129639935\n\nTransaction trace:\nC.constructor(){ block.timestamp: 115792089237316195423570985008687907853269984665640564039457584007913129639935 } diff --git a/test/libsolidity/smtCheckerTests/special/tx_vars_bmc_internal.sol b/test/libsolidity/smtCheckerTests/special/tx_vars_bmc_internal.sol new file mode 100644 index 000000000..137864a33 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/tx_vars_bmc_internal.sol @@ -0,0 +1,22 @@ +contract C { + uint gas; + address origin; + function f() public { + gas = tx.gasprice; + origin = tx.origin; + + g(); + } + function g() internal view { + assert(gas >= 0); // should hold + assert(uint160(origin) >= 0); // should hold + + assert(gas == tx.gasprice); // should fail with BMC + assert(origin == tx.origin); // should fail with BMC + } +} +// ==== +// SMTEngine: bmc +// ---- +// Warning 4661: (233-259): BMC: Assertion violation happens here. +// Warning 4661: (287-314): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/special/tx_vars_chc_internal.sol b/test/libsolidity/smtCheckerTests/special/tx_vars_chc_internal.sol new file mode 100644 index 000000000..ad958fe95 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/tx_vars_chc_internal.sol @@ -0,0 +1,23 @@ +contract C { + uint gas; + address origin; + function f() public { + gas = tx.gasprice; + origin = tx.origin; + + g(); + } + function g() internal view { + assert(gas >= 0); // should hold + assert(uint160(origin) >= 0); // should hold + + assert(gas == tx.gasprice); // should hold with CHC + assert(origin == tx.origin); // should hold with CHC + + assert(tx.origin == address(this)); // should fail + } +} +// ==== +// SMTEngine: chc +// ---- +// Warning 6328: (343-377): CHC: Assertion violation happens here.\nCounterexample:\ngas = 0, origin = 0x0\n\nTransaction trace:\nC.constructor()\nState: gas = 0, origin = 0x0\nC.f(){ tx.gasprice: 0, tx.origin: 0x0 }\n C.g() -- internal call diff --git a/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_1.sol b/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_1.sol new file mode 100644 index 000000000..50260aa3d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_1.sol @@ -0,0 +1,16 @@ +interface I { + function f() external; +} + +contract C { + function g(I _i) public payable { + uint x = address(this).balance; + _i.f(); + assert(x == address(this).balance); // should fail + } +} +// ==== +// SMTEngine: all +// SMTIgnoreOS: macos +// ---- +// Warning 6328: (135-169): CHC: Assertion violation happens here.\nCounterexample:\n\n_i = 0\nx = 841\n\nTransaction trace:\nC.constructor()\nC.g(0){ msg.value: 38 }\n _i.f() -- untrusted external call, synthesized as:\n C.g(0){ msg.value: 0 } -- reentrant call\n _i.f() -- untrusted external call diff --git a/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_2.sol b/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_2.sol new file mode 100644 index 000000000..aaddec5b5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/tx_vars_reentrancy_2.sol @@ -0,0 +1,16 @@ +interface I { + function f() external payable; +} + +contract C { + function g(I _i) public payable { + uint x = address(this).balance; + _i.f{ value: 100 }(); + assert(x == address(this).balance); // should fail + } +} +// ==== +// SMTEngine: all +// SMTIgnoreOS: macos +// ---- +// Warning 6328: (157-191): CHC: Assertion violation happens here.\nCounterexample:\n\n_i = 0\nx = 2537\n\nTransaction trace:\nC.constructor()\nC.g(0){ msg.value: 38 }\n _i.f{ value: 100 }() -- untrusted external call diff --git a/test/libsolidity/smtCheckerTests/typecast/address_literal.sol b/test/libsolidity/smtCheckerTests/typecast/address_literal.sol index c37c36ded..d56275fed 100644 --- a/test/libsolidity/smtCheckerTests/typecast/address_literal.sol +++ b/test/libsolidity/smtCheckerTests/typecast/address_literal.sol @@ -22,4 +22,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (454-468): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\na = 0\nb = 1\nc = 0\nd = 0\ne = 305419896\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.g() +// Warning 6328: (454-468): CHC: Assertion violation happens here.\nCounterexample:\nx = 0x0\na = 0x0\nb = 0x01\nc = 0x0\nd = 0x0\ne = 0x12345678\n\nTransaction trace:\nC.constructor()\nState: x = 0x0\nC.g() diff --git a/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol b/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol index 6f2dd33a4..91166baa8 100644 --- a/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/bytes_to_fixed_bytes_1.sol @@ -16,7 +16,7 @@ contract C { } } // ---- -// Warning 6328: (225-256): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 283686952306183\nd = 0\ne = 0\ng = 0\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (352-399): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 283686952306183\nd = 5233100606242806050944357496980485\ne = 0\ng = 0\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (526-589): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 283686952306183\nd = 5233100606242806050944357496980485\ne = 96533667595335344310996525432040024692804347064549891\ng = 0\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (732-811): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 283686952306183\nd = 5233100606242806050944357496980485\ne = 96533667595335344310996525432040024692804347064549891\ng = 1780731860627700044956966451854862080991451332659079878538166652776284161\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (225-256): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0x01020304050607\nd = 0x0\ne = 0x0\ng = 0x0\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (352-399): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0x01020304050607\nd = 0x010203040506070809000102030405\ne = 0x0\ng = 0x0\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (526-589): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0x01020304050607\nd = 0x010203040506070809000102030405\ne = 0x0102030405060708090001020304050607080900010203\ng = 0x0\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (732-811): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0x01020304050607\nd = 0x010203040506070809000102030405\ne = 0x0102030405060708090001020304050607080900010203\ng = 0x01020304050607080900010203040506070809000102030405060708090001\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol b/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol index b8a69f946..3a14cbb24 100644 --- a/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol +++ b/test/libsolidity/smtCheckerTests/typecast/cast_larger_3.sol @@ -12,4 +12,4 @@ contract C // ==== // SMTEngine: all // ---- -// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\n\na = 4660\nb = 305397760\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (240-254): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x1234\nb = 0x12340000\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/typecast/implicit_cast_string_literal_byte.sol b/test/libsolidity/smtCheckerTests/typecast/implicit_cast_string_literal_byte.sol index 6acb0d16f..093a20d7f 100644 --- a/test/libsolidity/smtCheckerTests/typecast/implicit_cast_string_literal_byte.sol +++ b/test/libsolidity/smtCheckerTests/typecast/implicit_cast_string_literal_byte.sol @@ -13,4 +13,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (153-174): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\nb = 0\n\nTransaction trace:\nC.constructor()\nC.f()\n C.g(0) -- internal call +// Warning 6328: (153-174): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 2\nb = 0x0\n\nTransaction trace:\nC.constructor()\nC.f()\n C.g(0x0) -- internal call diff --git a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_dynamic_bytes.sol b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_dynamic_bytes.sol index 04d973b99..db54c9035 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_literal_to_dynamic_bytes.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_literal_to_dynamic_bytes.sol @@ -9,4 +9,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (173-207): CHC: Assertion violation happens here.\nCounterexample:\n\nb = [255, 255]\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (173-207): CHC: Assertion violation happens here.\nCounterexample:\n\nb = [0xff, 0xff]\n\nTransaction trace:\nC.constructor()\nC.f() 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 4d1d832f9..c3fda61b8 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 @@ -9,4 +9,4 @@ contract B { // ==== // SMTEngine: all // ---- -// Warning 6328: (120-142): CHC: Assertion violation happens here.\nCounterexample:\n\na = 13564890559296822\n\nTransaction trace:\nB.constructor()\nB.f() +// Warning 6328: (120-142): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x30313233343536\n\nTransaction trace:\nB.constructor()\nB.f() 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 c869fca05..e443beb43 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 @@ -13,4 +13,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (410-429): CHC: Assertion violation happens here.\nCounterexample:\n\nv1 = 44048180597813453602326562734351324025098966208897425494240603688123167145984\nv2 = 6579558\n\nTransaction trace:\nC.constructor()\nC.a()\n C.f2() -- internal call\n C.h() -- internal call +// Warning 6328: (410-429): CHC: Assertion violation happens here.\nCounterexample:\n\nv1 = 0x6162630000000000000000000000000000000000000000000000000000000000\nv2 = 0x646566\n\nTransaction trace:\nC.constructor()\nC.a()\n C.f2() -- internal call\n C.h() -- internal call diff --git a/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol b/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol index 1b571c9f2..4910c417e 100644 --- a/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol +++ b/test/libsolidity/smtCheckerTests/typecast/string_to_bytes_push_1.sol @@ -11,4 +11,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (132-160): CHC: Assertion violation happens here.\nCounterexample:\nx = [97, 98, 99, 97]\n\nTransaction trace:\nC.constructor()\nState: x = []\nC.s() +// Warning 6328: (132-160): CHC: Assertion violation happens here.\nCounterexample:\nx = [0x61, 0x62, 0x63, 0x61]\n\nTransaction trace:\nC.constructor()\nState: x = []\nC.s() diff --git a/test/libsolidity/smtCheckerTests/types/abi_type_type_1.sol b/test/libsolidity/smtCheckerTests/types/abi_type_type_1.sol new file mode 100644 index 000000000..d4207bc86 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/abi_type_type_1.sol @@ -0,0 +1,10 @@ +contract C { + function f(bytes memory d) public pure { + (bool a, uint x) = abi.decode(d, (bool, uint)); + assert(a == (x == 2)); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (107-128): CHC: Assertion violation happens here.\nCounterexample:\n\na = true\nx = 1\n\nTransaction trace:\nC.constructor()\nC.f(d) diff --git a/test/libsolidity/smtCheckerTests/types/abi_type_type_2.sol b/test/libsolidity/smtCheckerTests/types/abi_type_type_2.sol new file mode 100644 index 000000000..e1730fedd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/abi_type_type_2.sol @@ -0,0 +1,9 @@ +contract C { + function f(bytes memory d) public pure { + assert(abi.decode(d, (bool))); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (57-86): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(d) diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer.sol b/test/libsolidity/smtCheckerTests/types/address_transfer.sol index 1715a8773..654c24969 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer.sol @@ -11,5 +11,5 @@ contract C // ==== // SMTEngine: all // ---- -// Warning 6328: (162-186): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\nx = 100\n\nTransaction trace:\nC.constructor()\nC.f(0) +// Warning 6328: (162-186): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x0\nx = 100\n\nTransaction trace:\nC.constructor()\nC.f(0x0) // Warning 1236: (98-113): 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 3cfaabb23..4b55c3241 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 // ==== // SMTEngine: all // ---- -// Warning 6328: (180-204): CHC: Assertion violation happens here.\nCounterexample:\n\na = 8855\nb = 8855\n\nTransaction trace:\nC.constructor()\nC.f(8855, 8855) +// Warning 6328: (180-204): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x2297\nb = 0x2297\n\nTransaction trace:\nC.constructor()\nC.f(0x2297, 0x2297) // Warning 1236: (101-116): BMC: Insufficient funds happens here. // Warning 1236: (120-136): BMC: Insufficient funds happens here. diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_2.sol index cb8caa3c7..187689b88 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 // ==== // SMTEngine: all // ---- -// Warning 6328: (83-97): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f(0)\n C.g() -- internal call +// Warning 6328: (83-97): CHC: Assertion violation happens here.\nCounterexample:\nx = 0x0\ny = 0x0\n\nTransaction trace:\nC.constructor()\nState: x = 0x0\nC.f(0x0)\n C.g() -- internal call diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_4.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_4.sol index ed675ce60..ae3bbf9ce 100644 --- a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_4.sol +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_4.sol @@ -12,4 +12,4 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (231-252): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 450552876409790643671482431940419874915447411150352389258589821042463539455\nz = 0\no = 255\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (231-252): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0xff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff\nz = 0x0\no = 0xff\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_5.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_5.sol index d2d4956f2..cb3cd1e0f 100644 --- a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_5.sol +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_5.sol @@ -11,6 +11,6 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (87-104): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 16909060\nb = 2\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (138-155): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 16909060\nb = 2\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (168-185): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 16909060\nb = 2\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (87-104): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0x01020304\nb = 0x02\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (138-155): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0x01020304\nb = 0x02\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (168-185): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0x01020304\nb = 0x02\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_7.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_7.sol index e264cf713..b97ffbe24 100644 --- a/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_7.sol +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_access_7.sol @@ -8,5 +8,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6368: (99-103): CHC: Out of bounds access happens here.\nCounterexample:\n\ni = 4\nx = 16909060\n\nTransaction trace:\nC.constructor()\nC.f(4) -// Warning 6328: (92-112): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 4\nx = 16909060\n\nTransaction trace:\nC.constructor()\nC.f(4) +// Warning 6368: (99-103): CHC: Out of bounds access happens here.\nCounterexample:\n\ni = 4\nx = 0x01020304\n\nTransaction trace:\nC.constructor()\nC.f(4) +// Warning 6328: (92-112): CHC: Assertion violation happens here.\nCounterexample:\n\ni = 4\nx = 0x01020304\n\nTransaction trace:\nC.constructor()\nC.f(4) diff --git a/test/libsolidity/smtCheckerTests/types/mapping_5.sol b/test/libsolidity/smtCheckerTests/types/mapping_5.sol index 04bec4948..632191a67 100644 --- a/test/libsolidity/smtCheckerTests/types/mapping_5.sol +++ b/test/libsolidity/smtCheckerTests/types/mapping_5.sol @@ -8,4 +8,4 @@ contract C // ==== // SMTEngine: all // ---- -// Warning 6328: (92-111): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\nx = 0\n\nTransaction trace:\nC.constructor()\nC.f(0, 0) +// Warning 6328: (92-111): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x0\nx = 0\n\nTransaction trace:\nC.constructor()\nC.f(0x0, 0) diff --git a/test/libsolidity/smtCheckerTests/types/static_array_length_1.sol b/test/libsolidity/smtCheckerTests/types/static_array_length_1.sol index dd7b8abd5..e8b6f1a32 100644 --- a/test/libsolidity/smtCheckerTests/types/static_array_length_1.sol +++ b/test/libsolidity/smtCheckerTests/types/static_array_length_1.sol @@ -8,5 +8,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (102-122): CHC: Assertion violation happens here.\nCounterexample:\n\na = [9, 9]\n\nTransaction trace:\nC.constructor()\nC.f([9, 9]) -// Warning 6328: (141-161): CHC: Assertion violation happens here.\nCounterexample:\n\na = [9, 9]\n\nTransaction trace:\nC.constructor()\nC.f([9, 9]) +// Warning 6328: (102-122): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0x09, 0x09]\n\nTransaction trace:\nC.constructor()\nC.f([0x09, 0x09]) +// Warning 6328: (141-161): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0x09, 0x09]\n\nTransaction trace:\nC.constructor()\nC.f([0x09, 0x09]) diff --git a/test/libsolidity/smtCheckerTests/types/static_array_length_2.sol b/test/libsolidity/smtCheckerTests/types/static_array_length_2.sol index 11ba621d2..f1128583b 100644 --- a/test/libsolidity/smtCheckerTests/types/static_array_length_2.sol +++ b/test/libsolidity/smtCheckerTests/types/static_array_length_2.sol @@ -8,5 +8,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (113-133): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0, 0]\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (152-172): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0, 0]\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (113-133): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0x0, 0x0]\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (152-172): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0x0, 0x0]\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/types/static_array_length_3.sol b/test/libsolidity/smtCheckerTests/types/static_array_length_3.sol index b5f47414c..691c25843 100644 --- a/test/libsolidity/smtCheckerTests/types/static_array_length_3.sol +++ b/test/libsolidity/smtCheckerTests/types/static_array_length_3.sol @@ -9,5 +9,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (106-126): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0, 0]\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (145-165): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0, 0]\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (106-126): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0x0, 0x0]\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (145-165): CHC: Assertion violation happens here.\nCounterexample:\n\na = [0x0, 0x0]\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol index acdaf3ae0..e1523a380 100644 --- a/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_1.sol @@ -20,4 +20,4 @@ contract C // ==== // SMTEngine: all // ---- -// Warning 6328: (330-389): CHC: Assertion violation happens here.\nCounterexample:\na = 512, b = false, c = 0\nx = 1\n\nTransaction trace:\nC.constructor()\nState: a = 0, b = false, c = 0\nC.f(1) +// Warning 6328: (330-389): CHC: Assertion violation happens here.\nCounterexample:\na = 0x0200, b = false, c = 0\nx = 1\n\nTransaction trace:\nC.constructor()\nState: a = 0x0, b = false, c = 0\nC.f(1) diff --git a/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol b/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol index 77ef26488..6bda2d66b 100644 --- a/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol +++ b/test/libsolidity/smtCheckerTests/types/storage_value_vars_2.sol @@ -10,4 +10,4 @@ contract C // ==== // SMTEngine: all // ---- -// Warning 6328: (91-104): CHC: Assertion violation happens here.\nCounterexample:\na = 0, b = false, c = 0\n\nTransaction trace:\nC.constructor()\nState: a = 0, b = false, c = 0\nC.f() +// Warning 6328: (91-104): CHC: Assertion violation happens here.\nCounterexample:\na = 0x0, b = false, c = 0\n\nTransaction trace:\nC.constructor()\nState: a = 0x0, b = false, c = 0\nC.f() diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_1.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_1.sol index 8015d2a1e..c568b2267 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (142-157): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\ny = 52647538817385212172903286807934654968315727694643370704309751478220717293568\nz = 154717211199090701642289212291190620160\n\nTransaction trace:\nC.constructor()\nC.f(52647538817385212172903286807934654968315727694643370704309751478220717293568) +// Warning 6328: (142-157): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0x7465737400000000000000000000000000000000000000000000000000000000\ny = 0x7465737400000000000000000000000000000000000000000000000000000000\nz = 0x746573747a0000000000000000000000\n\nTransaction trace:\nC.constructor()\nC.f(0x7465737400000000000000000000000000000000000000000000000000000000) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_2.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_2.sol index c534ce93b..fc68b7958 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (143-158): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\ny = 52647538817385212172903286807934654968315727694643370704309751478220717293568\nz = 154717211199090701642289212291190620160\n\nTransaction trace:\nC.constructor()\nC.f(52647538817385212172903286807934654968315727694643370704309751478220717293568) +// Warning 6328: (143-158): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0x7465737400000000000000000000000000000000000000000000000000000000\ny = 0x7465737400000000000000000000000000000000000000000000000000000000\nz = 0x746573747a0000000000000000000000\n\nTransaction trace:\nC.constructor()\nC.f(0x7465737400000000000000000000000000000000000000000000000000000000) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_3.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_3.sol index b6fe29658..7219e15bd 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (153-168): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\ny = 52647538817385212172903286807934654968315727694643370704309751478220717293568\nz = 154717211199090701642289212291190620160\n\nTransaction trace:\nC.constructor()\nC.f(52647538817385212172903286807934654968315727694643370704309751478220717293568) +// Warning 6328: (153-168): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0x7465737400000000000000000000000000000000000000000000000000000000\ny = 0x7465737400000000000000000000000000000000000000000000000000000000\nz = 0x746573747a0000000000000000000000\n\nTransaction trace:\nC.constructor()\nC.f(0x7465737400000000000000000000000000000000000000000000000000000000) diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_4.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_4.sol index 8514d785d..954aee7f4 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (228-243): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\ny = 52647538817385212172903286807934654968315727694643370704309751478220717293568\nz = 154717211199090701642289212291190620160\n\nTransaction trace:\nC.constructor()\nC.f(52647538817385212172903286807934654968315727694643370704309751478220717293568)\n C.g() -- internal call +// Warning 6328: (228-243): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0x7465737400000000000000000000000000000000000000000000000000000000\ny = 0x7465737400000000000000000000000000000000000000000000000000000000\nz = 0x746573747a0000000000000000000000\n\nTransaction trace:\nC.constructor()\nC.f(0x7465737400000000000000000000000000000000000000000000000000000000)\n C.g() -- internal call diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_assignment_5.sol b/test/libsolidity/smtCheckerTests/types/string_literal_assignment_5.sol index 9586b7b3e..71e331122 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (218-233): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\ny = 52647538817385212172903286807934654968315727694643370704309751478220717293568\nz = 154717211199090701642289212291190620160\n\nTransaction trace:\nC.constructor()\nC.f(52647538817385212172903286807934654968315727694643370704309751478220717293568)\n C.g() -- internal call +// Warning 6328: (218-233): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0x7465737400000000000000000000000000000000000000000000000000000000\ny = 0x7465737400000000000000000000000000000000000000000000000000000000\nz = 0x746573747a0000000000000000000000\n\nTransaction trace:\nC.constructor()\nC.f(0x7465737400000000000000000000000000000000000000000000000000000000)\n C.g() -- internal call diff --git a/test/libsolidity/smtCheckerTests/types/string_literal_comparison_1.sol b/test/libsolidity/smtCheckerTests/types/string_literal_comparison_1.sol index 72c4b500d..3c5742e65 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 { // ==== // SMTEngine: all // ---- -// Warning 6328: (137-157): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 52647538817385212172903286807934654968315727694643370704309751478220717293568\ny = 52647538817385212172903286807934654968315727694643370704309751478220717293568\nz = 52647538817385212172903286807934654968315727694643370704309751478220717293568\n\nTransaction trace:\nC.constructor()\nC.f(52647538817385212172903286807934654968315727694643370704309751478220717293568) +// Warning 6328: (137-157): CHC: Assertion violation happens here.\nCounterexample:\n\n_x = 0x7465737400000000000000000000000000000000000000000000000000000000\ny = 0x7465737400000000000000000000000000000000000000000000000000000000\nz = 0x7465737400000000000000000000000000000000000000000000000000000000\n\nTransaction trace:\nC.constructor()\nC.f(0x7465737400000000000000000000000000000000000000000000000000000000) 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 3087e94aa..0d525f50a 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_2d.sol @@ -9,4 +9,3 @@ function f() public pure { (int[][]); } // Warning 6133: (41-50): Statement has no effect. // Warning 8364: (42-47): Assertion checker does not yet implement type type(int256[] memory) // Warning 8364: (42-49): Assertion checker does not yet implement type type(int256[] memory[] memory) -// Warning 8364: (41-50): 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 58aab6642..37540b982 100644 --- a/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol +++ b/test/libsolidity/smtCheckerTests/types/type_expression_tuple_array_3d.sol @@ -10,4 +10,3 @@ function f() public pure { (int[][][]); } // Warning 8364: (42-47): Assertion checker does not yet implement type type(int256[] memory) // Warning 8364: (42-49): Assertion checker does not yet implement type type(int256[] memory[] memory) // Warning 8364: (42-51): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) -// Warning 8364: (41-52): Assertion checker does not yet implement type type(int256[] memory[] memory[] memory) diff --git a/test/libsolidity/smtCheckerTests/unchecked/check_var_init.sol b/test/libsolidity/smtCheckerTests/unchecked/check_var_init.sol index 805409c01..b7d19c0bd 100644 --- a/test/libsolidity/smtCheckerTests/unchecked/check_var_init.sol +++ b/test/libsolidity/smtCheckerTests/unchecked/check_var_init.sol @@ -16,5 +16,5 @@ contract D { // ==== // SMTEngine: all // ---- -// Warning 3944: (33-47): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nC.constructor() +// Warning 3944: (33-47): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\nx = 0\n\nTransaction trace:\nC.constructor(){ msg.value: 0 } // Warning 3944: (160-174): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\n\nTransaction trace:\nD.constructor()\nD.f()\n D.h() -- internal call diff --git a/test/libsolidity/smtCheckerTests/userTypes/constant.sol b/test/libsolidity/smtCheckerTests/userTypes/constant.sol new file mode 100644 index 000000000..9730d4b1c --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/constant.sol @@ -0,0 +1,18 @@ +type T is int224; +pragma solidity >= 0.0.0; +contract C { + T constant public s = T.wrap(int224(165521356710917456517261742455526507355687727119203895813322792776)); + T constant public t = s; + int224 constant public u = T.unwrap(t); + + function f() public pure { + assert(T.unwrap(s) == 165521356710917456517261742455526507355687727119203895813322792776); + assert(T.unwrap(t) == 165521356710917456517261742455526507355687727119203895813322792776); + assert(u == 165521356710917456517261742455526507355687727119203895813322792776); + assert(T.unwrap(s) == 0); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (531-555): CHC: Assertion violation happens here.\nCounterexample:\nu = 165521356710917456517261742455526507355687727119203895813322792776\n\nTransaction trace:\nC.constructor()\nState: u = 165521356710917456517261742455526507355687727119203895813322792776\nC.f() diff --git a/test/libsolidity/smtCheckerTests/userTypes/conversion.sol b/test/libsolidity/smtCheckerTests/userTypes/conversion.sol new file mode 100644 index 000000000..cad0233a7 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/conversion.sol @@ -0,0 +1,87 @@ +pragma abicoder v2; + +type MyUInt8 is uint8; +type MyInt8 is int8; +type MyUInt16 is uint16; + +contract C { + function f(uint a) internal pure returns(MyUInt8) { + return MyUInt8.wrap(uint8(a)); + } + function g(uint a) internal pure returns(MyInt8) { + return MyInt8.wrap(int8(int(a))); + } + function h(MyUInt8 a) internal pure returns (MyInt8) { + return MyInt8.wrap(int8(MyUInt8.unwrap(a))); + } + function i(MyUInt8 a) internal pure returns(MyUInt16) { + return MyUInt16.wrap(MyUInt8.unwrap(a)); + } + function j(MyUInt8 a) internal pure returns (uint) { + return MyUInt8.unwrap(a); + } + function k(MyUInt8 a) internal pure returns (MyUInt16) { + return MyUInt16.wrap(MyUInt8.unwrap(a)); + } + function m(MyUInt16 a) internal pure returns (MyUInt8) { + return MyUInt8.wrap(uint8(MyUInt16.unwrap(a))); + } + + function p() public pure { + assert(MyUInt8.unwrap(f(1)) == 1); + assert(MyUInt8.unwrap(f(2)) == 2); + assert(MyUInt8.unwrap(f(257)) == 1); + assert(MyUInt8.unwrap(f(257)) == 257); // should fail + } + + function q() public pure { + assert(MyInt8.unwrap(g(1)) == 1); + assert(MyInt8.unwrap(g(2)) == 2); + assert(MyInt8.unwrap(g(255)) == -1); + assert(MyInt8.unwrap(g(257)) == 1); + assert(MyInt8.unwrap(g(257)) == -1); // should fail + } + + function r() public pure { + assert(MyInt8.unwrap(h(MyUInt8.wrap(1))) == 1); + assert(MyInt8.unwrap(h(MyUInt8.wrap(2))) == 2); + assert(MyInt8.unwrap(h(MyUInt8.wrap(255))) == -1); + assert(MyInt8.unwrap(h(MyUInt8.wrap(255))) == 1); // should fail + } + + function s() public pure { + assert(MyUInt16.unwrap(i(MyUInt8.wrap(250))) == 250); + assert(MyUInt16.unwrap(i(MyUInt8.wrap(250))) == 0); // should fail + } + + function t() public pure { + assert(j(MyUInt8.wrap(1)) == 1); + assert(j(MyUInt8.wrap(2)) == 2); + assert(j(MyUInt8.wrap(255)) == 0xff); + assert(j(MyUInt8.wrap(255)) == 1); // should fail + } + + function v() public pure { + assert(MyUInt16.unwrap(k(MyUInt8.wrap(1))) == 1); + assert(MyUInt16.unwrap(k(MyUInt8.wrap(2))) == 2); + assert(MyUInt16.unwrap(k(MyUInt8.wrap(255))) == 0xff); + assert(MyUInt16.unwrap(k(MyUInt8.wrap(255))) == 1); // should fail + } + + function w() public pure { + assert(MyUInt8.unwrap(m(MyUInt16.wrap(1))) == 1); + assert(MyUInt8.unwrap(m(MyUInt16.wrap(2))) == 2); + assert(MyUInt8.unwrap(m(MyUInt16.wrap(255))) == 0xff); + assert(MyUInt8.unwrap(m(MyUInt16.wrap(255))) == 1); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (937-974): CHC: Assertion violation happens here. +// Warning 6328: (1174-1209): CHC: Assertion violation happens here. +// Warning 6328: (1413-1461): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.r()\n C.h(1) -- internal call\n C.h(2) -- internal call\n C.h(255) -- internal call\n C.h(255) -- internal call +// Warning 6328: (1568-1618): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.s()\n C.i(250) -- internal call\n C.i(250) -- internal call +// Warning 6328: (1779-1812): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.t()\n C.j(1) -- internal call\n C.j(2) -- internal call\n C.j(255) -- internal call\n C.j(255) -- internal call +// Warning 6328: (2024-2074): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.v()\n C.k(1) -- internal call\n C.k(2) -- internal call\n C.k(255) -- internal call\n C.k(255) -- internal call +// Warning 6328: (2286-2336): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/userTypes/fixedpoint.sol b/test/libsolidity/smtCheckerTests/userTypes/fixedpoint.sol new file mode 100644 index 000000000..a8aa0bd9a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/fixedpoint.sol @@ -0,0 +1,74 @@ +// Represent a 18 decimal, 256 bit wide fixed point type using a user defined value type. +type UFixed256x18 is uint256; + +/// A minimal library to do fixed point operations on UFixed256x18. +library FixedMath { + uint constant multiplier = 10**18; + /// Adds two UFixed256x18 numbers. Reverts on overflow, relying on checked arithmetic on + /// uint256. + function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) + UFixed256x18.unwrap(b)); + } + /// Multiplies UFixed256x18 and uint256. Reverts on overflow, relying on checked arithmetic on + /// uint256. + function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(UFixed256x18.unwrap(a) * b); + } + /// Take the floor of a UFixed256x18 number. + /// @return the largest integer that does not exceed `a`. + function floor(UFixed256x18 a) internal pure returns (uint256) { + return UFixed256x18.unwrap(a) / multiplier; + } + /// Turns a uint256 into a UFixed256x18 of the same value. + /// Reverts if the integer is too large. + function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) { + return UFixed256x18.wrap(a * multiplier); + } +} + +contract TestFixedMath { + function add(UFixed256x18 a, UFixed256x18 b) internal pure returns (UFixed256x18) { + return FixedMath.add(a, b); + } + function mul(UFixed256x18 a, uint256 b) internal pure returns (UFixed256x18) { + return FixedMath.mul(a, b); + } + function floor(UFixed256x18 a) internal pure returns (uint256) { + return FixedMath.floor(a); + } + function toUFixed256x18(uint256 a) internal pure returns (UFixed256x18) { + return FixedMath.toUFixed256x18(a); + } + + function f() public pure { + assert(UFixed256x18.unwrap(add(UFixed256x18.wrap(0), UFixed256x18.wrap(0))) == 0); + assert(UFixed256x18.unwrap(add(UFixed256x18.wrap(25), UFixed256x18.wrap(45))) == 0x46); + assert(UFixed256x18.unwrap(add(UFixed256x18.wrap(25), UFixed256x18.wrap(45))) == 46); // should fail + } + + function g() public pure { + assert(UFixed256x18.unwrap(mul(UFixed256x18.wrap(340282366920938463463374607431768211456), 20)) == 6805647338418769269267492148635364229120); + assert(UFixed256x18.unwrap(mul(UFixed256x18.wrap(340282366920938463463374607431768211456), 20)) == 0); // should fail + } + + function h() public pure { + assert(floor(UFixed256x18.wrap(11579208923731619542357098500868790785326998665640564039457584007913129639930)) == 11579208923731619542357098500868790785326998665640564039457); + assert(floor(UFixed256x18.wrap(115792089237316195423570985008687907853269984665640564039457584007913129639935)) == 115792089237316195423570985008687907853269984665640564039457); + assert(floor(UFixed256x18.wrap(11579208923731619542357098500868790785326998665640564039457584007913129639930)) == 0); // should fail + } + + function i() public pure { + assert(UFixed256x18.unwrap(toUFixed256x18(0)) == 0); + assert(UFixed256x18.unwrap(toUFixed256x18(5)) == 5000000000000000000); + assert(UFixed256x18.unwrap(toUFixed256x18(115792089237316195423570985008687907853269984665640564039457)) == 115792089237316195423570985008687907853269984665640564039457000000000000000000); + assert(UFixed256x18.unwrap(toUFixed256x18(5)) == 5); // should fail + } + +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (1886-1970): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nTestFixedMath.constructor()\nTestFixedMath.f()\n TestFixedMath.add(0, 0) -- internal call\n FixedMath.add(0, 0) -- internal call\n TestFixedMath.add(25, 45) -- internal call\n FixedMath.add(25, 45) -- internal call\n TestFixedMath.add(25, 45) -- internal call\n FixedMath.add(25, 45) -- internal call +// Warning 6328: (2165-2266): CHC: Assertion violation happens here. +// Warning 6328: (2675-2791): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nTestFixedMath.constructor()\nTestFixedMath.h()\n TestFixedMath.floor(11579208923731619542357098500868790785326998665640564039457584007913129639930) -- internal call\n FixedMath.floor(11579208923731619542357098500868790785326998665640564039457584007913129639930) -- internal call\n TestFixedMath.floor(115792089237316195423570985008687907853269984665640564039457584007913129639935) -- internal call\n FixedMath.floor(115792089237316195423570985008687907853269984665640564039457584007913129639935) -- internal call\n TestFixedMath.floor(11579208923731619542357098500868790785326998665640564039457584007913129639930) -- internal call\n FixedMath.floor(11579208923731619542357098500868790785326998665640564039457584007913129639930) -- internal call +// Warning 6328: (3161-3212): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nTestFixedMath.constructor()\nTestFixedMath.i()\n TestFixedMath.toUFixed256x18(0) -- internal call\n FixedMath.toUFixed256x18(0) -- internal call\n TestFixedMath.toUFixed256x18(5) -- internal call\n FixedMath.toUFixed256x18(5) -- internal call\n TestFixedMath.toUFixed256x18(115792089237316195423570985008687907853269984665640564039457) -- internal call\n FixedMath.toUFixed256x18(115792089237316195423570985008687907853269984665640564039457) -- internal call\n TestFixedMath.toUFixed256x18(5) -- internal call\n FixedMath.toUFixed256x18(5) -- internal call diff --git a/test/libsolidity/smtCheckerTests/userTypes/in_parenthesis.sol b/test/libsolidity/smtCheckerTests/userTypes/in_parenthesis.sol new file mode 100644 index 000000000..478b87dfe --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/in_parenthesis.sol @@ -0,0 +1,19 @@ +type MyInt is int; +contract C { + function f() public pure returns (MyInt a) { + a = MyInt.wrap(5); + assert(MyInt.unwrap(a) == 5); + assert(MyInt.unwrap(a) == 6); // should fail + } + + function g() public pure { + MyInt x = f(); + assert(MyInt.unwrap(x) == 5); + assert(MyInt.unwrap(x) == 6); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (133-161): CHC: Assertion violation happens here.\nCounterexample:\n\na = 5\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (261-289): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.g()\n C.f() -- internal call diff --git a/test/libsolidity/smtCheckerTests/userTypes/in_parenthesis_2.sol b/test/libsolidity/smtCheckerTests/userTypes/in_parenthesis_2.sol new file mode 100644 index 000000000..e8da1dd56 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/in_parenthesis_2.sol @@ -0,0 +1,26 @@ +type MyInt is int; +contract C { + function f() public pure returns (MyInt a, int b) { + (MyInt).wrap; + a = (MyInt).wrap(5); + (MyInt).unwrap; + b = (MyInt).unwrap((MyInt).wrap(10)); + } + + function g() public pure { + (MyInt x, int y) = f(); + assert(MyInt.unwrap(x) == 5); + assert(MyInt.unwrap(x) == 6); // should fail + assert(y == 10); + assert(y == 11); // should fail + } + +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6133: (87-99): Statement has no effect. +// Warning 6133: (126-140): Statement has no effect. +// Warning 6328: (274-302): CHC: Assertion violation happens here. +// Warning 6328: (340-355): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/userTypes/mapping_1.sol b/test/libsolidity/smtCheckerTests/userTypes/mapping_1.sol new file mode 100644 index 000000000..52ea82800 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/mapping_1.sol @@ -0,0 +1,10 @@ +type MyInt is int; +contract C { + mapping(MyInt => int) m; + function f(MyInt a) public view { + assert(m[a] == 0); // should hold + assert(m[a] != 0); // should fail + } +} +// ---- +// Warning 6328: (134-151): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0\n\nTransaction trace:\nC.constructor()\nC.f(0) diff --git a/test/libsolidity/smtCheckerTests/userTypes/modifier_1.sol b/test/libsolidity/smtCheckerTests/userTypes/modifier_1.sol new file mode 100644 index 000000000..3dbcc3126 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/modifier_1.sol @@ -0,0 +1,18 @@ +type T1 is uint; +type T2 is uint; + +contract C { + modifier m(T1 x, T2 y) { + require(T1.unwrap(x) == T2.unwrap(y)); + _; + } + + function f(uint x, uint y) m(T1.wrap(x), T2.wrap(y)) public pure { + assert(x == y); + assert(x != y); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (212-226): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\ny = 0\n\nTransaction trace:\nC.constructor()\nC.f(0, 0) diff --git a/test/libsolidity/smtCheckerTests/userTypes/multisource.sol b/test/libsolidity/smtCheckerTests/userTypes/multisource.sol new file mode 100644 index 000000000..23fbef98a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/multisource.sol @@ -0,0 +1,21 @@ +==== Source: A ==== +type MyInt is int; +type MyAddress is address; +==== Source: B ==== +import {MyInt, MyAddress as OurAddress} from "A"; +contract A { + function f(int x) internal pure returns(MyInt) { return MyInt.wrap(x); } + function f(address x) internal pure returns(OurAddress) { return OurAddress.wrap(x); } + + function g() public pure { + assert(MyInt.unwrap(f(int(5))) == 5); + assert(MyInt.unwrap(f(int(5))) == 0); // should fail + assert(OurAddress.unwrap(f(address(5))) == address(5)); + assert(OurAddress.unwrap(f(address(5))) == address(0)); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (B:296-332): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nA.constructor()\nA.g()\n A.f(5) -- internal call\n A.f(5) -- internal call +// Warning 6328: (B:409-463): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nA.constructor()\nA.g()\n A.f(5) -- internal call\n A.f(5) -- internal call\n A.f(0x05) -- internal call\n A.f(0x05) -- internal call diff --git a/test/libsolidity/smtCheckerTests/userTypes/multisource_module.sol b/test/libsolidity/smtCheckerTests/userTypes/multisource_module.sol new file mode 100644 index 000000000..ee3277d1b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/multisource_module.sol @@ -0,0 +1,20 @@ +==== Source: s1.sol ==== +type MyInt is int; +==== Source: s2.sol ==== +import "s1.sol" as M; +contract C { + function f(int x) public pure returns (M.MyInt) { return M.MyInt.wrap(x); } + function g(M.MyInt x) public pure returns (int) { return M.MyInt.unwrap(x); } + + function h() public pure { + assert(M.MyInt.unwrap(f(5)) == 5); + assert(M.MyInt.unwrap(f(5)) == 6); // should fail + assert(g(M.MyInt.wrap(1)) == 1); + assert(g(M.MyInt.wrap(1)) == 0); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (s2.sol:259-292): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.h()\n C.f(5) -- internal call\n C.f(5) -- internal call +// Warning 6328: (s2.sol:346-377): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.h()\n C.f(5) -- internal call\n C.f(5) -- internal call\n C.g(1) -- internal call\n C.g(1) -- internal call diff --git a/test/libsolidity/smtCheckerTests/userTypes/simple.sol b/test/libsolidity/smtCheckerTests/userTypes/simple.sol new file mode 100644 index 000000000..edfde6594 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/simple.sol @@ -0,0 +1,23 @@ +type MyInt is int; +contract C { + function f() internal pure returns (MyInt a) { + } + function g() internal pure returns (MyInt b, MyInt c) { + b = MyInt.wrap(int(1)); + c = MyInt.wrap(1); + } + + function h() public pure { + assert(MyInt.unwrap(f()) == 0); + assert(MyInt.unwrap(f()) == 1); // should fail + (MyInt x, MyInt y) = g(); + assert(MyInt.unwrap(x) == 1); + assert(MyInt.unwrap(x) == 0); // should fail + assert(MyInt.unwrap(y) == 1); + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (255-285): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.h()\n C.f() -- internal call\n C.f() -- internal call +// Warning 6328: (364-392): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.h()\n C.f() -- internal call\n C.f() -- internal call\n C.g() -- internal call diff --git a/test/libsolidity/smtCheckerTests/userTypes/user_abi_1.sol b/test/libsolidity/smtCheckerTests/userTypes/user_abi_1.sol new file mode 100644 index 000000000..1d31cbe3d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/user_abi_1.sol @@ -0,0 +1,14 @@ +type T is uint; + +contract C { + function f(bytes memory data) public pure { + T x = abi.decode(data, (T)); + uint y = abi.decode(data, (uint)); + assert(T.unwrap(x) == y); // should hold + assert(T.unwrap(x) != y); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (188-212): CHC: Assertion violation happens here.\nCounterexample:\n\ny = 2437\n\nTransaction trace:\nC.constructor()\nC.f(data) diff --git a/test/libsolidity/smtCheckerTests/userTypes/user_abi_2.sol b/test/libsolidity/smtCheckerTests/userTypes/user_abi_2.sol new file mode 100644 index 000000000..a4c4a3a29 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/user_abi_2.sol @@ -0,0 +1,14 @@ +type T is uint; + +contract C { + function f(bytes memory data) public pure { + T x = abi.decode(data, (T)); + T y = abi.decode(data, (T)); + assert(T.unwrap(x) == T.unwrap(y)); // should hold + assert(T.unwrap(x) != T.unwrap(y)); // should fail + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 6328: (192-226): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(data) diff --git a/test/libsolidity/smtCheckerTests/userTypes/wrap_unwrap_via_contract_name.sol b/test/libsolidity/smtCheckerTests/userTypes/wrap_unwrap_via_contract_name.sol new file mode 100644 index 000000000..065dae17a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/userTypes/wrap_unwrap_via_contract_name.sol @@ -0,0 +1,36 @@ +contract C { + type T is uint; +} +contract D { + function f(C.T x) internal pure returns(uint) { + return C.T.unwrap(x); + } + function g(uint x) internal pure returns(C.T) { + return C.T.wrap(x); + } + function h(uint x) internal pure returns(uint) { + return f(g(x)); + } + function i(C.T x) internal pure returns(C.T) { + return g(f(x)); + } + + function m() public pure { + assert(f(C.T.wrap(0x42)) == 0x42); + assert(f(C.T.wrap(0x42)) == 0x43); // should fail + assert(C.T.unwrap(g(0x42)) == 0x42); + assert(C.T.unwrap(g(0x42)) == 0x43); // should fail + assert(h(0x42) == 0x42); + assert(h(0x42) == 0x43); // should fail + assert(C.T.unwrap(i(C.T.wrap(0x42))) == 0x42); + assert(C.T.unwrap(i(C.T.wrap(0x42))) == 0x43); // should fail + } +} +// ==== +// SMTEngine: all +// SMTIgnoreCex: yes +// ---- +// Warning 6328: (403-436): CHC: Assertion violation happens here. +// Warning 6328: (494-529): CHC: Assertion violation happens here. +// Warning 6328: (575-598): CHC: Assertion violation happens here. +// Warning 6328: (666-711): CHC: Assertion violation happens here. diff --git a/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol b/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol index e25bac278..741667e7c 100644 --- a/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol +++ b/test/libsolidity/syntaxTests/array/concat/bytes_concat_on_type_info.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// TypeError 4259: (93-98): Invalid type for argument in the function call. A contract type or an integer type is required, but type(bytes) provided. +// TypeError 4259: (93-98): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(bytes) provided. diff --git a/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_dynamic_array.sol b/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_dynamic_array.sol new file mode 100644 index 000000000..2baadc8d0 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_dynamic_array.sol @@ -0,0 +1,5 @@ +contract C { + function f() internal returns (uint256[] calldata) {} +} +// ---- +// TypeError 3464: (48-66): This variable is of calldata pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_static_array.sol b/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_static_array.sol new file mode 100644 index 000000000..0adfc86fe --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_static_array.sol @@ -0,0 +1,5 @@ +contract C { + function f() internal returns (uint256[1] calldata) {} +} +// ---- +// TypeError 3464: (48-67): This variable is of calldata pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_struct.sol b/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_struct.sol new file mode 100644 index 000000000..fd682b090 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/calldataReturn/calldata_return_struct.sol @@ -0,0 +1,6 @@ +contract C { + struct S { uint256 x; } + function f() internal returns (S calldata) {} +} +// ---- +// TypeError 3464: (76-86): This variable is of calldata pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/imports/alias_import_not_forwarded.sol b/test/libsolidity/syntaxTests/imports/alias_import_not_forwarded.sol new file mode 100644 index 000000000..1a7bb40fe --- /dev/null +++ b/test/libsolidity/syntaxTests/imports/alias_import_not_forwarded.sol @@ -0,0 +1,8 @@ +==== Source: dummy ==== +contract Dummy { string public constant FOO = "FOO"; } +==== Source: hasAlias ==== +import {Dummy as AliasedDummy} from "dummy"; +==== Source: Main ==== +import {AliasedDummy} from "hasAlias"; +contract TestAlias is AliasedDummy {} +// ---- diff --git a/test/libsolidity/syntaxTests/inheritance/interface/overrides_multiple.sol b/test/libsolidity/syntaxTests/inheritance/interface/overrides_multiple.sol index 96f741471..9c2f39bd9 100644 --- a/test/libsolidity/syntaxTests/inheritance/interface/overrides_multiple.sol +++ b/test/libsolidity/syntaxTests/inheritance/interface/overrides_multiple.sol @@ -23,8 +23,6 @@ interface Sub is SuperA, SuperB { } // ---- -// TypeError 9456: (572-616): Overriding function is missing "override" specifier. -// TypeError 9456: (572-616): Overriding function is missing "override" specifier. // TypeError 4327: (572-616): Function needs to specify overridden contracts "SuperA" and "SuperB". // TypeError 4327: (647-655): Function needs to specify overridden contracts "SuperA" and "SuperB". // TypeError 4327: (705-721): Function needs to specify overridden contract "SuperB". diff --git a/test/libsolidity/syntaxTests/inheritance/interface/overrides_single.sol b/test/libsolidity/syntaxTests/inheritance/interface/overrides_single.sol index 106fb8975..bed7026dd 100644 --- a/test/libsolidity/syntaxTests/inheritance/interface/overrides_single.sol +++ b/test/libsolidity/syntaxTests/inheritance/interface/overrides_single.sol @@ -11,4 +11,3 @@ interface Sub is Super { } // ---- -// TypeError 9456: (197-241): Overriding function is missing "override" specifier. diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface.sol index 33b66a86f..7be022c02 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface.sol @@ -6,9 +6,9 @@ interface I { } contract C is I { uint dummy; - function f(uint[] memory) public override pure {} - function g(uint[] memory) public override view { dummy; } - function h(uint[] memory) public override { dummy = 42; } - function i(uint[] memory) public override payable {} + function f(uint[] memory) public pure {} + function g(uint[] memory) public view { dummy; } + function h(uint[] memory) public { dummy = 42; } + function i(uint[] memory) public payable {} } // ---- diff --git a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_instantiate.sol b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_instantiate.sol index 1a1ec2971..2c1045802 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_instantiate.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_instantiate.sol @@ -2,7 +2,7 @@ interface I { function f(uint[] calldata) external pure; } contract A is I { - function f(uint[] memory) public override pure {} + function f(uint[] memory) public pure {} } contract C { function f() public { 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 e77836724..1ba73bb97 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/calldata_memory_interface_struct.sol @@ -9,8 +9,8 @@ interface I { contract C is I { uint dummy; function f(S memory) public override pure {} - function g(S memory) public override view { dummy; } + function g(S memory) public view { dummy; } function h(S memory) public override { dummy = 42; } - function i(S memory) public override payable {} + function i(S memory) public payable {} } // ---- diff --git a/test/libsolidity/syntaxTests/inheritance/override/change_return_types_in_interface.sol b/test/libsolidity/syntaxTests/inheritance/override/change_return_types_in_interface.sol index 8a477b70d..2ac2734c5 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/change_return_types_in_interface.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/change_return_types_in_interface.sol @@ -7,5 +7,4 @@ contract B is I { function f() public pure returns (uint, uint) {} } // ---- -// TypeError 9456: (182-230): Overriding function is missing "override" specifier. // TypeError 4822: (182-230): Overriding function return types differ. diff --git a/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_implementation.sol b/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_implementation.sol index 0164d4dd1..cc065bbee 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_implementation.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_implementation.sol @@ -3,15 +3,15 @@ interface I { function g() external; } abstract contract A is I { - function f() external override {} - function g() external override virtual; + function f() external {} + function g() external virtual; } abstract contract B is I { - function g() external override {} - function f() external override virtual; + function g() external {} + function f() external virtual; } contract C is A, B { } // ---- -// TypeError 6480: (292-314): Derived contract must override function "f". Two or more base classes define function with same name and parameter types. -// TypeError 6480: (292-314): Derived contract must override function "g". Two or more base classes define function with same name and parameter types. +// TypeError 6480: (256-278): Derived contract must override function "f". Two or more base classes define function with same name and parameter types. +// TypeError 6480: (256-278): Derived contract must override function "g". Two or more base classes define function with same name and parameter types. diff --git a/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_mention.sol b/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_mention.sol index c258bd219..7a4244941 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_mention.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/common_base_and_unique_mention.sol @@ -3,10 +3,10 @@ interface I { function g() external; } abstract contract A is I { - function f() external override {} + function f() external {} } abstract contract B is I { - function g() external override {} + function g() external {} } contract C is A, B { } diff --git a/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable.sol index 1903f08ae..7d0a0740c 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable.sol @@ -3,7 +3,7 @@ interface I { } abstract contract A is I { - uint public override f; + uint public f; } abstract contract B is I { diff --git a/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function.sol b/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function.sol index 90c747dc4..4ea1ab4c0 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function.sol @@ -3,12 +3,12 @@ interface I { } contract A is I { - uint public override f; + uint public f; } abstract contract B is I { - function f() external virtual override returns (uint); + function f() external virtual returns (uint); } abstract contract C is A, B {} // ---- -// TypeError 6480: (185-215): Derived contract must override function "f". Two or more base classes define function with same name and parameter types. Since one of the bases defines a public state variable which cannot be overridden, you have to change the inheritance layout or the names of the functions. +// TypeError 6480: (167-197): Derived contract must override function "f". Two or more base classes define function with same name and parameter types. Since one of the bases defines a public state variable which cannot be overridden, you have to change the inheritance layout or the names of the functions. diff --git a/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function_implemented.sol b/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function_implemented.sol index add03e5cb..90efce554 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function_implemented.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/diamond_interface_intermediate_public_state_variable_and_function_implemented.sol @@ -3,12 +3,12 @@ interface I { } contract A is I { - uint public override f; + uint public f; } abstract contract B is I { - function f() external virtual override returns (uint) { return 2; } + function f() external virtual returns (uint) { return 2; } } abstract contract C is A, B {} // ---- -// TypeError 6480: (198-228): Derived contract must override function "f". Two or more base classes define function with same name and parameter types. Since one of the bases defines a public state variable which cannot be overridden, you have to change the inheritance layout or the names of the functions. +// TypeError 6480: (180-210): Derived contract must override function "f". Two or more base classes define function with same name and parameter types. Since one of the bases defines a public state variable which cannot be overridden, you have to change the inheritance layout or the names of the functions. diff --git a/test/libsolidity/syntaxTests/inheritance/override/function_state_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/function_state_variable.sol index d1e5eb306..ccb567099 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/function_state_variable.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/function_state_variable.sol @@ -1,3 +1,3 @@ interface ERC20 { function x() external returns (uint); } -contract C is ERC20 { uint public override x; } +contract C is ERC20 { uint public x; } // ---- diff --git a/test/libsolidity/syntaxTests/inheritance/override/implement_interface_by_public_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/implement_interface_by_public_variable.sol index 5f493f515..d98e253c5 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/implement_interface_by_public_variable.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/implement_interface_by_public_variable.sol @@ -1,6 +1,6 @@ interface X { function test() external returns (uint256); } contract Y is X { - uint256 public override test = 42; + uint256 public test = 42; } contract T { constructor() { new Y(); } diff --git a/test/libsolidity/syntaxTests/inheritance/override/interface_exception/abstract_needed.sol b/test/libsolidity/syntaxTests/inheritance/override/interface_exception/abstract_needed.sol new file mode 100644 index 000000000..97290af9c --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/interface_exception/abstract_needed.sol @@ -0,0 +1,8 @@ +abstract contract I { + function f() external virtual; +} +contract C is I { + function f() external {} +} +// ---- +// TypeError 9456: (75-99): Overriding function is missing "override" specifier. diff --git a/test/libsolidity/syntaxTests/inheritance/override/interface_exception/diamond_needed.sol b/test/libsolidity/syntaxTests/inheritance/override/interface_exception/diamond_needed.sol new file mode 100644 index 000000000..6c46e0a8b --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/interface_exception/diamond_needed.sol @@ -0,0 +1,19 @@ +interface I { + function f() external; + function g() external; + function h() external; +} +interface J { + function f() external; + function g() external; + function h() external; +} +contract C is I, J { + function f() external {} + function g() external override {} + function h() external override(I) {} +} +// ---- +// TypeError 4327: (198-222): Function needs to specify overridden contracts "I" and "J". +// TypeError 4327: (246-254): Function needs to specify overridden contracts "I" and "J". +// TypeError 4327: (281-292): Function needs to specify overridden contract "J". diff --git a/test/libsolidity/syntaxTests/inheritance/override/interface_exception/regular_optional.sol b/test/libsolidity/syntaxTests/inheritance/override/interface_exception/regular_optional.sol new file mode 100644 index 000000000..b2f1d7c13 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/interface_exception/regular_optional.sol @@ -0,0 +1,11 @@ +interface I { + function f() external; + function g() external; + function h() external; +} +contract C is I { + function f() external {} + function g() external override {} + function h() external override(I) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/inheritance/override/override_interface.sol b/test/libsolidity/syntaxTests/inheritance/override/override_interface.sol index 8b70bdbb6..9f2635511 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/override_interface.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/override_interface.sol @@ -1,9 +1,11 @@ interface A { function test() external returns (uint256); function test2() external returns (uint256); + function test3() external returns (uint256); } contract X is A { function test() external override returns (uint256) {} function test2() external override(A) returns (uint256) {} + function test3() external returns (uint256) {} } // ---- diff --git a/test/libsolidity/syntaxTests/inheritance/override/public_var_implements_parallel_interface.sol b/test/libsolidity/syntaxTests/inheritance/override/public_var_implements_parallel_interface.sol index dc2f7b16a..eeb2574d5 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/public_var_implements_parallel_interface.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/public_var_implements_parallel_interface.sol @@ -11,11 +11,11 @@ contract X is A, B { function goo() external virtual override(A, B) returns (uint) {} } abstract contract T is A { - function foo() external virtual override returns (uint); - function goo() external virtual override returns (uint); + function foo() external virtual returns (uint); + function goo() external virtual returns (uint); } contract Y is X, T { } // ---- -// TypeError 6480: (484-506): Derived contract must override function "foo". Two or more base classes define function with same name and parameter types. Since one of the bases defines a public state variable which cannot be overridden, you have to change the inheritance layout or the names of the functions. -// TypeError 6480: (484-506): Derived contract must override function "goo". Two or more base classes define function with same name and parameter types. +// TypeError 6480: (466-488): Derived contract must override function "foo". Two or more base classes define function with same name and parameter types. Since one of the bases defines a public state variable which cannot be overridden, you have to change the inheritance layout or the names of the functions. +// TypeError 6480: (466-488): Derived contract must override function "goo". Two or more base classes define function with same name and parameter types. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/constant_length_access.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/constant_length_access.sol new file mode 100644 index 000000000..eda71f955 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/constant_length_access.sol @@ -0,0 +1,10 @@ +contract Test { + uint constant x = 2; + function f() public pure { + assembly { + let y := x.length + } + } +} +// ---- +// TypeError 3622: (91-99): The suffix ".length" is not supported by this variable or type. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/external_function_pointer_offset.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/external_function_pointer_offset.sol new file mode 100644 index 000000000..f6abe2976 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/external_function_pointer_offset.sol @@ -0,0 +1,15 @@ +contract C { + function testFunction() external {} + + function testYul() public { + function() external fp = this.testFunction; + + uint myOffset; + + assembly { + myOffset := fp.offset + } + } +} +// ---- +// TypeError 9272: (173-182): Variables of type function pointer only support ".selector" and ".address". diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/internal_function_pointer_address.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/internal_function_pointer_address.sol new file mode 100644 index 000000000..c252657ca --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/internal_function_pointer_address.sol @@ -0,0 +1,18 @@ +contract C { + function testFunction() internal {} + + function testYul() public returns (address adr) { + function() internal fp = testFunction; + uint selectorValue = 0; + + assembly { + adr := fp.address + } + } + function testSol() public returns (address) { + return testFunction.address; + } +} +// ---- +// TypeError 8533: (193-203): Only Variables of type external function pointer support ".selector" and ".address". +// TypeError 9582: (267-287): Member "address" not found or not visible after argument-dependent lookup in function (). diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/internal_function_pointer_selector.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/internal_function_pointer_selector.sol new file mode 100644 index 000000000..4913bf5d6 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/internal_function_pointer_selector.sol @@ -0,0 +1,20 @@ +contract C { + function testFunction() internal {} + + function testYul() public returns (uint32) { + function() internal fp = testFunction; + uint selectorValue = 0; + + assembly { + selectorValue := fp.selector + } + + return uint32(bytes4(bytes32(selectorValue))); + } + function testSol() public returns (uint32) { + return uint32(testFunction.selector); + } +} +// ---- +// TypeError 8533: (198-209): Only Variables of type external function pointer support ".selector" and ".address". +// TypeError 9582: (329-350): Member "selector" not found or not visible after argument-dependent lookup in function (). diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/leave_items_on_tack.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/leave_items_on_stack.sol similarity index 100% rename from test/libsolidity/syntaxTests/inlineAssembly/invalid/leave_items_on_tack.sol rename to test/libsolidity/syntaxTests/inlineAssembly/invalid/leave_items_on_stack.sol diff --git a/test/libsolidity/syntaxTests/license/license_AND.sol b/test/libsolidity/syntaxTests/license/license_AND.sol new file mode 100644 index 000000000..eb996a948 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_AND.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0 AND GPL-2.0 +contract C {} +// ---- diff --git a/test/libsolidity/syntaxTests/license/license_OR.sol b/test/libsolidity/syntaxTests/license/license_OR.sol new file mode 100644 index 000000000..9f36bb416 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_OR.sol @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 +contract C {} +// ---- diff --git a/test/libsolidity/syntaxTests/license/license_double2.sol b/test/libsolidity/syntaxTests/license/license_double2.sol new file mode 100644 index 000000000..4c390a3d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_double2.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-3.0 +// SPDX-License-Identifier: MIT +contract C {} +// ---- +// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information. diff --git a/test/libsolidity/syntaxTests/license/license_double3.sol b/test/libsolidity/syntaxTests/license/license_double3.sol new file mode 100644 index 000000000..10ade5d9d --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_double3.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: GPL-3.0 +// SPDX-License-Identifier: GPL-3.0 +contract C {} +// ---- +// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information. diff --git a/test/libsolidity/syntaxTests/license/license_double4.sol b/test/libsolidity/syntaxTests/license/license_double4.sol new file mode 100644 index 000000000..395a458ee --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_double4.sol @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 +SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 */ +contract C {} +// ---- +// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information. diff --git a/test/libsolidity/syntaxTests/license/license_double5.sol b/test/libsolidity/syntaxTests/license/license_double5.sol new file mode 100644 index 000000000..efc22186b --- /dev/null +++ b/test/libsolidity/syntaxTests/license/license_double5.sol @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 */ /* SPDX-License-Identifier: GPL-3.0 OR GPL-2.0 */ +contract C {} +// ---- +// ParserError 3716: Multiple SPDX license identifiers found in source file. Use "AND" or "OR" to combine multiple licenses. Please see https://spdx.org for more information. diff --git a/test/libsolidity/syntaxTests/license/license_hidden_unicode.sol b/test/libsolidity/syntaxTests/license/license_hidden_unicode.sol index 5ce155f32..f93ef18c4 100644 --- a/test/libsolidity/syntaxTests/license/license_hidden_unicode.sol +++ b/test/libsolidity/syntaxTests/license/license_hidden_unicode.sol @@ -1,4 +1,4 @@ -// This is parsed as GPL-3.0: // SPDX-License-Identifier: GPL-3.0 ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ contract C {} // ---- +// ParserError 1114: Invalid SPDX license identifier. diff --git a/test/libsolidity/syntaxTests/license/license_unicode.sol b/test/libsolidity/syntaxTests/license/license_unicode.sol index 801c6e576..bcf2cdb77 100644 --- a/test/libsolidity/syntaxTests/license/license_unicode.sol +++ b/test/libsolidity/syntaxTests/license/license_unicode.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: ⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒ contract C {} // ---- -// Warning 1878: 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. +// ParserError 1114: Invalid SPDX license identifier. diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol index e7b3a6845..7d057e132 100644 --- a/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccess_super.sol @@ -10,4 +10,4 @@ contract SuperTest is Other { } } // ---- -// 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. +// TypeError 4259: (177-182): Invalid type for argument in the function call. An enum type, 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 index 328870447..0740b31cf 100644 --- a/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol +++ b/test/libsolidity/syntaxTests/metaTypes/interfaceid_super.sol @@ -14,4 +14,4 @@ abstract contract Test is ERC165 { } } // ---- -// 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. +// TypeError 4259: (592-597): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super Test) provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/super_name.sol b/test/libsolidity/syntaxTests/metaTypes/super_name.sol index 5b9ac73a1..5795b8190 100644 --- a/test/libsolidity/syntaxTests/metaTypes/super_name.sol +++ b/test/libsolidity/syntaxTests/metaTypes/super_name.sol @@ -50,4 +50,4 @@ contract D is B, C { } } // ---- -// TypeError 4259: (426-431): Invalid type for argument in the function call. A contract type or an integer type is required, but type(contract super B) provided. +// TypeError 4259: (426-431): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract super B) provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol index c28d2fe05..427caf4d0 100644 --- a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol +++ b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol @@ -4,4 +4,4 @@ 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: (65-75): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(contract Test) provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol index 050ae044c..9ed26dc6f 100644 --- a/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol +++ b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol @@ -6,4 +6,4 @@ contract Test { } } // ---- -// TypeError 4259: (154-155): Invalid type for argument in the function call. A contract type or an integer type is required, but type(struct Test.S) provided. +// TypeError 4259: (154-155): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(struct Test.S) provided. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_contract.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_contract.sol new file mode 100644 index 000000000..9b0a805ad --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_contract.sol @@ -0,0 +1,11 @@ +contract C { + modifier m { _; } + modifier mv virtual { _; } +} + +abstract contract A { + modifier m { _; } + modifier mv virtual { _; } + modifier muv virtual; +} +// ---- diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_contract_unimplemented.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_contract_unimplemented.sol new file mode 100644 index 000000000..2018ae4ea --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_contract_unimplemented.sol @@ -0,0 +1,7 @@ +contract C { + modifier mu; + modifier muv virtual; +} +// ---- +// TypeError 3656: (0-57): Contract "C" should be marked as abstract. +// TypeError 8063: (17-29): Modifiers without implementation must be marked virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_interface.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_interface.sol new file mode 100644 index 000000000..2f8281bde --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_interface.sol @@ -0,0 +1,12 @@ +interface I { + modifier m { _; } + modifier mu; + modifier mv virtual { _; } + modifier muv virtual; +} +// ---- +// TypeError 6408: (18-35): Modifiers cannot be defined or declared in interfaces. +// TypeError 6408: (40-52): Modifiers cannot be defined or declared in interfaces. +// TypeError 8063: (40-52): Modifiers without implementation must be marked virtual. +// TypeError 6408: (57-83): Modifiers cannot be defined or declared in interfaces. +// TypeError 6408: (88-109): Modifiers cannot be defined or declared in interfaces. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_library.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_library.sol new file mode 100644 index 000000000..6acbf3a0f --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_library.sol @@ -0,0 +1,5 @@ +library L { + modifier mv virtual { _; } +} +// ---- +// TypeError 3275: (16-42): Modifiers in a library cannot be virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_library_unimplemented.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_library_unimplemented.sol new file mode 100644 index 000000000..5fbc35682 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_library_unimplemented.sol @@ -0,0 +1,7 @@ +library L { + modifier mu; + modifier muv virtual; +} +// ---- +// TypeError 8063: (16-28): Modifiers without implementation must be marked virtual. +// TypeError 3275: (33-54): Modifiers in a library cannot be virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_library_virtual.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_library_virtual.sol new file mode 100644 index 000000000..173a94536 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_library_virtual.sol @@ -0,0 +1,4 @@ +library L { + modifier m { _; } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol b/test/libsolidity/syntaxTests/modifiers/use_on_interface_function.sol similarity index 67% rename from test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol rename to test/libsolidity/syntaxTests/modifiers/use_on_interface_function.sol index e506828d0..febdd9615 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol +++ b/test/libsolidity/syntaxTests/modifiers/use_on_interface_function.sol @@ -4,3 +4,4 @@ interface I { } // ---- // SyntaxError 5842: (16-60): Functions in interfaces cannot have modifiers. +// TypeError 6408: (63-82): Modifiers cannot be defined or declared in interfaces. diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol index 71063e34e..68aa61a41 100644 --- a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol @@ -5,4 +5,4 @@ contract c { mapping(S => uint) data; } // ---- -// TypeError 7804: (47-48): Only elementary types, contract types or enums are allowed as mapping keys. +// TypeError 7804: (47-48): Only elementary types, user defined value types, contract types or enums are allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol index 34f0e82b3..991f2b622 100644 --- a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol @@ -5,4 +5,4 @@ contract c { mapping(S => uint) data; } // ---- -// TypeError 7804: (49-50): Only elementary types, contract types or enums are allowed as mapping keys. +// TypeError 7804: (49-50): Only elementary types, user defined value types, contract types or enums are allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/parsing/user_defined_value_type.sol b/test/libsolidity/syntaxTests/parsing/user_defined_value_type.sol new file mode 100644 index 000000000..e4bd137a3 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/user_defined_value_type.sol @@ -0,0 +1,5 @@ +type MyInt is uint; + +contract C { + type MyAddress is address; +} diff --git a/test/libsolidity/syntaxTests/parsing/user_defined_value_type_err.sol b/test/libsolidity/syntaxTests/parsing/user_defined_value_type_err.sol new file mode 100644 index 000000000..dc1208bfd --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/user_defined_value_type_err.sol @@ -0,0 +1,3 @@ +type(MyInt) is uint256; +// ---- +// ParserError 2314: (4-5): Expected identifier but got '(' diff --git a/test/libsolidity/syntaxTests/parsing/user_defined_value_type_in_function_err.sol b/test/libsolidity/syntaxTests/parsing/user_defined_value_type_in_function_err.sol new file mode 100644 index 000000000..6e31db226 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/user_defined_value_type_in_function_err.sol @@ -0,0 +1,6 @@ +function f() { + type(uint).max; + type MyInt is int; +} +// ---- +// ParserError 2314: (44-49): Expected ';' but got identifier diff --git a/test/libsolidity/syntaxTests/tryCatch/no_special.sol b/test/libsolidity/syntaxTests/tryCatch/no_special.sol index 6cfed6eda..d760e6ee3 100644 --- a/test/libsolidity/syntaxTests/tryCatch/no_special.sol +++ b/test/libsolidity/syntaxTests/tryCatch/no_special.sol @@ -14,4 +14,4 @@ contract C { // ---- // 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. +// TypeError 4259: (176-183): Invalid type for argument in the function call. An enum type, contract type or an integer type is required, but type(address) provided. diff --git a/test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol b/test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol index 5dc39266f..be4e1dde0 100644 --- a/test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol +++ b/test/libsolidity/syntaxTests/types/struct_mapping_recursion.sol @@ -6,4 +6,4 @@ contract C { function g (S calldata) external view {} } // ---- -// TypeError 7804: (56-57): Only elementary types, contract types or enums are allowed as mapping keys. +// TypeError 7804: (56-57): Only elementary types, user defined value types, contract types or enums are allowed as mapping keys. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/all_value_types.sol b/test/libsolidity/syntaxTests/userDefinedValueType/all_value_types.sol new file mode 100644 index 000000000..3388c1976 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/all_value_types.sol @@ -0,0 +1,12 @@ +type MyAddress is address; +type MyAddressPayable is address payable; +type MyInt is int; +type MyUInt is uint; +type MyInt128 is int128; +type MyUInt128 is uint128; +// TODO add fixed point type, when it's implemented +type MyFixedBytes32 is bytes32; +type MyFixedBytes1 is bytes1; +type MyBool is bool; +/// test to see if having NatSpec causes issues +type redundantNatSpec is bytes2; diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/constant.sol b/test/libsolidity/syntaxTests/userDefinedValueType/constant.sol new file mode 100644 index 000000000..febe08193 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/constant.sol @@ -0,0 +1,8 @@ +contract C { + type MyInt is int; + MyInt constant mi = MyInt.wrap(5); + // This is currently unsupported. + uint[MyInt.unwrap(mi)] arr; +} +// ---- +// TypeError 5462: (122-138): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/conversion_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/conversion_err.sol new file mode 100644 index 000000000..33c81aee5 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/conversion_err.sol @@ -0,0 +1,6 @@ +type MyAddress is address; +function f() { + MyAddress a = MyAddress(5, 2); +} +// ---- +// TypeError 2558: (60-75): Exactly one argument expected for explicit type conversion. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversion_self.sol b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversion_self.sol new file mode 100644 index 000000000..c2fdc84e4 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversion_self.sol @@ -0,0 +1,4 @@ +type MyInt is int; +function f(MyInt a) pure returns (MyInt b) { + b = MyInt(a); +} diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversions_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversions_err.sol new file mode 100644 index 000000000..24a7a071e --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversions_err.sol @@ -0,0 +1,24 @@ +type MyUInt is uint; +type MyAddress is address; +type AnotherUInt is uint; + +function f() pure { + MyUInt(-1); + MyAddress(-1); + MyUInt(5); + MyAddress(address(5)); + + AnotherUInt(MyUInt.wrap(5)); + MyUInt(AnotherUInt.wrap(10)); + AnotherUInt.unwrap(MyUInt.wrap(5)); + MyUInt.unwrap(AnotherUInt.wrap(10)); +} +// ---- +// TypeError 9640: (99-109): Explicit type conversion not allowed from "int_const -1" to "MyUInt". +// TypeError 9640: (115-128): Explicit type conversion not allowed from "int_const -1" to "MyAddress". +// TypeError 9640: (134-143): Explicit type conversion not allowed from "int_const 5" to "MyUInt". +// TypeError 9640: (149-170): Explicit type conversion not allowed from "address" to "MyAddress". +// TypeError 9640: (177-204): Explicit type conversion not allowed from "MyUInt" to "AnotherUInt". +// TypeError 9640: (210-238): Explicit type conversion not allowed from "AnotherUInt" to "MyUInt". +// TypeError 9553: (263-277): Invalid type for argument in function call. Invalid implicit conversion from MyUInt to AnotherUInt requested. +// TypeError 9553: (298-318): Invalid type for argument in function call. Invalid implicit conversion from AnotherUInt to MyUInt requested. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversions_wrap.sol b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversions_wrap.sol new file mode 100644 index 000000000..6d10e8b20 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_conversions_wrap.sol @@ -0,0 +1,10 @@ +type MyUint is uint; +type MyAddress is address; + +function f() pure { + MyUint.wrap(5); + MyAddress.wrap(address(5)); +} +// ---- +// Warning 6133: (73-87): Statement has no effect. +// Warning 6133: (93-119): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/explicit_literal_conversion_wrap.sol b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_literal_conversion_wrap.sol new file mode 100644 index 000000000..0fb851196 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/explicit_literal_conversion_wrap.sol @@ -0,0 +1,12 @@ +type MyAddress is address; +type MyUInt8 is uint8; + +function f() pure { + MyAddress.wrap(address(5)); + MyUInt8.wrap(5); + MyUInt8.wrap(50); +} +// ---- +// Warning 6133: (75-101): Statement has no effect. +// Warning 6133: (107-122): Statement has no effect. +// Warning 6133: (128-144): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/forward_reference_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/forward_reference_err.sol new file mode 100644 index 000000000..9ea93cb09 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/forward_reference_err.sol @@ -0,0 +1,4 @@ +function f(MyIntB x) pure {} +type MyIntB is MyIntB; +// ---- +// TypeError 8657: (44-50): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/implicit_conversion.sol b/test/libsolidity/syntaxTests/userDefinedValueType/implicit_conversion.sol new file mode 100644 index 000000000..8837db7d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/implicit_conversion.sol @@ -0,0 +1,14 @@ +type MyInt is uint; +type MyAddress is address; +function f() pure { + MyInt a; + MyInt b = a; + MyAddress c; + MyAddress d = c; + b; + d; +} + +function g(MyInt a) pure returns (MyInt) { + return a; +} diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/implicit_conversion_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/implicit_conversion_err.sol new file mode 100644 index 000000000..3b6179845 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/implicit_conversion_err.sol @@ -0,0 +1,22 @@ +type MyInt is int; +function f(int a) pure returns (int) { + MyInt b = a; + + int c = b; + + address d = b; + + MyInt e = d; + + uint x = 0; + MyInt y = MyInt(x); + + return e; +} +// ---- +// TypeError 9574: (62-73): Type int256 is not implicitly convertible to expected type MyInt. +// TypeError 9574: (80-89): Type MyInt is not implicitly convertible to expected type int256. +// TypeError 9574: (96-109): Type MyInt is not implicitly convertible to expected type address. +// TypeError 9574: (116-127): Type address is not implicitly convertible to expected type MyInt. +// TypeError 9640: (160-168): Explicit type conversion not allowed from "uint256" to "MyInt". +// TypeError 6359: (182-183): Return argument type MyInt is not implicitly convertible to expected type (type of first return variable) int256. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/mapping_key.sol b/test/libsolidity/syntaxTests/userDefinedValueType/mapping_key.sol new file mode 100644 index 000000000..ae83ae4c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/mapping_key.sol @@ -0,0 +1,4 @@ +type MyInt is int; +contract C { + mapping(MyInt => int) m; +} diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/multisource1.sol b/test/libsolidity/syntaxTests/userDefinedValueType/multisource1.sol new file mode 100644 index 000000000..b6a7b5bcc --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/multisource1.sol @@ -0,0 +1,10 @@ +==== Source: A ==== +type MyInt is int; +type MyAddress is address; +==== Source: B ==== +import {MyAddress as OurAddress} from "A"; +contract A { + function f(int x) external view returns(MyInt) { return MyInt.wrap(x); } +} +// ---- +// DeclarationError 7920: (B:100-105): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/multisource2.sol b/test/libsolidity/syntaxTests/userDefinedValueType/multisource2.sol new file mode 100644 index 000000000..a0e7f9ec0 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/multisource2.sol @@ -0,0 +1,10 @@ +==== Source: A ==== +type MyInt is int; +type MyAddress is address; +==== Source: B ==== +import {MyAddress as OurAddress} from "A"; +contract A { + function f(address x) external view returns(MyAddress) { return MyAddress.wrap(x); } +} +// ---- +// DeclarationError 7920: (B:104-113): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/multisource3.sol b/test/libsolidity/syntaxTests/userDefinedValueType/multisource3.sol new file mode 100644 index 000000000..ed2cbde28 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/multisource3.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +type MyInt is int; +==== Source: s2.sol ==== +import "s1.sol" as M; +contract C { + function f(int x) public pure returns (M.MyInt) { return M.MyInt.wrap(x); } + function g(M.MyInt x) public pure returns (int) { return M.MyInt.unwrap(x); } +} +// ---- diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/multisource4.sol b/test/libsolidity/syntaxTests/userDefinedValueType/multisource4.sol new file mode 100644 index 000000000..92a1e54f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/multisource4.sol @@ -0,0 +1,9 @@ +==== Source: s1.sol ==== +type MyInt is int; +==== Source: s2.sol ==== +import "s1.sol" as M; +contract C { + function f(int x) public pure returns (MyInt) { return MyInt.wrap(x); } +} +// ---- +// DeclarationError 7920: (s2.sol:76-81): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/name_conflict_contract_warning.sol b/test/libsolidity/syntaxTests/userDefinedValueType/name_conflict_contract_warning.sol new file mode 100644 index 000000000..8c5306bc4 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/name_conflict_contract_warning.sol @@ -0,0 +1,6 @@ +type MyAddress is address; +contract C { + type MyAddress is address; +} +// ---- +// Warning 2519: (44-70): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/name_conflict_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/name_conflict_err.sol new file mode 100644 index 000000000..6573111ea --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/name_conflict_err.sol @@ -0,0 +1,9 @@ +type MyInt is int; +type MyInt is address; +contract C { + type MyAddress is address; + type MyAddress is address; +} +// ---- +// DeclarationError 2333: (19-41): Identifier already declared. +// DeclarationError 2333: (90-116): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_bytes.sol b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_bytes.sol new file mode 100644 index 000000000..2bb6c5809 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_bytes.sol @@ -0,0 +1,3 @@ +type MyBytes is bytes; +// ---- +// TypeError 8129: (0-22): The underlying type of the user defined value type "MyBytes" is not a value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_contract_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_contract_err.sol new file mode 100644 index 000000000..5a86e92f9 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_contract_err.sol @@ -0,0 +1,4 @@ +contract C {} +type MyContract is C; +// ---- +// TypeError 8657: (33-34): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_function_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_function_err.sol new file mode 100644 index 000000000..78d9c7043 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_function_err.sol @@ -0,0 +1,3 @@ +type MyFunction is function(uint) returns (uint); +// ---- +// TypeError 8657: (19-49): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_mapping_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_mapping_err.sol new file mode 100644 index 000000000..9f894c521 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_mapping_err.sol @@ -0,0 +1,3 @@ +type MyInt is mapping(uint => uint); +// ---- +// TypeError 8657: (14-35): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_string_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_string_err.sol new file mode 100644 index 000000000..b44b23bdb --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_string_err.sol @@ -0,0 +1,3 @@ +type MyString is string; +// ---- +// TypeError 8129: (0-24): The underlying type of the user defined value type "MyString" is not a value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_struct_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_struct_err.sol new file mode 100644 index 000000000..3a713a887 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/non_value_type_struct_err.sol @@ -0,0 +1,7 @@ +struct S {uint x;} + +contract C { + type MyType is S; +} +// ---- +// TypeError 8657: (52-53): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/overload_clash_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/overload_clash_err.sol new file mode 100644 index 000000000..a84930433 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/overload_clash_err.sol @@ -0,0 +1,19 @@ +type MyAddress is address; +interface I {} +contract C { + function f(MyAddress a) external { + } + function f(address a) external { + } +} +contract D { + function g(MyAddress a) external { + } +} +contract E is D { + function g(I a) external { + } +} +// ---- +// TypeError 9914: (104-142): Function overload clash during conversion to external types for arguments. +// TypeError 9914: (162-202): Function overload clash during conversion to external types for arguments. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/recursive_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/recursive_err.sol new file mode 100644 index 000000000..4130edde5 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/recursive_err.sol @@ -0,0 +1,4 @@ +type MyInt1 is MyInt2; +type MyInt2 is MyInt1; +// ---- +// TypeError 8657: (15-21): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/recursive_function_paramter_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/recursive_function_paramter_err.sol new file mode 100644 index 000000000..52e99311e --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/recursive_function_paramter_err.sol @@ -0,0 +1,3 @@ +type MyFunction is function(MyFunction) external returns(MyFunction); +// ---- +// TypeError 8657: (19-69): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/reference.sol b/test/libsolidity/syntaxTests/userDefinedValueType/reference.sol new file mode 100644 index 000000000..81a1ae8af --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/reference.sol @@ -0,0 +1,13 @@ +library L { + type MyInt is int; +} + +contract C { + L.MyInt a; + type MyInt is int8; +} + +contract D is C { + C.MyInt b; + L.MyInt c; +} diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/repetition_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/repetition_err.sol new file mode 100644 index 000000000..d9fc18d7f --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/repetition_err.sol @@ -0,0 +1,7 @@ +type MyInt is int; +type MyInt is int; +type MyAddress is address; +type MyAddress is uint; +// ---- +// DeclarationError 2333: (19-37): Identifier already declared. +// DeclarationError 2333: (65-88): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/self_reference_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/self_reference_err.sol new file mode 100644 index 000000000..20c657c73 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/self_reference_err.sol @@ -0,0 +1,3 @@ +type MyInt is MyInt; +// ---- +// TypeError 8657: (14-19): The underlying type for a user defined value type has to be an elementary value type. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_assign_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_assign_err.sol new file mode 100644 index 000000000..15bc15fa0 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_assign_err.sol @@ -0,0 +1,8 @@ +type MyInt is int; +function test() pure { + function (MyInt) returns (int) f = MyInt.unwrap; + function (int) returns (MyInt) g = MyInt.wrap; +} +// ---- +// TypeError 9574: (46-93): Type function (MyInt) pure returns (int256) is not implicitly convertible to expected type function (MyInt) returns (int256). Special functions can not be converted to function types. +// TypeError 9574: (99-144): Type function (int256) pure returns (MyInt) is not implicitly convertible to expected type function (int256) returns (MyInt). Special functions can not be converted to function types. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_calls_err.sol b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_calls_err.sol new file mode 100644 index 000000000..d4ee407fd --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_calls_err.sol @@ -0,0 +1,16 @@ +type MyInt is int; +function f() { + MyInt.wrap(5, 6, 7); + MyInt.wrap({test: 5}); + MyInt.wrap(); + MyInt.unwrap(5); + MyInt.unwrap({test: 5}); + MyInt.unwrap(MyInt.wrap(1), MyInt.wrap(2)); +} +// ---- +// TypeError 6160: (38-57): Wrong argument count for function call: 3 arguments given but expected 1. +// TypeError 4974: (63-84): Named argument "test" does not match function declaration. +// TypeError 6160: (90-102): Wrong argument count for function call: 0 arguments given but expected 1. +// TypeError 9553: (121-122): Invalid type for argument in function call. Invalid implicit conversion from int_const 5 to MyInt requested. +// TypeError 4974: (129-152): Named argument "test" does not match function declaration. +// TypeError 6160: (158-200): Wrong argument count for function call: 2 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_via_contract_name.sol b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_via_contract_name.sol new file mode 100644 index 000000000..1d9c6eb13 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_via_contract_name.sol @@ -0,0 +1,9 @@ +contract C { type T is uint; } +library L { type T is uint; } +interface I { type T is uint; } +contract D +{ + C.T x = C.T.wrap(uint(1)); + L.T y = L.T.wrap(uint(1)); + I.T z = I.T.wrap(uint(1)); +} diff --git a/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_via_contract_name_different.sol b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_via_contract_name_different.sol new file mode 100644 index 000000000..334cb9fb0 --- /dev/null +++ b/test/libsolidity/syntaxTests/userDefinedValueType/wrap_unwrap_via_contract_name_different.sol @@ -0,0 +1,8 @@ +contract C { type T is uint; } +library L { type T is uint; } +contract D +{ + C.T x = L.T.wrap(uint(1)); +} +// ---- +// TypeError 7407: (86-103): Type L.T is not implicitly convertible to expected type C.T. diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index 25638764e..6745bd1df 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -21,6 +21,7 @@ #include #include +#include #include @@ -108,7 +109,7 @@ bytes BytesUtils::convertFixedPoint(string const& _literal, size_t& o_fractional u256 value(valueInteger); if (negative) value = s2u(-u2s(value)); - return util::toBigEndian(value); + return toBigEndian(value); } catch (std::exception const&) { @@ -193,7 +194,7 @@ string BytesUtils::formatHexString(bytes const& _bytes) { stringstream os; - os << "hex\"" << toHex(_bytes) << "\""; + os << "hex\"" << util::toHex(_bytes) << "\""; return os.str(); } diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index df75dcad1..6f9aec64d 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -963,7 +963,7 @@ BOOST_AUTO_TEST_CASE(call_effects) std::map builtins; builtins["builtin_returning_call_effect"] = [](FunctionCall const&) -> std::optional { - return util::toBigEndian(u256(0x1234)); + return toBigEndian(u256(0x1234)); }; builtins["builtin_returning_call_effect_no_ret"] = [](FunctionCall const&) -> std::optional { diff --git a/test/libsolutil/CommonIO.cpp b/test/libsolutil/CommonIO.cpp index 12b99df7b..ce1a24a41 100644 --- a/test/libsolutil/CommonIO.cpp +++ b/test/libsolutil/CommonIO.cpp @@ -66,6 +66,30 @@ BOOST_AUTO_TEST_CASE(readFileAsString_symlink) BOOST_TEST(readFileAsString(tempDir.path() / "symlink.txt") == "ABC\ndef\n"); } +BOOST_AUTO_TEST_CASE(readUntilEnd_no_ending_newline) +{ + istringstream inputStream("ABC\ndef"); + BOOST_TEST(readUntilEnd(inputStream) == "ABC\ndef"); +} + +BOOST_AUTO_TEST_CASE(readUntilEnd_with_ending_newline) +{ + istringstream inputStream("ABC\ndef\n"); + BOOST_TEST(readUntilEnd(inputStream) == "ABC\ndef\n"); +} + +BOOST_AUTO_TEST_CASE(readUntilEnd_cr_lf_newline) +{ + istringstream inputStream("ABC\r\ndef"); + BOOST_TEST(readUntilEnd(inputStream) == "ABC\r\ndef"); +} + +BOOST_AUTO_TEST_CASE(readUntilEnd_empty) +{ + istringstream inputStream(""); + BOOST_TEST(readUntilEnd(inputStream) == ""); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace solidity::util::test diff --git a/test/libsolutil/JSON.cpp b/test/libsolutil/JSON.cpp index 666397877..1b0ed9388 100644 --- a/test/libsolutil/JSON.cpp +++ b/test/libsolutil/JSON.cpp @@ -33,6 +33,48 @@ namespace solidity::util::test BOOST_AUTO_TEST_SUITE(JsonTest, *boost::unit_test::label("nooptions")) +BOOST_AUTO_TEST_CASE(json_types) +{ + auto check = [](Json::Value value, string const& expectation) { + BOOST_CHECK(jsonCompactPrint(value) == expectation); + }; + + Json::Value value; + BOOST_CHECK(value.empty()); + value = {}; + BOOST_CHECK(value.empty()); + value = Json::Value(); + BOOST_CHECK(value.empty()); + value = Json::nullValue; + BOOST_CHECK(value.empty()); + + check(value, "null"); + check({}, "null"); + check(Json::Value(), "null"); + check(Json::nullValue, "null"); + check(Json::objectValue, "{}"); + check(Json::arrayValue, "[]"); + check(Json::UInt(1), "1"); + check(Json::UInt(-1), "4294967295"); + check(Json::UInt64(1), "1"); + check(Json::UInt64(-1), "18446744073709551615"); + check(Json::LargestUInt(1), "1"); + check(Json::LargestUInt(-1), "18446744073709551615"); + check(Json::LargestUInt(0xffffffff), "4294967295"); + check(Json::Value("test"), "\"test\""); + check("test", "\"test\""); + check(true, "true"); + + value = Json::objectValue; + value["key"] = "value"; + check(value, "{\"key\":\"value\"}"); + + value = Json::arrayValue; + value.append(1); + value.append(2); + check(value, "[1,2]"); +} + BOOST_AUTO_TEST_CASE(json_pretty_print) { Json::Value json; @@ -43,6 +85,8 @@ BOOST_AUTO_TEST_CASE(json_pretty_print) json["1"] = 1; json["2"] = "2"; json["3"] = jsonChild; + json["4"] = "ऑ ऒ ओ औ क ख"; + json["5"] = "\xff"; BOOST_CHECK( "{\n" @@ -52,7 +96,9 @@ BOOST_AUTO_TEST_CASE(json_pretty_print) " {\n" " \"3.1\": \"3.1\",\n" " \"3.2\": 2\n" - " }\n" + " },\n" + " \"4\": \"\\u0911 \\u0912 \\u0913 \\u0914 \\u0915 \\u0916\",\n" + " \"5\": \"\\ufffd\"\n" "}" == jsonPrettyPrint(json)); } @@ -66,26 +112,32 @@ BOOST_AUTO_TEST_CASE(json_compact_print) json["1"] = 1; json["2"] = "2"; json["3"] = jsonChild; + json["4"] = "ऑ ऒ ओ औ क ख"; + json["5"] = "\xff"; - BOOST_CHECK("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}" == jsonCompactPrint(json)); + BOOST_CHECK("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2},\"4\":\"\\u0911 \\u0912 \\u0913 \\u0914 \\u0915 \\u0916\",\"5\":\"\\ufffd\"}" == jsonCompactPrint(json)); } BOOST_AUTO_TEST_CASE(parse_json_strict) { + // In this test we check conformance against JSON.parse (https://tc39.es/ecma262/multipage/structured-data.html#sec-json.parse) + // and ECMA-404 (https://www.ecma-international.org/publications-and-standards/standards/ecma-404/) + Json::Value json; std::string errors; - // just parse a valid json input + // Just parse a valid json input BOOST_CHECK(jsonParseStrict("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}", json, &errors)); BOOST_CHECK(json["1"] == 1); BOOST_CHECK(json["2"] == "2"); BOOST_CHECK(json["3"]["3.1"] == "3.1"); BOOST_CHECK(json["3"]["3.2"] == 2); - // trailing garbage is not allowed in strict-mode + // Trailing garbage is not allowed in ECMA-262 BOOST_CHECK(!jsonParseStrict("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); - // comments are allowed in strict-mode? - that's strange... + // Comments are not allowed in ECMA-262 + // ... but JSONCPP allows them BOOST_CHECK(jsonParseStrict( "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":5}}", json, &errors )); @@ -94,15 +146,42 @@ BOOST_AUTO_TEST_CASE(parse_json_strict) BOOST_CHECK(json["3"]["3.1"] == "3.1"); BOOST_CHECK(json["3"]["3.2"] == 5); - // root element can only be object or array + // According to ECMA-404 object, array, number, string, true, false, null are allowed + // ... but JSONCPP disallows value types BOOST_CHECK(jsonParseStrict("[]", json, &errors)); + BOOST_CHECK(json.isArray()); BOOST_CHECK(jsonParseStrict("{}", json, &errors)); + BOOST_CHECK(json.isObject()); BOOST_CHECK(!jsonParseStrict("1", json, &errors)); + // BOOST_CHECK(json.isNumeric()); BOOST_CHECK(!jsonParseStrict("\"hello\"", json, &errors)); + // BOOST_CHECK(json.isString()); + BOOST_CHECK(!jsonParseStrict("true", json, &errors)); + // BOOST_CHECK(json.isBool()); + BOOST_CHECK(!jsonParseStrict("null", json, &errors)); + // BOOST_CHECK(json.isNull()); - // non-UTF-8 escapes allowed?? + // Single quotes are also disallowed by ECMA-404 + BOOST_CHECK(!jsonParseStrict("'hello'", json, &errors)); + // BOOST_CHECK(json.isString()); + + // Only string keys in objects are allowed in ECMA-404 + BOOST_CHECK(!jsonParseStrict("{ 42: \"hello\" }", json, &errors)); + + // According to ECMA-404 hex escape sequences are not allowed, only unicode (\uNNNN) and + // a few control characters (\b, \f, \n, \r, \t) + // + // More lenient parsers allow hex escapes as long as they translate to a valid UTF-8 encoding. + // + // ... but JSONCPP allows any hex escapes BOOST_CHECK(jsonParseStrict("[ \"\x80\xec\x80\" ]", json, &errors)); + BOOST_CHECK(json.isArray()); BOOST_CHECK(json[0] == "\x80\xec\x80"); + + // This would be valid more lenient parsers. + BOOST_CHECK(jsonParseStrict("[ \"\xF0\x9F\x98\x8A\" ]", json, &errors)); + BOOST_CHECK(json.isArray()); + BOOST_CHECK(json[0] == "😊"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libyul/ControlFlowGraphTest.cpp b/test/libyul/ControlFlowGraphTest.cpp index 1107f4ca2..dffc7ecbf 100644 --- a/test/libyul/ControlFlowGraphTest.cpp +++ b/test/libyul/ControlFlowGraphTest.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -52,26 +53,6 @@ ControlFlowGraphTest::ControlFlowGraphTest(string const& _filename): namespace { -static std::string stackSlotToString(StackSlot const& _slot) -{ - return std::visit(util::GenericVisitor{ - [](FunctionCallReturnLabelSlot const& _ret) -> std::string { return "RET[" + _ret.call.get().functionName.name.str() + "]"; }, - [](FunctionReturnLabelSlot const&) -> std::string { return "RET"; }, - [](VariableSlot const& _var) { return _var.variable.get().name.str(); }, - [](LiteralSlot const& _lit) { return util::toCompactHexWithPrefix(_lit.value); }, - [](TemporarySlot const& _tmp) -> std::string { return "TMP[" + _tmp.call.get().functionName.name.str() + ", " + std::to_string(_tmp.index) + "]"; }, - [](JunkSlot const&) -> std::string { return "JUNK"; } - }, _slot); -} - -static std::string stackToString(Stack const& _stack) -{ - std::string result("[ "); - for (auto const& slot: _stack) - result += stackSlotToString(slot) + ' '; - result += ']'; - return result; -} static std::string variableSlotToString(VariableSlot const& _slot) { return _slot.variable.get().name.str(); @@ -219,7 +200,7 @@ TestCase::TestResult ControlFlowGraphTest::run(ostream& _stream, string const& _ { ErrorList errors; auto [object, analysisInfo] = parse(m_source, *m_dialect, errors); - if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) + if (!object || !analysisInfo || Error::containsErrors(errors)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; return TestResult::FatalError; diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index 2c3560726..37b1688a9 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -81,7 +81,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li solAssert(obj.bytecode, ""); m_obtainedResult = "Text:\n" + obj.assembly + "\n"; - m_obtainedResult += "Binary:\n" + toHex(obj.bytecode->bytecode) + "\n"; + m_obtainedResult += "Binary:\n" + util::toHex(obj.bytecode->bytecode) + "\n"; } else { @@ -95,7 +95,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li else m_obtainedResult += "Bytecode: " + - toHex(obj.bytecode->bytecode) + + util::toHex(obj.bytecode->bytecode) + "\nOpcodes: " + boost::trim_copy(evmasm::disassemble(obj.bytecode->bytecode)) + "\nSourceMappings:" + diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index 1d4e0abc7..d70d2002c 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -72,7 +72,7 @@ pair parse(string const& _source) return {false, {}}; } -optional parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +optional parseAndReturnFirstError(string const& _source, bool _allowWarningsAndInfos = true) { bool success; ErrorList errors; @@ -85,11 +85,11 @@ optional parseAndReturnFirstError(string const& _source, bool _allowWarni else { // If success is true, there might still be an error in the assembly stage. - if (_allowWarnings && Error::containsOnlyWarnings(errors)) + if (_allowWarningsAndInfos && !Error::containsErrors(errors)) return {}; else if (!errors.empty()) { - if (!_allowWarnings) + if (!_allowWarningsAndInfos) BOOST_CHECK_EQUAL(errors.size(), 1); return *errors.front(); } @@ -97,15 +97,15 @@ optional parseAndReturnFirstError(string const& _source, bool _allowWarni return {}; } -bool successParse(string const& _source, bool _allowWarnings = true) +bool successParse(string const& _source, bool _allowWarningsAndInfos = true) { - return !parseAndReturnFirstError(_source, _allowWarnings); + return !parseAndReturnFirstError(_source, _allowWarningsAndInfos); } -Error expectError(string const& _source, bool _allowWarnings = false) +Error expectError(string const& _source, bool _allowWarningsAndInfos = false) { - auto error = parseAndReturnFirstError(_source, _allowWarnings); + auto error = parseAndReturnFirstError(_source, _allowWarningsAndInfos); BOOST_REQUIRE(error); return *error; } @@ -121,8 +121,9 @@ tuple, ErrorList> tryGetSourceLocationMapping(string _so Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(EVMVersion::berlin()); ObjectParser objectParser{reporter, dialect}; CharStream stream(move(source), ""); - objectParser.parse(make_shared(stream), false); - return {objectParser.sourceNameMapping(), std::move(errors)}; + auto object = objectParser.parse(make_shared(stream), false); + BOOST_REQUIRE(object && object->debugData); + return {object->debugData->sourceNames, std::move(errors)}; } } diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 4a95f625e..49152e7f8 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -44,6 +44,9 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; +BOOST_TEST_DONT_PRINT_LOG_VALUE(ErrorId) +BOOST_TEST_DONT_PRINT_LOG_VALUE(Error::Type) + namespace solidity::yul::test { @@ -82,7 +85,7 @@ shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorRep return {}; } -std::optional parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarnings = true) +std::optional parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarningsAndInfos = true) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -95,11 +98,11 @@ std::optional parseAndReturnFirstError(string const& _source, Dialect con else { // If success is true, there might still be an error in the assembly stage. - if (_allowWarnings && Error::containsOnlyWarnings(errors)) + if (_allowWarningsAndInfos && !Error::containsErrors(errors)) return {}; else if (!errors.empty()) { - if (!_allowWarnings) + if (!_allowWarningsAndInfos) BOOST_CHECK_EQUAL(errors.size(), 1); return *errors.front(); } @@ -107,15 +110,15 @@ std::optional parseAndReturnFirstError(string const& _source, Dialect con return {}; } -bool successParse(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarnings = true) +bool successParse(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarningsAndInfos = true) { - return !parseAndReturnFirstError(_source, _dialect, _allowWarnings); + return !parseAndReturnFirstError(_source, _dialect, _allowWarningsAndInfos); } -Error expectError(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarnings = false) +Error expectError(std::string const& _source, Dialect const& _dialect = Dialect::yulDeprecated(), bool _allowWarningsAndInfos = false) { - auto error = parseAndReturnFirstError(_source, _dialect, _allowWarnings); + auto error = parseAndReturnFirstError(_source, _dialect, _allowWarningsAndInfos); BOOST_REQUIRE(error); return *error; } @@ -165,7 +168,7 @@ BOOST_AUTO_TEST_CASE(default_types_set) EVMDialectTyped::instance(EVMVersion{}), reporter ); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); // Use no dialect so that all types are printed. // This tests that the default types are properly assigned. @@ -210,8 +213,8 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block) "{}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) @@ -229,12 +232,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(3, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543); - CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 234, 543); + CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source0", 123, 432); // [2] is inherited source location - CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(2)), "source0", 123, 432); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) @@ -251,13 +254,13 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(3, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 234, 543); - CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 234, 543); + CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source1", 123, 432); // [2] is inherited source location - CHECK_LOCATION(locationOf(result->statements.at(2)), "source1", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(2)), "source1", 123, 432); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) @@ -273,10 +276,10 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(2, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 343, 434); + CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source0", 343, 434); } BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) @@ -297,20 +300,20 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, "source0", 234, 543); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(2, result->statements.size()); BOOST_REQUIRE(holds_alternative(result->statements.at(1))); auto const& switchStmt = get(result->statements.at(1)); - CHECK_LOCATION(switchStmt.debugData->location, "source0", 343, 434); + CHECK_LOCATION(switchStmt.debugData->originLocation, "source0", 343, 434); BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size()); - CHECK_LOCATION(switchStmt.cases.at(0).debugData->location, "source0", 3141, 59265); + CHECK_LOCATION(switchStmt.cases.at(0).debugData->originLocation, "source0", 3141, 59265); auto const& caseBody = switchStmt.cases.at(0).body; BOOST_REQUIRE_EQUAL(1, caseBody.statements.size()); - CHECK_LOCATION(locationOf(caseBody.statements.at(0)), "source0", 271, 828); + CHECK_LOCATION(originLocationOf(caseBody.statements.at(0)), "source0", 271, 828); } BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) @@ -329,21 +332,21 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); - CHECK_LOCATION(result->debugData->location, "source0", 1, 100); + CHECK_LOCATION(result->debugData->originLocation, "source0", 1, 100); BOOST_REQUIRE_EQUAL(3, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 1, 100); + CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 1, 100); // First child element must be a block itself with one statement. BOOST_REQUIRE(holds_alternative(result->statements.at(0))); BOOST_REQUIRE_EQUAL(get(result->statements.at(0)).statements.size(), 1); - CHECK_LOCATION(locationOf(get(result->statements.at(0)).statements.at(0)), "source0", 123, 432); + CHECK_LOCATION(originLocationOf(get(result->statements.at(0)).statements.at(0)), "source0", 123, 432); // The next two elements have an inherited source location from the prior inner scope. - CHECK_LOCATION(locationOf(result->statements.at(1)), "source0", 123, 432); - CHECK_LOCATION(locationOf(result->statements.at(2)), "source0", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source0", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(2)), "source0", 123, 432); } BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) @@ -360,10 +363,10 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); // should still parse + BOOST_REQUIRE(!!result && errorList.size() == 0); // should still parse BOOST_REQUIRE_EQUAL(2, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432); - CHECK_LOCATION(locationOf(result->statements.at(1)), "source1", 1, 10); + CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source1", 1, 10); } BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index) @@ -382,6 +385,9 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); // should still parse + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 2674_error); } BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1) @@ -398,13 +404,13 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1) "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(1, result->statements.size()); - CHECK_LOCATION(locationOf(result->statements.at(0)), "source0", 123, 432); + CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 123, 432); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); - CHECK_LOCATION(locationOf(*varDecl.value), "source0", 234, 2026); + CHECK_LOCATION(originLocationOf(*varDecl.value), "source0", 234, 2026); } BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) @@ -421,22 +427,22 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(1, result->statements.size()); - CHECK_LOCATION(result->debugData->location, "source0", 0, 5); + CHECK_LOCATION(result->debugData->originLocation, "source0", 0, 5); // `let x := add(1, ` BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); - CHECK_LOCATION(varDecl.debugData->location, "source0", 0, 5); + CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 0, 5); BOOST_REQUIRE(!!varDecl.value); BOOST_REQUIRE(holds_alternative(*varDecl.value)); FunctionCall const& call = get(*varDecl.value); - CHECK_LOCATION(call.debugData->location, "source1", 2, 3); + CHECK_LOCATION(call.debugData->originLocation, "source1", 2, 3); // `2` BOOST_REQUIRE_EQUAL(2, call.arguments.size()); - CHECK_LOCATION(locationOf(call.arguments.at(1)), "source0", 4, 8); + CHECK_LOCATION(originLocationOf(call.arguments.at(1)), "source0", 4, 8); } BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) @@ -455,26 +461,26 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(2, result->statements.size()); - CHECK_LOCATION(result->debugData->location, "source1", 23, 45); + CHECK_LOCATION(result->debugData->originLocation, "source1", 23, 45); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); Block const& innerBlock = get(result->statements.at(0)); - CHECK_LOCATION(innerBlock.debugData->location, "source1", 23, 45); + CHECK_LOCATION(innerBlock.debugData->originLocation, "source1", 23, 45); BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size()); BOOST_REQUIRE(holds_alternative(result->statements.at(1))); ExpressionStatement const& sstoreStmt = get(innerBlock.statements.at(0)); BOOST_REQUIRE(holds_alternative(sstoreStmt.expression)); FunctionCall const& sstoreCall = get(sstoreStmt.expression); - CHECK_LOCATION(sstoreCall.debugData->location, "source1", 23, 45); + CHECK_LOCATION(sstoreCall.debugData->originLocation, "source1", 23, 45); BOOST_REQUIRE(holds_alternative(result->statements.at(1))); ExpressionStatement mstoreStmt = get(result->statements.at(1)); BOOST_REQUIRE(holds_alternative(mstoreStmt.expression)); FunctionCall const& mstoreCall = get(mstoreStmt.expression); - CHECK_LOCATION(mstoreCall.debugData->location, "source0", 420, 680); + CHECK_LOCATION(mstoreCall.debugData->originLocation, "source0", 420, 680); } BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) @@ -491,13 +497,13 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(1, result->statements.size()); - CHECK_LOCATION(result->debugData->location, "source1", 23, 45); + CHECK_LOCATION(result->debugData->originLocation, "source1", 23, 45); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); - CHECK_LOCATION(varDecl.debugData->location, "source0", 420, 680); + CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 420, 680); } BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix) @@ -511,7 +517,24 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix) EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, "", -1, -1); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 8387_error); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_prefix) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// abc@src 0:111:222 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); } BOOST_AUTO_TEST_CASE(customSourceLocations_unspecified) @@ -524,8 +547,42 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_unspecified) )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_non_integer) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src a:b:c + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); - CHECK_LOCATION(result->debugData->location, "", -1, -1); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 8387_error); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_bad_integer) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 111111111111111111111:222222222222222222222:333333333333333333333 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 6367_error); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); } BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match) @@ -542,12 +599,54 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match) )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); // Ensure the latest @src per documentation-comment is used (0:30:40). - CHECK_LOCATION(varDecl.debugData->location, "source0", 30, 40); + CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 30, 40); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_no_whitespace) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222@src 1:333:444 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 8387_error); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_separated_with_single_space) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 @src 1:333:444 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source1", 333, 444); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_leading_trailing_whitespace) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = "/// @src 0:111:222 \n{}"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222); } BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) @@ -563,12 +662,293 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); shared_ptr result = parse(sourceText, dialect, reporter); - BOOST_REQUIRE(!!result); + BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE(holds_alternative(result->statements.at(0))); VariableDeclaration const& varDecl = get(result->statements.at(0)); // -1 points to original source code, which in this case is `"source0"` (which is also - CHECK_LOCATION(varDecl.debugData->location, "", 10, 20); + CHECK_LOCATION(varDecl.debugData->originLocation, "", 10, 20); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"~~~( + { + /// @src 0:149:156 "new C(\"123\")" + let x := 123 + + let y := /** @src 1:96:165 "contract D {..." */ 128 + } + )~~~"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + BOOST_REQUIRE_EQUAL(result->statements.size(), 2); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = get(result->statements.at(0)); + CHECK_LOCATION(varX.debugData->originLocation, "source0", 149, 156); + + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + VariableDeclaration const& varY = get(result->statements.at(1)); + BOOST_REQUIRE(!!varY.value); + BOOST_REQUIRE(holds_alternative(*varY.value)); + Literal const& literal128 = get(*varY.value); + CHECK_LOCATION(literal128.debugData->originLocation, "source1", 96, 165); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_empty_snippet) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 "" + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_before_snippet) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222"abc" def + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 8387_error); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_after_snippet) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 "abc"def + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_no_whitespace) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 "abc"@src 1:333:444 "abc" + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + CHECK_LOCATION(result->debugData->originLocation, "source1", 333, 444); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_unterminated_quote) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src 0:111:222 " abc @src 1:333:444 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 1544_error); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_with_nested_locations) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"~~~( + { + /// @src 0:149:156 "new C(\"123\") /// @src 1:3:4 " + let x := 123 + + let y := /** @src 1:96:165 "function f() internal { \"\/** @src 0:6:7 *\/\"; }" */ 128 + } + )~~~"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + BOOST_REQUIRE_EQUAL(result->statements.size(), 2); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = get(result->statements.at(0)); + CHECK_LOCATION(varX.debugData->originLocation, "source0", 149, 156); + + BOOST_REQUIRE(holds_alternative(result->statements.at(1))); + VariableDeclaration const& varY = get(result->statements.at(1)); + BOOST_REQUIRE(!!varY.value); + BOOST_REQUIRE(holds_alternative(*varY.value)); + Literal const& literal128 = get(*varY.value); + CHECK_LOCATION(literal128.debugData->originLocation, "source1", 96, 165); +} + +BOOST_AUTO_TEST_CASE(astid) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src -1:-1:-1 @ast-id 7 + { + /** @ast-id 2 */ + function f(x) -> y {} + mstore(1, 2) + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_CHECK(result->debugData->astID == int64_t(7)); + auto const& funDef = get(result->statements.at(0)); + BOOST_CHECK(funDef.debugData->astID == int64_t(2)); + BOOST_CHECK(funDef.parameters.at(0).debugData->astID == nullopt); + BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == nullopt); +} + +BOOST_AUTO_TEST_CASE(astid_reset) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src -1:-1:-1 @ast-id 7 @src 1:1:1 + { + /** @ast-id 2 */ + function f(x) -> y {} + mstore(1, 2) + } + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_CHECK(result->debugData->astID == int64_t(7)); + auto const& funDef = get(result->statements.at(0)); + BOOST_CHECK(funDef.debugData->astID == int64_t(2)); + BOOST_CHECK(funDef.parameters.at(0).debugData->astID == nullopt); + BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == nullopt); +} + +BOOST_AUTO_TEST_CASE(astid_multi) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src -1:-1:-1 @ast-id 7 @src 1:1:1 @ast-id 8 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_CHECK(result->debugData->astID == int64_t(8)); +} + +BOOST_AUTO_TEST_CASE(astid_invalid) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @src -1:-1:-1 @ast-id abc @src 1:1:1 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 1749_error); + CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); +} + +BOOST_AUTO_TEST_CASE(astid_too_large) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @ast-id 9223372036854775808 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 1749_error); +} + +BOOST_AUTO_TEST_CASE(astid_way_too_large) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @ast-id 999999999999999999999999999999999999999 + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 1749_error); +} + +BOOST_AUTO_TEST_CASE(astid_not_fully_numeric) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = R"( + /// @ast-id 9x + {} + )"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result); + BOOST_REQUIRE(errorList.size() == 1); + BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); + BOOST_TEST(errorList[0]->errorId() == 1749_error); +} + +BOOST_AUTO_TEST_CASE(customSourceLocations_multiple_src_tags_on_one_line) +{ + ErrorList errorList; + ErrorReporter reporter(errorList); + auto const sourceText = + "{\n" + " /// " + R"~~(@src 1:2:3 ""@src 1:2:4 @src-1:2:5@src 1:2:6 @src 1:2:7 "" @src 1:2:8)~~" + R"~~( X "@src 0:10:20 "new C(\"123\") /// @src 1:4:5 "" XYZ)~~" + R"~~( @src0:20:30 "abc"@src0:2:4 @src-0:2:5@)~~" + R"~~( @some text with random @ signs @@@ @- @** 1:6:7 "src 1:8:9")~~" + "\n" + " let x := 123\n" + "}\n"; + EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); + shared_ptr result = parse(sourceText, dialect, reporter); + BOOST_REQUIRE(!!result && errorList.size() == 0); + BOOST_REQUIRE_EQUAL(result->statements.size(), 1); + + BOOST_REQUIRE(holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = get(result->statements.at(0)); + CHECK_LOCATION(varX.debugData->originLocation, "source1", 4, 5); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libyul/StackLayoutGeneratorTest.cpp b/test/libyul/StackLayoutGeneratorTest.cpp new file mode 100644 index 000000000..70fda680e --- /dev/null +++ b/test/libyul/StackLayoutGeneratorTest.cpp @@ -0,0 +1,267 @@ +/* + 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 + +#ifdef ISOLTEST +#include +#endif + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; +using namespace solidity::yul::test; +using namespace solidity::frontend; +using namespace solidity::frontend::test; +using namespace std; + +StackLayoutGeneratorTest::StackLayoutGeneratorTest(string const& _filename): + TestCase(_filename) +{ + m_source = m_reader.source(); + auto dialectName = m_reader.stringSetting("dialect", "evm"); + m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); + m_expectation = m_reader.simpleExpectations(); +} + +namespace +{ +static std::string variableSlotToString(VariableSlot const& _slot) +{ + return _slot.variable.get().name.str(); +} +} + +class StackLayoutPrinter +{ +public: + StackLayoutPrinter(std::ostream& _stream, StackLayout const& _stackLayout): + m_stream(_stream), m_stackLayout(_stackLayout) + { + } + void operator()(CFG::BasicBlock const& _block, bool _isMainEntry = true) + { + if (_isMainEntry) + { + m_stream << "Entry [label=\"Entry\"];\n"; + m_stream << "Entry -> Block" << getBlockId(_block) << ";\n"; + } + while (!m_blocksToPrint.empty()) + { + CFG::BasicBlock const* block = *m_blocksToPrint.begin(); + m_blocksToPrint.erase(m_blocksToPrint.begin()); + printBlock(*block); + } + + } + void operator()( + CFG::FunctionInfo const& _info + ) + { + m_stream << "FunctionEntry_" << _info.function.name.str() << " [label=\""; + m_stream << "function " << _info.function.name.str() << "("; + m_stream << joinHumanReadable(_info.parameters | ranges::views::transform(variableSlotToString)); + m_stream << ")"; + if (!_info.returnVariables.empty()) + { + m_stream << " -> "; + m_stream << joinHumanReadable(_info.returnVariables | ranges::views::transform(variableSlotToString)); + } + m_stream << "\\l\\\n"; + Stack functionEntryStack = {FunctionReturnLabelSlot{_info.function}}; + functionEntryStack += _info.parameters | ranges::views::reverse; + m_stream << stackToString(functionEntryStack) << "\"];\n"; + m_stream << "FunctionEntry_" << _info.function.name.str() << " -> Block" << getBlockId(*_info.entry) << ";\n"; + (*this)(*_info.entry, false); + } + +private: + void printBlock(CFG::BasicBlock const& _block) + { + m_stream << "Block" << getBlockId(_block) << " [label=\"\\\n"; + + // Verify that the entries of this block exit into this block. + for (auto const& entry: _block.entries) + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::Jump const& _jump) + { + soltestAssert(_jump.target == &_block, "Invalid control flow graph."); + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + soltestAssert( + _conditionalJump.zero == &_block || _conditionalJump.nonZero == &_block, + "Invalid control flow graph." + ); + }, + [&](auto const&) + { + soltestAssert(false, "Invalid control flow graph."); + } + }, entry->exit); + + auto const& blockInfo = m_stackLayout.blockInfos.at(&_block); + m_stream << stackToString(blockInfo.entryLayout) << "\\l\\\n"; + for (auto const& operation: _block.operations) + { + auto entryLayout = m_stackLayout.operationEntryLayout.at(&operation); + m_stream << stackToString(m_stackLayout.operationEntryLayout.at(&operation)) << "\\l\\\n"; + std::visit(util::GenericVisitor{ + [&](CFG::FunctionCall const& _call) { + m_stream << _call.function.get().name.str(); + }, + [&](CFG::BuiltinCall const& _call) { + m_stream << _call.functionCall.get().functionName.name.str(); + + }, + [&](CFG::Assignment const& _assignment) { + m_stream << "Assignment("; + m_stream << joinHumanReadable(_assignment.variables | ranges::views::transform(variableSlotToString)); + m_stream << ")"; + } + }, operation.operation); + m_stream << "\\l\\\n"; + soltestAssert(operation.input.size() <= entryLayout.size(), "Invalid Stack Layout."); + for (size_t i = 0; i < operation.input.size(); ++i) + entryLayout.pop_back(); + entryLayout += operation.output; + m_stream << stackToString(entryLayout) << "\\l\\\n"; + } + m_stream << stackToString(blockInfo.exitLayout) << "\\l\\\n"; + m_stream << "\"];\n"; + std::visit(util::GenericVisitor{ + [&](CFG::BasicBlock::MainExit const&) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"MainExit\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + }, + [&](CFG::BasicBlock::Jump const& _jump) + { + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit [arrowhead=none];\n"; + m_stream << "Block" << getBlockId(_block) << "Exit [label=\""; + if (_jump.backwards) + m_stream << "Backwards"; + m_stream << "Jump\" shape=oval];\n"; + m_stream << "Block" << getBlockId(_block) << "Exit -> Block" << getBlockId(*_jump.target) << ";\n"; + }, + [&](CFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"{ "; + m_stream << stackSlotToString(_conditionalJump.condition); + m_stream << "| { <0> Zero | <1> NonZero }}\" shape=Mrecord];\n"; + m_stream << "Block" << getBlockId(_block); + m_stream << "Exit:0 -> Block" << getBlockId(*_conditionalJump.zero) << ";\n"; + m_stream << "Block" << getBlockId(_block); + m_stream << "Exit:1 -> Block" << getBlockId(*_conditionalJump.nonZero) << ";\n"; + }, + [&](CFG::BasicBlock::FunctionReturn const& _return) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"FunctionReturn[" << _return.info->function.name.str() << "]\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + }, + [&](CFG::BasicBlock::Terminated const&) + { + m_stream << "Block" << getBlockId(_block) << "Exit [label=\"Terminated\"];\n"; + m_stream << "Block" << getBlockId(_block) << " -> Block" << getBlockId(_block) << "Exit;\n"; + } + }, _block.exit); + m_stream << "\n"; + } + size_t getBlockId(CFG::BasicBlock const& _block) + { + if (size_t* id = util::valueOrNullptr(m_blockIds, &_block)) + return *id; + size_t id = m_blockIds[&_block] = m_blockCount++; + m_blocksToPrint.emplace_back(&_block); + return id; + } + std::ostream& m_stream; + StackLayout const& m_stackLayout; + std::map m_blockIds; + size_t m_blockCount = 0; + std::list m_blocksToPrint; +}; + +TestCase::TestResult StackLayoutGeneratorTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + ErrorList errors; + auto [object, analysisInfo] = parse(m_source, *m_dialect, errors); + if (!object || !analysisInfo || Error::containsErrors(errors)) + { + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + return TestResult::FatalError; + } + + std::ostringstream output; + + std::unique_ptr cfg = ControlFlowGraphBuilder::build(*analysisInfo, *m_dialect, *object->code); + StackLayout stackLayout = StackLayoutGenerator::run(*cfg); + + output << "digraph CFG {\nnodesep=0.7;\nnode[shape=box];\n\n"; + StackLayoutPrinter printer{output, stackLayout}; + printer(*cfg->entry); + for (auto function: cfg->functions) + printer(cfg->functionInfo.at(function)); + output << "}\n"; + + m_obtainedResult = output.str(); + + auto result = checkResult(_stream, _linePrefix, _formatted); + +#ifdef ISOLTEST + char* graphDisplayer = nullptr; + if (result == TestResult::Failure) + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_FAILURE"); + else if (result == TestResult::Success) + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_SUCCESS"); + + if (graphDisplayer) + { + if (result == TestResult::Success) + std::cout << std::endl << m_source << std::endl; + boost::process::opstream pipe; + boost::process::child child(graphDisplayer, boost::process::std_in < pipe); + + pipe << output.str(); + pipe.flush(); + pipe.pipe().close(); + if (result == TestResult::Success) + child.wait(); + else + child.detach(); + } +#endif + + return result; +} diff --git a/test/libyul/StackLayoutGeneratorTest.h b/test/libyul/StackLayoutGeneratorTest.h new file mode 100644 index 000000000..9f570045f --- /dev/null +++ b/test/libyul/StackLayoutGeneratorTest.h @@ -0,0 +1,43 @@ +/* + 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 +{ +struct Dialect; + +namespace test +{ + +class StackLayoutGeneratorTest: public solidity::frontend::test::TestCase +{ +public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename); + } + explicit StackLayoutGeneratorTest(std::string const& _filename); + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; +private: + Dialect const* m_dialect = nullptr; +}; +} +} diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index ada942152..6404fa7c6 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -114,7 +114,7 @@ std::pair, std::shared_ptr> YulOptimize shared_ptr object; shared_ptr analysisInfo; std::tie(object, analysisInfo) = yul::test::parse(_source, *m_dialect, errors); - if (!object || !analysisInfo || !Error::containsOnlyWarnings(errors)) + if (!object || !analysisInfo || Error::containsErrors(errors)) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; CharStream charStream(_source, ""); diff --git a/test/libyul/objectCompiler/function_series.yul b/test/libyul/objectCompiler/function_series.yul index cf4911997..f905c04b5 100644 --- a/test/libyul/objectCompiler/function_series.yul +++ b/test/libyul/objectCompiler/function_series.yul @@ -11,15 +11,15 @@ object "Contract" { // ---- // Assembly: // /* "source":33:48 */ -// jump(tag_1) -// tag_2: -// tag_3: +// jump(tag_3) +// tag_1: +// tag_4: // jump // out // /* "source":53:68 */ -// tag_4: +// tag_2: // tag_5: // jump // out -// tag_1: +// tag_3: // /* "source":83:84 */ // 0x01 // /* "source":80:81 */ diff --git a/test/libyul/objectCompiler/jump_tags.yul b/test/libyul/objectCompiler/jump_tags.yul index b78173d18..ba0984b10 100644 --- a/test/libyul/objectCompiler/jump_tags.yul +++ b/test/libyul/objectCompiler/jump_tags.yul @@ -11,21 +11,21 @@ object "Contract" { // ---- // Assembly: // /* "source":33:54 */ -// jump(tag_1) -// tag_2: +// jump(tag_3) +// tag_1: // /* "source":48:52 */ -// tag_4 +// tag_5 // /* "source":50:51 */ // 0x01 // /* "source":48:52 */ -// tag_5 +// tag_2 // jump // in -// tag_4: +// tag_5: // /* "source":33:54 */ -// tag_3: +// tag_4: // jump // out // /* "source":59:104 */ -// tag_5: +// tag_2: // /* "source":78:79 */ // dup1 // /* "source":75:89 */ @@ -46,20 +46,20 @@ object "Contract" { // /* "source":92:101 */ // add // /* "source":90:102 */ -// tag_5 +// tag_2 // jump // in // tag_8: // /* "source":59:104 */ // pop // tag_6: // jump // out -// tag_1: +// tag_3: // /* "source":109:113 */ // tag_9 // /* "source":111:112 */ // 0x01 // /* "source":109:113 */ -// tag_5 +// tag_2 // jump // in // tag_9: // Bytecode: 6026565b600b6001600e565b5b565b8015601857506024565b602260028201600e565b505b565b602e6001600e565b diff --git a/test/libyul/objectCompiler/sourceLocations.yul b/test/libyul/objectCompiler/sourceLocations.yul index f98f74f77..4e75d05e7 100644 --- a/test/libyul/objectCompiler/sourceLocations.yul +++ b/test/libyul/objectCompiler/sourceLocations.yul @@ -10,6 +10,7 @@ object "a" { datasize("sub") ) } + /// @use-src 3: "abc.sol" , 2: "def.sol" object "sub" { code { /// @src 2:70:72 diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 68640c7ca..ae1ca7848 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -284,15 +284,17 @@ // let _5 := 0x40 // calldatacopy(0xe0, add(_3, 164), _5) // calldatacopy(0x20, add(_3, 100), _5) -// mstore(0x120, sub(_2, c)) -// mstore(0x60, k) +// let _6 := 0x120 +// mstore(_6, sub(_2, c)) +// let _7 := 0x60 +// mstore(_7, k) // mstore(0xc0, a) -// let result := call(gas(), 7, 0, 0xe0, 0x60, 0x1a0, _5) -// let result_1 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x120, _5)) -// let _6 := 0x160 -// let result_2 := and(result_1, call(gas(), 7, 0, 0x80, 0x60, _6, _5)) -// let result_3 := and(result_2, call(gas(), 6, 0, 0x120, 0x80, _6, _5)) -// result := and(result_3, call(gas(), 6, 0, _6, 0x80, b, _5)) +// let result := call(gas(), 7, 0, 0xe0, _7, 0x1a0, _5) +// let result_1 := and(result, call(gas(), 7, 0, 0x20, _7, _6, _5)) +// let _8 := 0x160 +// let result_2 := and(result_1, call(gas(), 7, 0, 0x80, _7, _8, _5)) +// let result_3 := and(result_2, call(gas(), 6, 0, _6, 0x80, _8, _5)) +// result := and(result_3, call(gas(), 6, 0, _8, 0x80, b, _5)) // if eq(i, m) // { // mstore(0x260, mload(0x20)) @@ -302,8 +304,8 @@ // } // if gt(i, m) // { -// mstore(0x60, c) -// let result_4 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x220, _5)) +// mstore(_7, c) +// let result_4 := and(result, call(gas(), 7, 0, 0x20, _7, 0x220, _5)) // let result_5 := and(result_4, call(gas(), 6, 0, 0x220, 0x80, 0x260, _5)) // result := and(result_5, call(gas(), 6, 0, 0x1a0, 0x80, 0x1e0, _5)) // } diff --git a/test/libyul/yulOptimizerTests/loadResolver/keccak_crash.yul b/test/libyul/yulOptimizerTests/loadResolver/keccak_crash.yul new file mode 100644 index 000000000..dd6a164eb --- /dev/null +++ b/test/libyul/yulOptimizerTests/loadResolver/keccak_crash.yul @@ -0,0 +1,13 @@ +// This test used to crash: https://github.com/ethereum/solidity/issues/11801 +{ + for {} addmod(keccak256(0x0,create(0x0, 0x0, 0x0)), 0x0, 0x0) {} {} +} +// ---- +// step: loadResolver +// +// { +// for { } +// addmod(keccak256(0x0, create(0x0, 0x0, 0x0)), 0x0, 0x0) +// { } +// { } +// } diff --git a/test/libyul/yulStackLayout/complex.yul b/test/libyul/yulStackLayout/complex.yul new file mode 100644 index 000000000..e521e6faa --- /dev/null +++ b/test/libyul/yulStackLayout/complex.yul @@ -0,0 +1,318 @@ +{ + function f(a, b) -> c { + for { let x := 42 } lt(x, a) { + x := add(x, 1) + if calldataload(x) + { + sstore(0, x) + c := 0x21 + leave + sstore(0x01, 0x0101) + } + sstore(0xFF, 0xFFFF) + } + { + switch mload(x) + case 0 { + sstore(a, b) + break + sstore(a, b) + } + case 1 { + sstore(0x04, x) + leave + sstore(a, 0x0505) + } + case 2 { + sstore(x, 0x06) + c := 42 + revert(0, 0) + sstore(0x07, 0x0707) + } + case 3 { + sstore(0x08, 0x0808) + } + default { + if mload(b) { + return(0, 0) + sstore(0x09, 0x0909) + } + sstore(0x0A, 0x0A0A) + } + sstore(0x0B, 0x0B0B) + } + sstore(0x0C, 0x0C0C) + if sload(0x0D) { + c := 0x424242 + } + } + pop(f(1,2)) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// [ ]\l\ +// [ RET[f] 0x02 0x01 ]\l\ +// f\l\ +// [ TMP[f, 0] ]\l\ +// [ TMP[f, 0] ]\l\ +// pop\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f [label="function f(a, b) -> c\l\ +// [ RET b a ]"]; +// FunctionEntry_f -> Block1; +// Block1 [label="\ +// [ c RET a b ]\l\ +// [ c RET a b 0x2a ]\l\ +// Assignment(x)\l\ +// [ c RET a b x ]\l\ +// [ c RET a b x ]\l\ +// "]; +// Block1 -> Block1Exit [arrowhead=none]; +// Block1Exit [label="Jump" shape=oval]; +// Block1Exit -> Block2; +// +// Block2 [label="\ +// [ c RET a b x ]\l\ +// [ c RET a b x a x ]\l\ +// lt\l\ +// [ c RET a b x TMP[lt, 0] ]\l\ +// [ c RET a b x TMP[lt, 0] ]\l\ +// "]; +// Block2 -> Block2Exit; +// Block2Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block2Exit:0 -> Block3; +// Block2Exit:1 -> Block4; +// +// Block3 [label="\ +// [ c RET JUNK JUNK JUNK ]\l\ +// [ c RET 0x0c0c 0x0c ]\l\ +// sstore\l\ +// [ c RET ]\l\ +// [ c RET 0x0d ]\l\ +// sload\l\ +// [ c RET TMP[sload, 0] ]\l\ +// [ c RET TMP[sload, 0] ]\l\ +// "]; +// Block3 -> Block3Exit; +// Block3Exit [label="{ TMP[sload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block3Exit:0 -> Block5; +// Block3Exit:1 -> Block6; +// +// Block4 [label="\ +// [ c RET a b x ]\l\ +// [ c RET a b x x ]\l\ +// mload\l\ +// [ c RET a b x TMP[mload, 0] ]\l\ +// [ c RET a b x TMP[mload, 0] ]\l\ +// Assignment(GHOST[0])\l\ +// [ c RET a b x GHOST[0] ]\l\ +// [ c RET a b x GHOST[0] GHOST[0] 0x00 ]\l\ +// eq\l\ +// [ c RET a b x GHOST[0] TMP[eq, 0] ]\l\ +// [ c RET a b x GHOST[0] TMP[eq, 0] ]\l\ +// "]; +// Block4 -> Block4Exit; +// Block4Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block4Exit:0 -> Block7; +// Block4Exit:1 -> Block8; +// +// Block5 [label="\ +// [ c RET ]\l\ +// [ c RET ]\l\ +// "]; +// Block5Exit [label="FunctionReturn[f]"]; +// Block5 -> Block5Exit; +// +// Block6 [label="\ +// [ JUNK RET ]\l\ +// [ RET 0x424242 ]\l\ +// Assignment(c)\l\ +// [ RET c ]\l\ +// [ c RET ]\l\ +// "]; +// Block6 -> Block6Exit [arrowhead=none]; +// Block6Exit [label="Jump" shape=oval]; +// Block6Exit -> Block5; +// +// Block7 [label="\ +// [ c RET a b x GHOST[0] ]\l\ +// [ c RET a b x GHOST[0] GHOST[0] 0x01 ]\l\ +// eq\l\ +// [ c RET a b x GHOST[0] TMP[eq, 0] ]\l\ +// [ c RET a b x GHOST[0] TMP[eq, 0] ]\l\ +// "]; +// Block7 -> Block7Exit; +// Block7Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block7Exit:0 -> Block9; +// Block7Exit:1 -> Block10; +// +// Block8 [label="\ +// [ c RET a b JUNK JUNK ]\l\ +// [ c RET b a ]\l\ +// sstore\l\ +// [ c RET ]\l\ +// [ c RET ]\l\ +// "]; +// Block8 -> Block8Exit [arrowhead=none]; +// Block8Exit [label="Jump" shape=oval]; +// Block8Exit -> Block3; +// +// Block9 [label="\ +// [ c RET a b x GHOST[0] ]\l\ +// [ c RET a b x GHOST[0] GHOST[0] 0x02 ]\l\ +// eq\l\ +// [ c RET a b x GHOST[0] TMP[eq, 0] ]\l\ +// [ c RET a b x GHOST[0] TMP[eq, 0] ]\l\ +// "]; +// Block9 -> Block9Exit; +// Block9Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block9Exit:0 -> Block11; +// Block9Exit:1 -> Block12; +// +// Block10 [label="\ +// [ c RET JUNK JUNK x JUNK ]\l\ +// [ c RET x 0x04 ]\l\ +// sstore\l\ +// [ c RET ]\l\ +// [ c RET ]\l\ +// "]; +// Block10Exit [label="FunctionReturn[f]"]; +// Block10 -> Block10Exit; +// +// Block11 [label="\ +// [ c RET a b x GHOST[0] ]\l\ +// [ c RET a b x GHOST[0] 0x03 ]\l\ +// eq\l\ +// [ c RET a b x TMP[eq, 0] ]\l\ +// [ c RET a b x TMP[eq, 0] ]\l\ +// "]; +// Block11 -> Block11Exit; +// Block11Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block11Exit:0 -> Block13; +// Block11Exit:1 -> Block14; +// +// Block12 [label="\ +// [ JUNK JUNK JUNK JUNK x JUNK ]\l\ +// [ 0x06 x ]\l\ +// sstore\l\ +// [ ]\l\ +// [ 0x2a ]\l\ +// Assignment(c)\l\ +// [ c ]\l\ +// [ 0x00 0x00 ]\l\ +// revert\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block12Exit [label="Terminated"]; +// Block12 -> Block12Exit; +// +// Block13 [label="\ +// [ c RET a b x ]\l\ +// [ c RET a b x b ]\l\ +// mload\l\ +// [ c RET a b x TMP[mload, 0] ]\l\ +// [ c RET a b x TMP[mload, 0] ]\l\ +// "]; +// Block13 -> Block13Exit; +// Block13Exit [label="{ TMP[mload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block13Exit:0 -> Block15; +// Block13Exit:1 -> Block16; +// +// Block14 [label="\ +// [ c RET a b x ]\l\ +// [ c RET a b 0x01 x 0x0808 0x08 ]\l\ +// sstore\l\ +// [ c RET a b 0x01 x ]\l\ +// [ c RET a b 0x01 x ]\l\ +// "]; +// Block14 -> Block14Exit [arrowhead=none]; +// Block14Exit [label="Jump" shape=oval]; +// Block14Exit -> Block17; +// +// Block15 [label="\ +// [ c RET a b x ]\l\ +// [ c RET a b 0x01 x 0x0a0a 0x0a ]\l\ +// sstore\l\ +// [ c RET a b 0x01 x ]\l\ +// [ c RET a b 0x01 x ]\l\ +// "]; +// Block15 -> Block15Exit [arrowhead=none]; +// Block15Exit [label="Jump" shape=oval]; +// Block15Exit -> Block17; +// +// Block16 [label="\ +// [ JUNK JUNK JUNK JUNK JUNK ]\l\ +// [ 0x00 0x00 ]\l\ +// return\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block16Exit [label="Terminated"]; +// Block16 -> Block16Exit; +// +// Block17 [label="\ +// [ c RET a b 0x01 x ]\l\ +// [ c RET a b 0x01 x 0x0b0b 0x0b ]\l\ +// sstore\l\ +// [ c RET a b 0x01 x ]\l\ +// [ c RET a b 0x01 x ]\l\ +// "]; +// Block17 -> Block17Exit [arrowhead=none]; +// Block17Exit [label="Jump" shape=oval]; +// Block17Exit -> Block18; +// +// Block18 [label="\ +// [ c RET a b 0x01 x ]\l\ +// [ c RET a b 0x01 x ]\l\ +// add\l\ +// [ c RET a b TMP[add, 0] ]\l\ +// [ c RET a b TMP[add, 0] ]\l\ +// Assignment(x)\l\ +// [ c RET a b x ]\l\ +// [ c RET x b a x ]\l\ +// calldataload\l\ +// [ c RET x b a TMP[calldataload, 0] ]\l\ +// [ c RET x b a TMP[calldataload, 0] ]\l\ +// "]; +// Block18 -> Block18Exit; +// Block18Exit [label="{ TMP[calldataload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block18Exit:0 -> Block19; +// Block18Exit:1 -> Block20; +// +// Block19 [label="\ +// [ c RET x b a ]\l\ +// [ c RET x b a 0xffff 0xff ]\l\ +// sstore\l\ +// [ c RET x b a ]\l\ +// [ c RET x b a ]\l\ +// "]; +// Block19 -> Block19Exit [arrowhead=none]; +// Block19Exit [label="BackwardsJump" shape=oval]; +// Block19Exit -> Block2; +// +// Block20 [label="\ +// [ JUNK RET x JUNK JUNK ]\l\ +// [ RET x 0x00 ]\l\ +// sstore\l\ +// [ RET ]\l\ +// [ RET 0x21 ]\l\ +// Assignment(c)\l\ +// [ RET c ]\l\ +// [ c RET ]\l\ +// "]; +// Block20Exit [label="FunctionReturn[f]"]; +// Block20 -> Block20Exit; +// +// } diff --git a/test/libyul/yulStackLayout/for.yul b/test/libyul/yulStackLayout/for.yul new file mode 100644 index 000000000..fc0fbcdb7 --- /dev/null +++ b/test/libyul/yulStackLayout/for.yul @@ -0,0 +1,91 @@ +{ + let x := 0x01 + let y := 0x02 + sstore(0x01, x) + for { sstore(0x02, 0x0202) } lt(x, 0x0303) { x := add(x,0x0404) } { + sstore(0x05, 0x0505) + y := sload(x) + } + sstore(0x06, 0x0506) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// [ ]\l\ +// [ 0x01 ]\l\ +// Assignment(x)\l\ +// [ x ]\l\ +// [ x 0x02 ]\l\ +// Assignment(y)\l\ +// [ x y ]\l\ +// [ x x 0x01 ]\l\ +// sstore\l\ +// [ x ]\l\ +// [ x 0x0202 0x02 ]\l\ +// sstore\l\ +// [ x ]\l\ +// [ x ]\l\ +// "]; +// Block0 -> Block0Exit [arrowhead=none]; +// Block0Exit [label="Jump" shape=oval]; +// Block0Exit -> Block1; +// +// Block1 [label="\ +// [ x ]\l\ +// [ x 0x0303 x ]\l\ +// lt\l\ +// [ x TMP[lt, 0] ]\l\ +// [ x TMP[lt, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[lt, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block2; +// Block1Exit:1 -> Block3; +// +// Block2 [label="\ +// [ JUNK ]\l\ +// [ 0x0506 0x06 ]\l\ +// sstore\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block2Exit [label="MainExit"]; +// Block2 -> Block2Exit; +// +// Block3 [label="\ +// [ x ]\l\ +// [ 0x0404 x 0x0505 0x05 ]\l\ +// sstore\l\ +// [ 0x0404 x ]\l\ +// [ 0x0404 x x ]\l\ +// sload\l\ +// [ 0x0404 x TMP[sload, 0] ]\l\ +// [ 0x0404 x TMP[sload, 0] ]\l\ +// Assignment(y)\l\ +// [ 0x0404 x y ]\l\ +// [ 0x0404 x ]\l\ +// "]; +// Block3 -> Block3Exit [arrowhead=none]; +// Block3Exit [label="Jump" shape=oval]; +// Block3Exit -> Block4; +// +// Block4 [label="\ +// [ 0x0404 x ]\l\ +// [ 0x0404 x ]\l\ +// add\l\ +// [ TMP[add, 0] ]\l\ +// [ TMP[add, 0] ]\l\ +// Assignment(x)\l\ +// [ x ]\l\ +// [ x ]\l\ +// "]; +// Block4 -> Block4Exit [arrowhead=none]; +// Block4Exit [label="BackwardsJump" shape=oval]; +// Block4Exit -> Block1; +// +// } diff --git a/test/libyul/yulStackLayout/function.yul b/test/libyul/yulStackLayout/function.yul new file mode 100644 index 000000000..343555123 --- /dev/null +++ b/test/libyul/yulStackLayout/function.yul @@ -0,0 +1,117 @@ +{ + function f(a, b) -> r { + let x := add(a,b) + r := sub(x,a) + } + function g() { + sstore(0x01, 0x0101) + } + function h(x) { + h(f(x, 0)) + g() + } + function i() -> v, w { + v := 0x0202 + w := 0x0303 + } + let x, y := i() + h(x) + h(y) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// [ ]\l\ +// [ RET[h] RET[h] RET[i] ]\l\ +// i\l\ +// [ RET[h] RET[h] TMP[i, 0] TMP[i, 1] ]\l\ +// [ RET[h] RET[h] TMP[i, 0] TMP[i, 1] ]\l\ +// Assignment(x, y)\l\ +// [ RET[h] RET[h] x y ]\l\ +// [ RET[h] y RET[h] x ]\l\ +// h\l\ +// [ RET[h] y ]\l\ +// [ RET[h] y ]\l\ +// h\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// FunctionEntry_f [label="function f(a, b) -> r\l\ +// [ RET b a ]"]; +// FunctionEntry_f -> Block1; +// Block1 [label="\ +// [ RET a b ]\l\ +// [ RET a b a ]\l\ +// add\l\ +// [ RET a TMP[add, 0] ]\l\ +// [ RET a TMP[add, 0] ]\l\ +// Assignment(x)\l\ +// [ RET a x ]\l\ +// [ RET a x ]\l\ +// sub\l\ +// [ RET TMP[sub, 0] ]\l\ +// [ RET TMP[sub, 0] ]\l\ +// Assignment(r)\l\ +// [ RET r ]\l\ +// [ r RET ]\l\ +// "]; +// Block1Exit [label="FunctionReturn[f]"]; +// Block1 -> Block1Exit; +// +// FunctionEntry_g [label="function g()\l\ +// [ RET ]"]; +// FunctionEntry_g -> Block2; +// Block2 [label="\ +// [ RET ]\l\ +// [ RET 0x0101 0x01 ]\l\ +// sstore\l\ +// [ RET ]\l\ +// [ RET ]\l\ +// "]; +// Block2Exit [label="FunctionReturn[g]"]; +// Block2 -> Block2Exit; +// +// FunctionEntry_h [label="function h(x)\l\ +// [ RET x ]"]; +// FunctionEntry_h -> Block3; +// Block3 [label="\ +// [ RET RET[h] RET[f] 0x00 x ]\l\ +// [ RET RET[h] RET[f] 0x00 x ]\l\ +// f\l\ +// [ RET RET[h] TMP[f, 0] ]\l\ +// [ RET RET[h] TMP[f, 0] ]\l\ +// h\l\ +// [ RET ]\l\ +// [ RET RET[g] ]\l\ +// g\l\ +// [ RET ]\l\ +// [ RET ]\l\ +// "]; +// Block3Exit [label="FunctionReturn[h]"]; +// Block3 -> Block3Exit; +// +// FunctionEntry_i [label="function i() -> v, w\l\ +// [ RET ]"]; +// FunctionEntry_i -> Block4; +// Block4 [label="\ +// [ RET ]\l\ +// [ RET 0x0202 ]\l\ +// Assignment(v)\l\ +// [ RET v ]\l\ +// [ v RET 0x0303 ]\l\ +// Assignment(w)\l\ +// [ v RET w ]\l\ +// [ v w RET ]\l\ +// "]; +// Block4Exit [label="FunctionReturn[i]"]; +// Block4 -> Block4Exit; +// +// } diff --git a/test/libyul/yulStackLayout/if.yul b/test/libyul/yulStackLayout/if.yul new file mode 100644 index 000000000..a019788b3 --- /dev/null +++ b/test/libyul/yulStackLayout/if.yul @@ -0,0 +1,51 @@ +{ + sstore(0x01, 0x0101) + if calldataload(0) { + sstore(0x02, 0x0202) + } + sstore(0x03, 0x003) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// [ ]\l\ +// [ 0x0101 0x01 ]\l\ +// sstore\l\ +// [ ]\l\ +// [ 0x00 ]\l\ +// calldataload\l\ +// [ TMP[calldataload, 0] ]\l\ +// [ TMP[calldataload, 0] ]\l\ +// "]; +// Block0 -> Block0Exit; +// Block0Exit [label="{ TMP[calldataload, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0Exit:0 -> Block1; +// Block0Exit:1 -> Block2; +// +// Block1 [label="\ +// [ ]\l\ +// [ 0x03 0x03 ]\l\ +// sstore\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block1Exit [label="MainExit"]; +// Block1 -> Block1Exit; +// +// Block2 [label="\ +// [ ]\l\ +// [ 0x0202 0x02 ]\l\ +// sstore\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block2 -> Block2Exit [arrowhead=none]; +// Block2Exit [label="Jump" shape=oval]; +// Block2Exit -> Block1; +// +// } diff --git a/test/libyul/yulStackLayout/stub.yul b/test/libyul/yulStackLayout/stub.yul new file mode 100644 index 000000000..5a16304aa --- /dev/null +++ b/test/libyul/yulStackLayout/stub.yul @@ -0,0 +1,17 @@ +{ +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/libyul/yulStackLayout/switch.yul b/test/libyul/yulStackLayout/switch.yul new file mode 100644 index 000000000..5f655731b --- /dev/null +++ b/test/libyul/yulStackLayout/switch.yul @@ -0,0 +1,107 @@ +{ + let x := 0x0101 + let y := 0x0202 + let z := 0x0303 + switch sload(x) + case 0 { + x := 0x42 + } + case 1 { + y := 0x42 + } + default { + sstore(z, z) + } + + sstore(0x0404, y) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// [ ]\l\ +// [ 0x0101 ]\l\ +// Assignment(x)\l\ +// [ x ]\l\ +// [ x 0x0202 ]\l\ +// Assignment(y)\l\ +// [ x y ]\l\ +// [ y x 0x0303 ]\l\ +// Assignment(z)\l\ +// [ y x z ]\l\ +// [ y z x ]\l\ +// sload\l\ +// [ y z TMP[sload, 0] ]\l\ +// [ y z TMP[sload, 0] ]\l\ +// Assignment(GHOST[0])\l\ +// [ y z GHOST[0] ]\l\ +// [ y z GHOST[0] GHOST[0] 0x00 ]\l\ +// eq\l\ +// [ y z GHOST[0] TMP[eq, 0] ]\l\ +// [ y z GHOST[0] TMP[eq, 0] ]\l\ +// "]; +// Block0 -> Block0Exit; +// Block0Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0Exit:0 -> Block1; +// Block0Exit:1 -> Block2; +// +// Block1 [label="\ +// [ y z GHOST[0] ]\l\ +// [ y z GHOST[0] 0x01 ]\l\ +// eq\l\ +// [ y z TMP[eq, 0] ]\l\ +// [ y z TMP[eq, 0] ]\l\ +// "]; +// Block1 -> Block1Exit; +// Block1Exit [label="{ TMP[eq, 0]| { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1Exit:0 -> Block3; +// Block1Exit:1 -> Block4; +// +// Block2 [label="\ +// [ y JUNK JUNK ]\l\ +// [ y 0x42 ]\l\ +// Assignment(x)\l\ +// [ y x ]\l\ +// [ y ]\l\ +// "]; +// Block2 -> Block2Exit [arrowhead=none]; +// Block2Exit [label="Jump" shape=oval]; +// Block2Exit -> Block5; +// +// Block3 [label="\ +// [ y z ]\l\ +// [ y z z ]\l\ +// sstore\l\ +// [ y ]\l\ +// [ y ]\l\ +// "]; +// Block3 -> Block3Exit [arrowhead=none]; +// Block3Exit [label="Jump" shape=oval]; +// Block3Exit -> Block5; +// +// Block4 [label="\ +// [ JUNK JUNK ]\l\ +// [ 0x42 ]\l\ +// Assignment(y)\l\ +// [ y ]\l\ +// [ y ]\l\ +// "]; +// Block4 -> Block4Exit [arrowhead=none]; +// Block4Exit [label="Jump" shape=oval]; +// Block4Exit -> Block5; +// +// Block5 [label="\ +// [ y ]\l\ +// [ y 0x0404 ]\l\ +// sstore\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block5Exit [label="MainExit"]; +// Block5 -> Block5Exit; +// +// } diff --git a/test/libyul/yulStackLayout/variables.yul b/test/libyul/yulStackLayout/variables.yul new file mode 100644 index 000000000..3e66d5686 --- /dev/null +++ b/test/libyul/yulStackLayout/variables.yul @@ -0,0 +1,51 @@ +{ + let x := calldataload(0) + let y := calldataload(2) + + x := calldataload(3) + y := calldataload(4) + + sstore(x,y) +} +// ---- +// digraph CFG { +// nodesep=0.7; +// node[shape=box]; +// +// Entry [label="Entry"]; +// Entry -> Block0; +// Block0 [label="\ +// [ ]\l\ +// [ 0x00 ]\l\ +// calldataload\l\ +// [ TMP[calldataload, 0] ]\l\ +// [ TMP[calldataload, 0] ]\l\ +// Assignment(x)\l\ +// [ x ]\l\ +// [ 0x02 ]\l\ +// calldataload\l\ +// [ TMP[calldataload, 0] ]\l\ +// [ TMP[calldataload, 0] ]\l\ +// Assignment(y)\l\ +// [ y ]\l\ +// [ 0x03 ]\l\ +// calldataload\l\ +// [ TMP[calldataload, 0] ]\l\ +// [ TMP[calldataload, 0] ]\l\ +// Assignment(x)\l\ +// [ x ]\l\ +// [ x 0x04 ]\l\ +// calldataload\l\ +// [ x TMP[calldataload, 0] ]\l\ +// [ x TMP[calldataload, 0] ]\l\ +// Assignment(y)\l\ +// [ x y ]\l\ +// [ y x ]\l\ +// sstore\l\ +// [ ]\l\ +// [ ]\l\ +// "]; +// Block0Exit [label="MainExit"]; +// Block0 -> Block0Exit; +// +// } diff --git a/test/libyul/yulSyntaxTests/invalid/leave_items_on_stack_with_debug_info.yul b/test/libyul/yulSyntaxTests/invalid/leave_items_on_stack_with_debug_info.yul new file mode 100644 index 000000000..ce93dd922 --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/leave_items_on_stack_with_debug_info.yul @@ -0,0 +1,9 @@ +/// @use-src 0:"input.sol" +object "C" { + code { + /// @src 0:0:0 + calldataload(0) + } +} +// ---- +// TypeError 3083: (82-97): 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_with_debug_info.yul b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed_with_debug_info.yul new file mode 100644 index 000000000..79ba4f83d --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid/literals_on_stack_disallowed_with_debug_info.yul @@ -0,0 +1,9 @@ +/// @use-src 0:"input.sol" +object "C" { + code { + 1 + } +} +// ---- +// ParserError 6913: (65-66): Call or assignment expected. +// ParserError 2314: (67-68): Expected end of source but got '}' diff --git a/test/libyul/yulSyntaxTests/invalid_tuple_assignment_with_debug_info.yul b/test/libyul/yulSyntaxTests/invalid_tuple_assignment_with_debug_info.yul new file mode 100644 index 000000000..8ea0302eb --- /dev/null +++ b/test/libyul/yulSyntaxTests/invalid_tuple_assignment_with_debug_info.yul @@ -0,0 +1,8 @@ +/// @use-src 0:"input.sol" +object "C" { + code { + let x, y := 1 + } +} +// ---- +// DeclarationError 3812: (59-72): Variable count mismatch for declaration of "x, y": 2 variables and 1 values. diff --git a/test/libyul/yulSyntaxTests/name_clash_sub_scope_with_debug_info.yul b/test/libyul/yulSyntaxTests/name_clash_sub_scope_with_debug_info.yul new file mode 100644 index 000000000..b6c2ab66c --- /dev/null +++ b/test/libyul/yulSyntaxTests/name_clash_sub_scope_with_debug_info.yul @@ -0,0 +1,11 @@ +/// @use-src 0:"input.sol" +object "C" { + code { + /// @src 0:0:0 + function g() { + function g() {} + } + } +} +// ---- +// DeclarationError 6052: (109-124): Function name g already taken in this scope. diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp index 0023e9f57..3d1fce7d2 100644 --- a/test/solc/CommandLineInterface.cpp +++ b/test/solc/CommandLineInterface.cpp @@ -27,6 +27,8 @@ #include #include +#include + #include #include @@ -151,7 +153,7 @@ BOOST_AUTO_TEST_CASE(cli_input) {"a", "b", "c/d/e/"}, }; map expectedSources = { - {"", "\n"}, + {"", ""}, {(expectedDir1 / "input1.sol").generic_string(), ""}, {(expectedDir2 / "input2.sol").generic_string(), ""}, }; @@ -786,12 +788,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_symlinks) TemporaryWorkingDirectory tempWorkDir(tempDir.path() / "r"); if ( -#if !defined(_WIN32) !createSymlinkIfSupportedByFilesystem("../x/y", tempDir.path() / "r/sym", true) || -#else - // NOTE: On Windows / works as a separator in a symlink target only if the target is absolute - !createSymlinkIfSupportedByFilesystem("..\\x\\y", tempDir.path() / "r/sym", true) || -#endif !createSymlinkIfSupportedByFilesystem("contract.sol", tempDir.path() / "x/y/z/contract_symlink.sol", false) ) return; @@ -857,7 +854,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_base_path_and_stdin) expectedOptions.modelChecker.initialize = true; map expectedSources = { - {"", "\n"}, + {"", ""}, }; FileReader::FileSystemPathSet expectedAllowedDirectories = {}; @@ -873,6 +870,427 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_base_path_and_stdin) BOOST_TEST(result.reader.basePath() == expectedWorkDir / "base"); } +BOOST_AUTO_TEST_CASE(cli_include_paths) +{ + TemporaryDirectory tempDir({"base/", "include/", "lib/nested/"}, TEST_CASE_NAME); + TemporaryWorkingDirectory tempWorkDir(tempDir); + + string const preamble = + "// SPDX-License-Identifier: GPL-3.0\n" + "pragma solidity >=0.0;\n"; + string const mainContractSource = preamble + + "import \"contract.sol\";\n" + "import \"contract_via_callback.sol\";\n" + "import \"include.sol\";\n" + "import \"include_via_callback.sol\";\n" + "import \"nested.sol\";\n" + "import \"nested_via_callback.sol\";\n" + "import \"lib.sol\";\n" + "import \"lib_via_callback.sol\";\n"; + + createFilesWithParentDirs( + { + tempDir.path() / "base/contract.sol", + tempDir.path() / "base/contract_via_callback.sol", + tempDir.path() / "include/include.sol", + tempDir.path() / "include/include_via_callback.sol", + tempDir.path() / "lib/nested/nested.sol", + tempDir.path() / "lib/nested/nested_via_callback.sol", + tempDir.path() / "lib/lib.sol", + tempDir.path() / "lib/lib_via_callback.sol", + }, + preamble + ); + createFilesWithParentDirs({tempDir.path() / "base/main.sol"}, mainContractSource); + + boost::filesystem::path canonicalWorkDir = boost::filesystem::canonical(tempDir); + boost::filesystem::path expectedWorkDir = "/" / canonicalWorkDir.relative_path(); + + vector commandLine = { + "solc", + "--no-color", + "--base-path=base/", + "--include-path=include/", + "--include-path=lib/nested", + "--include-path=lib/", + "base/main.sol", + "base/contract.sol", + "include/include.sol", + "lib/nested/nested.sol", + "lib/lib.sol", + }; + + CommandLineOptions expectedOptions; + expectedOptions.input.paths = { + "base/main.sol", + "base/contract.sol", + "include/include.sol", + "lib/nested/nested.sol", + "lib/lib.sol", + }; + expectedOptions.input.basePath = "base/"; + expectedOptions.input.includePaths = { + "include/", + "lib/nested", + "lib/", + }; + expectedOptions.formatting.coloredOutput = false; + expectedOptions.modelChecker.initialize = true; + + map expectedSources = { + {"main.sol", mainContractSource}, + {"contract.sol", preamble}, + {"contract_via_callback.sol", preamble}, + {"include.sol", preamble}, + {"include_via_callback.sol", preamble}, + {"nested.sol", preamble}, + {"nested_via_callback.sol", preamble}, + {"lib.sol", preamble}, + {"lib_via_callback.sol", preamble}, + }; + + vector expectedIncludePaths = { + expectedWorkDir / "include/", + expectedWorkDir / "lib/nested", + expectedWorkDir / "lib/", + }; + + FileReader::FileSystemPathSet expectedAllowedDirectories = { + canonicalWorkDir / "base", + canonicalWorkDir / "include", + canonicalWorkDir / "lib/nested", + canonicalWorkDir / "lib", + }; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles( + commandLine, + "", + true /* _processInput */ + ); + + BOOST_TEST(result.stderrContent == ""); + BOOST_TEST(result.stdoutContent == ""); + BOOST_REQUIRE(result.success); + BOOST_TEST(result.options == expectedOptions); + BOOST_TEST(result.reader.sourceCodes() == expectedSources); + BOOST_TEST(result.reader.includePaths() == expectedIncludePaths); + BOOST_TEST(result.reader.allowedDirectories() == expectedAllowedDirectories); + BOOST_TEST(result.reader.basePath() == expectedWorkDir / "base/"); +} + +BOOST_AUTO_TEST_CASE(standard_json_include_paths) +{ + TemporaryDirectory tempDir({"base/", "include/", "lib/nested/"}, TEST_CASE_NAME); + TemporaryWorkingDirectory tempWorkDir(tempDir); + + string const preamble = + "// SPDX-License-Identifier: GPL-3.0\n" + "pragma solidity >=0.0;\n"; + string const mainContractSource = preamble + + "import 'contract_via_callback.sol';\n" + "import 'include_via_callback.sol';\n" + "import 'nested_via_callback.sol';\n" + "import 'lib_via_callback.sol';\n"; + + string const standardJsonInput = R"( + { + "language": "Solidity", + "sources": { + "main.sol": {"content": ")" + mainContractSource + R"("} + } + } + )"; + + createFilesWithParentDirs( + { + tempDir.path() / "base/contract_via_callback.sol", + tempDir.path() / "include/include_via_callback.sol", + tempDir.path() / "lib/nested/nested_via_callback.sol", + tempDir.path() / "lib/lib_via_callback.sol", + }, + preamble + ); + + boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); + + vector commandLine = { + "solc", + "--base-path=base/", + "--include-path=include/", + "--include-path=lib/nested", + "--include-path=lib/", + "--standard-json", + }; + + CommandLineOptions expectedOptions; + expectedOptions.input.mode = InputMode::StandardJson; + expectedOptions.input.paths = {}; + expectedOptions.input.addStdin = true; + expectedOptions.input.basePath = "base/"; + expectedOptions.input.includePaths = { + "include/", + "lib/nested", + "lib/", + }; + expectedOptions.modelChecker.initialize = false; + + // NOTE: Source code from Standard JSON does not end up in FileReader. This is not a problem + // because FileReader is only used once to initialize the compiler stack and after that + // its sources are irrelevant (even though the callback still stores everything it loads). + map expectedSources = { + {"contract_via_callback.sol", preamble}, + {"include_via_callback.sol", preamble}, + {"nested_via_callback.sol", preamble}, + {"lib_via_callback.sol", preamble}, + }; + + vector expectedIncludePaths = { + expectedWorkDir / "include/", + expectedWorkDir / "lib/nested", + expectedWorkDir / "lib/", + }; + + FileReader::FileSystemPathSet expectedAllowedDirectories = {}; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles( + commandLine, + standardJsonInput, + true /* _processInput */ + ); + + Json::Value parsedStdout; + string jsonParsingErrors; + BOOST_TEST(util::jsonParseStrict(result.stdoutContent, parsedStdout, &jsonParsingErrors)); + BOOST_TEST(jsonParsingErrors == ""); + for (Json::Value const& errorDict: parsedStdout["errors"]) + // The error list might contain pre-release compiler warning + BOOST_TEST(errorDict["severity"] != "error"); + BOOST_TEST( + (parsedStdout["sources"].getMemberNames() | ranges::to) == + (expectedSources | ranges::views::keys | ranges::to) + set{"main.sol"} + ); + + BOOST_REQUIRE(result.success); + BOOST_TEST(result.options == expectedOptions); + BOOST_TEST(result.reader.sourceCodes() == expectedSources); + BOOST_TEST(result.reader.includePaths() == expectedIncludePaths); + BOOST_TEST(result.reader.allowedDirectories() == expectedAllowedDirectories); + BOOST_TEST(result.reader.basePath() == expectedWorkDir / "base/"); +} + +BOOST_AUTO_TEST_CASE(cli_include_paths_empty_path) +{ + TemporaryDirectory tempDir({"base/", "include/"}, TEST_CASE_NAME); + TemporaryWorkingDirectory tempWorkDir(tempDir); + createFilesWithParentDirs({tempDir.path() / "base/main.sol"}); + + string expectedMessage = "Empty values are not allowed in --include-path.\n"; + + vector commandLine = { + "solc", + "--base-path=base/", + "--include-path", "include/", + "--include-path", "", + "base/main.sol", + }; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(cli_include_paths_without_base_path) +{ + TemporaryDirectory tempDir(TEST_CASE_NAME); + TemporaryWorkingDirectory tempWorkDir(tempDir); + createFilesWithParentDirs({tempDir.path() / "contract.sol"}); + + string expectedMessage = "--include-path option requires a non-empty base path.\n"; + + vector commandLine = {"solc", "--include-path", "include/", "contract.sol"}; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(!result.success); + BOOST_TEST(result.stderrContent == expectedMessage); +} + +BOOST_AUTO_TEST_CASE(cli_include_paths_should_detect_source_unit_name_collisions) +{ + TemporaryDirectory tempDir({"dir1/", "dir2/", "dir3/"}, TEST_CASE_NAME); + TemporaryWorkingDirectory tempWorkDir(tempDir); + createFilesWithParentDirs({ + "dir1/contract1.sol", + "dir1/contract2.sol", + "dir2/contract1.sol", + "dir2/contract2.sol", + }); + + boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); + + string expectedMessage = + "Source unit name collision detected. " + "The specified values of base path and/or include paths would result in multiple " + "input files being assigned the same source unit name:\n" + "contract1.sol matches: " + "\"" + (expectedWorkDir / "dir1/contract1.sol").generic_string() + "\", " + "\"" + (expectedWorkDir / "dir2/contract1.sol").generic_string() + "\"\n" + "contract2.sol matches: " + "\"" + (expectedWorkDir / "dir1/contract2.sol").generic_string() + "\", " + "\"" + (expectedWorkDir / "dir2/contract2.sol").generic_string() + "\"\n"; + + { + // import "contract1.sol" and import "contract2.sol" would be ambiguous: + vector commandLine = { + "solc", + "--base-path=dir1/", + "--include-path=dir2/", + "dir1/contract1.sol", + "dir2/contract1.sol", + "dir1/contract2.sol", + "dir2/contract2.sol", + }; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(result.stderrContent == expectedMessage); + BOOST_REQUIRE(!result.success); + } + + { + // import "contract1.sol" and import "contract2.sol" would be ambiguous: + vector commandLine = { + "solc", + "--base-path=dir3/", + "--include-path=dir1/", + "--include-path=dir2/", + "dir1/contract1.sol", + "dir2/contract1.sol", + "dir1/contract2.sol", + "dir2/contract2.sol", + }; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(result.stderrContent == expectedMessage); + BOOST_REQUIRE(!result.success); + } + + { + // No conflict if files with the same name exist but only one is given to the compiler. + vector commandLine = { + "solc", + "--base-path=dir3/", + "--include-path=dir1/", + "--include-path=dir2/", + "dir1/contract1.sol", + "dir1/contract2.sol", + }; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(result.stderrContent == ""); + BOOST_REQUIRE(result.success); + } + + { + // The same file specified multiple times is not a conflict. + vector commandLine = { + "solc", + "--base-path=dir3/", + "--include-path=dir1/", + "--include-path=dir2/", + "dir1/contract1.sol", + "dir1/contract1.sol", + "./dir1/contract1.sol", + }; + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(result.stderrContent == ""); + BOOST_REQUIRE(result.success); + } +} + +BOOST_AUTO_TEST_CASE(cli_include_paths_should_allow_duplicate_paths) +{ + TemporaryDirectory tempDir({"dir1/", "dir2/"}, TEST_CASE_NAME); + TemporaryWorkingDirectory tempWorkDir(tempDir); + createFilesWithParentDirs({"dir1/contract.sol"}); + + boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); + boost::filesystem::path expectedTempDir = "/" / tempDir.path().relative_path(); + + vector commandLine = { + "solc", + "--base-path=dir1/", + "--include-path", "dir1", + "--include-path", "dir1", + "--include-path", "dir1/", + "--include-path", "dir1/", + "--include-path", "./dir1/", + "--include-path", "dir2/../dir1/", + "--include-path", (tempDir.path() / "dir1/").string(), + "--include-path", (expectedWorkDir / "dir1/").string(), + "--include-path", "dir1/", + "dir1/contract.sol", + }; + + // Duplicates do not affect the result but are not removed from the include path list. + vector expectedIncludePaths = { + expectedWorkDir / "dir1", + expectedWorkDir / "dir1", + expectedWorkDir / "dir1/", + expectedWorkDir / "dir1/", + expectedWorkDir / "dir1/", + expectedWorkDir / "dir1/", + // NOTE: On macOS expectedTempDir usually contains a symlink and therefore for us it's + // different from expectedWorkDir. + expectedTempDir / "dir1/", + expectedWorkDir / "dir1/", + expectedWorkDir / "dir1/", + }; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); + BOOST_TEST(result.stderrContent == ""); + BOOST_REQUIRE(result.success); + BOOST_TEST(result.reader.includePaths() == expectedIncludePaths); + BOOST_TEST(result.reader.basePath() == expectedWorkDir / "dir1/"); +} + +BOOST_AUTO_TEST_CASE(cli_include_paths_ambiguous_import) +{ + TemporaryDirectory tempDir({"base/", "include/"}, TEST_CASE_NAME); + TemporaryWorkingDirectory tempWorkDir(tempDir); + + string const preamble = + "// SPDX-License-Identifier: GPL-3.0\n" + "pragma solidity >=0.0;\n"; + string const mainContractSource = preamble + + // Ambiguous: both base/contract.sol and include/contract.sol match the import. + "import \"contract.sol\";"; + + createFilesWithParentDirs({"base/contract.sol", "include/contract.sol"}, preamble); + + boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); + + vector commandLine = { + "solc", + "--no-color", + "--base-path=base/", + "--include-path=include/", + "-", + }; + + string expectedMessage = + "Error: Source \"contract.sol\" not found: Ambiguous import. " + "Multiple matching files found inside base path and/or include paths: \"" + + (expectedWorkDir / "base/contract.sol").generic_string() + "\", \"" + + (expectedWorkDir / "include/contract.sol").generic_string() + "\".\n" + " --> :3:1:\n" + " |\n" + "3 | import \"contract.sol\";\n" + " | ^^^^^^^^^^^^^^^^^^^^^^\n\n"; + + OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles( + commandLine, + mainContractSource, + true /* _processInput */ + ); + + BOOST_TEST(result.stderrContent == expectedMessage); + BOOST_REQUIRE(!result.success); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace solidity::frontend::test diff --git a/test/solc/CommandLineInterfaceAllowPaths.cpp b/test/solc/CommandLineInterfaceAllowPaths.cpp new file mode 100644 index 000000000..a15199618 --- /dev/null +++ b/test/solc/CommandLineInterfaceAllowPaths.cpp @@ -0,0 +1,573 @@ +/* + 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 solc/CommandLineInterface.h + +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +using namespace std; +using namespace solidity::frontend; +using namespace solidity::test; + +#define TEST_CASE_NAME (boost::unit_test::framework::current_test_case().p_name) + +namespace +{ + +struct ImportCheck +{ + enum class Result + { + Unknown, ///< Status is unknown due to a failure of the status check. + OK, ///< Passed compilation without errors. + FileNotFound, ///< Error was reported: file not found. + PathDisallowed, ///< Error was reported: file not allowed paths. + }; + + bool operator==(ImportCheck const& _other) const { return result == _other.result && message == _other.message; } + bool operator!=(ImportCheck const& _other) const { return !(*this == _other); } + + operator bool() const { return this->result == Result::OK; } + + static ImportCheck const OK() { return {Result::OK, ""}; } + static ImportCheck const FileNotFound() { return {Result::FileNotFound, ""}; } + static ImportCheck const PathDisallowed() { return {Result::PathDisallowed, ""}; } + static ImportCheck const Unknown(const string& _message) { return {Result::Unknown, _message}; } + + Result result; + std::string message; +}; + +ImportCheck checkImport( + string const& _import, + vector const& _cliOptions +) +{ + soltestAssert(regex_match(_import, regex{R"(import '[^']*')"}), ""); + for (string const& option: _cliOptions) + soltestAssert( + boost::starts_with(option, "--base-path") || + boost::starts_with(option, "--include-path") || + boost::starts_with(option, "--allow-paths") || + !boost::starts_with(option, "--"), + "" + ); + + vector commandLine = { + "solc", + "-", + "--no-color", + "--error-codes", + }; + commandLine += _cliOptions; + + string standardInputContent = + "// SPDX-License-Identifier: GPL-3.0\n" + "pragma solidity >=0.0;\n" + + _import + ";"; + + test::OptionsReaderAndMessages cliResult = test::parseCommandLineAndReadInputFiles( + commandLine, + standardInputContent, + true /* processInput */ + ); + if (cliResult.success) + return ImportCheck::OK(); + + static regex const sourceNotFoundErrorRegex{ + R"(^Error \(6275\): Source ".+" not found: (.*)\.\n)" + R"(\s*--> .*:\d+:\d+:\n)" + R"(\s*\|\n)" + R"(\d+\s*\| import '.+';\n)" + R"(\s*\| \^+\n\s*$)" + }; + + smatch submatches; + if (!regex_match(cliResult.stderrContent, submatches, sourceNotFoundErrorRegex)) + return ImportCheck::Unknown("Unexpected stderr content: '" + cliResult.stderrContent + "'"); + if (submatches[1] != "File not found" && submatches[1] != "File outside of allowed directories") + return ImportCheck::Unknown("Unexpected error message: '" + cliResult.stderrContent + "'"); + + if (submatches[1] == "File not found") + return ImportCheck::FileNotFound(); + else if (submatches[1] == "File outside of allowed directories") + return ImportCheck::PathDisallowed(); + else + return ImportCheck::Unknown("Unexpected error message '" + submatches[1].str() + "'"); +} + +class AllowPathsFixture +{ +protected: + AllowPathsFixture(): + m_tempDir({"code/", "work/"}, TEST_CASE_NAME), + m_tempWorkDir(m_tempDir.path() / "work"), + m_codeDir(m_tempDir.path() / "code"), + m_workDir(m_tempDir.path() / "work"), + m_portablePrefix(("/" / boost::filesystem::canonical(m_codeDir).relative_path()).generic_string()) + { + createFilesWithParentDirs( + { + m_codeDir / "a/b/c/d.sol", + m_codeDir / "a/b/c.sol", + m_codeDir / "a/b/X.sol", + m_codeDir / "a/X/c.sol", + m_codeDir / "X/b/c.sol", + + m_codeDir / "a/bc/d.sol", + m_codeDir / "X/bc/d.sol", + + m_codeDir / "x/y/z.sol", + m_codeDir / "1/2/3.sol", + m_codeDir / "contract.sol", + + m_workDir / "a/b/c/d.sol", + m_workDir / "a/b/c.sol", + m_workDir / "a/b/X.sol", + m_workDir / "a/X/c.sol", + m_workDir / "X/b/c.sol", + + m_workDir / "contract.sol", + }, + "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0;" + ); + + if ( + !createSymlinkIfSupportedByFilesystem("b", m_codeDir / "a/b_sym", true) || + !createSymlinkIfSupportedByFilesystem("../x/y", m_codeDir / "a/y_sym", true) || + !createSymlinkIfSupportedByFilesystem("../../a/b/c.sol", m_codeDir / "a/b/c_sym.sol", false) || + !createSymlinkIfSupportedByFilesystem("../../x/y/z.sol", m_codeDir / "a/b/z_sym.sol", false) + ) + return; + + m_caseSensitiveFilesystem = boost::filesystem::create_directories(m_codeDir / "A/B/C"); + soltestAssert(boost::filesystem::equivalent(m_codeDir / "a/b/c", m_codeDir / "A/B/C") != m_caseSensitiveFilesystem, ""); + } + + TemporaryDirectory m_tempDir; + TemporaryWorkingDirectory m_tempWorkDir; + boost::filesystem::path const m_codeDir; + boost::filesystem::path const m_workDir; + string m_portablePrefix; + bool m_caseSensitiveFilesystem = true; +}; + +ostream& operator<<(ostream& _out, ImportCheck const& _value) +{ + switch (_value.result) + { + case ImportCheck::Result::Unknown: _out << "Unknown"; break; + case ImportCheck::Result::OK: _out << "OK"; break; + case ImportCheck::Result::FileNotFound: _out << "FileNotFound"; break; + case ImportCheck::Result::PathDisallowed: _out << "PathDisallowed"; break; + } + if (_value.message != "") + _out << "(" << _value.message << ")"; + return _out; +} + +} // namespace + +namespace boost::test_tools::tt_detail +{ + +// Boost won't find the << operator unless we put it in the std namespace which is illegal. +// The recommended solution is to overload print_log_value<> struct and make it use our operator. + +template<> +struct print_log_value +{ + void operator()(std::ostream& _out, ImportCheck const& _value) { ::operator<<(_out, _value); } +}; + +} // namespace boost::test_tools::tt_detail + +namespace solidity::frontend::test +{ + +BOOST_AUTO_TEST_SUITE(CommandLineInterfaceAllowPathsTest) + +BOOST_FIXTURE_TEST_CASE(allow_path_multiple_paths, AllowPathsFixture) +{ + string allowedPaths = + m_codeDir.generic_string() + "/a/b/X.sol," + + m_codeDir.generic_string() + "/X/," + + m_codeDir.generic_string() + "/z," + + m_codeDir.generic_string() + "/a/b"; + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths", allowedPaths})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"--allow-paths", allowedPaths})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"--allow-paths", allowedPaths}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"--allow-paths", allowedPaths})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_should_work_with_various_path_forms, AllowPathsFixture) +{ + string import = "import '" + m_portablePrefix + "/a/b/c.sol'"; + + // Without --allow-path + BOOST_TEST(checkImport(import, {}) == ImportCheck::PathDisallowed()); + + // Absolute paths allowed + BOOST_TEST(checkImport(import, {"--allow-paths", m_codeDir.string()})); + BOOST_TEST(checkImport(import, {"--allow-paths", m_codeDir.string() + "/a"})); + BOOST_TEST(checkImport(import, {"--allow-paths", m_codeDir.string() + "/a/"})); + BOOST_TEST(checkImport(import, {"--allow-paths", m_codeDir.string() + "/a/b"})); + BOOST_TEST(checkImport(import, {"--allow-paths", m_codeDir.string() + "/a/b/c.sol"})); + + // Relative paths allowed + BOOST_TEST(checkImport(import, {"--allow-paths=../code/a"})); + BOOST_TEST(checkImport(import, {"--allow-paths=../code/a/"})); + BOOST_TEST(checkImport(import, {"--allow-paths=../code/a/b"})); + BOOST_TEST(checkImport(import, {"--allow-paths=../code/a/b/c.sol"})); + + // Non-normalized paths allowed + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/."})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/./"})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a/.."})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a/../"})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a/b"})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a/./b"})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a/../a/b"})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a///b"})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a/b//"})); + BOOST_TEST(checkImport(import, {"--allow-paths", "./../code/a/b///"})); + + // Root path allowed + BOOST_TEST(checkImport(import, {"--allow-paths=/"})); + BOOST_TEST(checkImport(import, {"--allow-paths=///"})); + + // UNC paths should be treated differently from normal paths + soltestAssert(FileReader::isUNCPath("/" + m_portablePrefix), ""); + BOOST_TEST(checkImport(import, {"--allow-paths=//"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport(import, {"--allow-paths=/" + m_portablePrefix}) == ImportCheck::PathDisallowed()); + + // Paths going beyond root allowed + BOOST_TEST(checkImport(import, {"--allow-paths=/../../"})); + BOOST_TEST(checkImport(import, {"--allow-paths=/../.."})); + BOOST_TEST(checkImport(import, {"--allow-paths=/../../a/../"})); + BOOST_TEST(checkImport(import, {"--allow-paths=/../../" + m_portablePrefix})); + + // File named like a directory + BOOST_TEST(checkImport(import, {"--allow-paths", m_codeDir.string() + "/a/b/c.sol/"})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_should_handle_empty_paths, AllowPathsFixture) +{ + // Work dir is base path + BOOST_TEST(checkImport("import 'a/../../work/a/b/c.sol'", {"--allow-paths", ""})); + BOOST_TEST(checkImport("import 'a/../../work/a/b/c.sol'", {"--allow-paths", "x,,y"})); + BOOST_TEST(checkImport("import 'a/../../code/a/b/c.sol'", {"--allow-paths", ""}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import 'a/../../code/a/b/c.sol'", {"--allow-paths", "x,,y"}) == ImportCheck::PathDisallowed()); + + // Work dir is not base path + BOOST_TEST(checkImport("import 'a/../../work/a/b/c.sol'", {"--allow-paths", "", "--base-path=../code/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import 'a/../../work/a/b/c.sol'", {"--allow-paths", "x,,y", "--base-path=../code/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import 'a/../../code/a/b/c.sol'", {"--allow-paths", "", "--base-path=../code/"})); + BOOST_TEST(checkImport("import 'a/../../code/a/b/c.sol'", {"--allow-paths", "x,,y", "--base-path=../code/"})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_case_sensitive, AllowPathsFixture) +{ + // Allowed paths are case-sensitive even on case-insensitive filesystems + BOOST_TEST( + checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths", m_codeDir.string() + "/A/B/"}) == + ImportCheck::PathDisallowed() + ); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_should_work_with_various_import_forms, AllowPathsFixture) +{ + // Absolute import paths + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths", "../code/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"--allow-paths", "../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"--allow-paths", "../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"--allow-paths", "../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths", "../code/a/b/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"--allow-paths", "../code/a/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"--allow-paths", "../code/a/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"--allow-paths", "../code/a/b/"})); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths", "../code/a/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"--allow-paths", "../code/a/b"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"--allow-paths", "../code/a/b"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"--allow-paths", "../code/a/b"})); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths", "../code/a"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"--allow-paths", "../code/a"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"--allow-paths", "../code/a"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"--allow-paths", "../code/a"})); + + // Relative import paths + // NOTE: Base path is whitelisted by default so we need the 'a/../../code/' part to get + // outside of it. And it can't be just '../code/' because that would not be a direct import. + BOOST_TEST(checkImport("import 'a/../../code/a/b/c.sol'", {"--allow-paths", "../code/a/b"})); + BOOST_TEST(checkImport("import 'a/../../code/X/b/c.sol'", {"--allow-paths", "../code/a/b"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import 'a/../../code/a/X/c.sol'", {"--allow-paths", "../code/a/b"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import 'a/../../code/a/b/X.sol'", {"--allow-paths", "../code/a/b"})); + + // Non-normalized relative import paths + BOOST_TEST(checkImport("import 'a/../../code/a/./b/c.sol'", {"--allow-paths", "../code/a/b/c.sol"})); + BOOST_TEST(checkImport("import 'a/../../code/a/../a/b/c.sol'", {"--allow-paths", "../code/a/b/c.sol"})); + BOOST_TEST(checkImport("import 'a/../../code/a///b/c.sol'", {"--allow-paths", "../code/a/b/c.sol"})); + +#if !defined(_WIN32) + // UNC paths in imports. + // Unfortunately can't test it on Windows without having an existing UNC path. On Linux we can + // at least rely on the fact that `//` works like `/`. + string uncImportPath = "/" + m_portablePrefix + "/a/b/c.sol"; + soltestAssert(FileReader::isUNCPath(uncImportPath), ""); + BOOST_TEST(checkImport("import '" + uncImportPath + "'", {"--allow-paths", "../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); +#endif +} + +BOOST_FIXTURE_TEST_CASE(allow_path_automatic_whitelisting_input_files, AllowPathsFixture) +{ + // By default none of the files is whitelisted + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c/d.sol'", {}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {}) == ImportCheck::PathDisallowed()); + + // Compiling a file whitelists its directory and subdirectories + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {m_codeDir.string() + "/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c/d.sol'", {m_codeDir.string() + "/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {m_codeDir.string() + "/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {m_codeDir.string() + "/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {m_codeDir.string() + "/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {m_codeDir.string() + "/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + + // If only file name is specified, its parent dir path is empty. This should be equivalent to + // whitelisting the work dir. + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/contract.sol'", {"contract.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import 'contract.sol'", {"contract.sol"})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_automatic_whitelisting_remappings, AllowPathsFixture) +{ + // Adding a remapping whitelists target's parent directory and subdirectories + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../code/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c/d.sol'", {"x=../code/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"x=../code/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"x=../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {"x=../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=/contract.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=/contract.sol/"}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {m_portablePrefix + "/a/b=../code/X/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {m_portablePrefix + "/a/b/=../code/X/b/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {m_portablePrefix + "/a/b=../code/X/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {m_portablePrefix + "/a/b/=../code/X/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {m_portablePrefix + "/a/b:y/z=x/w"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {m_portablePrefix + "/a/b:y/z=x/w"}) == ImportCheck::PathDisallowed()); + + // Adding a remapping whitelists the target and subdirectories when the target is a directory + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../code/a/b/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c/d.sol'", {"x=../code/a/b/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"x=../code/a/b/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"x=../code/a/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../code/a/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {"x=../code/a/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {"x=../code/a/c/"}) == ImportCheck::PathDisallowed()); + + // Adding a remapping whitelists target's parent directory and subdirectories when the target + // is a directory but does not have a trailing slash + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../code/a/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c/d.sol'", {"x=../code/a/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/X.sol'", {"x=../code/a/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"x=../code/a/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../code/a/b"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {"x=../code/a/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/bc/d.sol'", {"x=../code/a/c"})); + + // Adding a remapping to a relative target at VFS root whitelists the work dir + BOOST_TEST(checkImport("import '/../../x/y/z.sol'", {"x=contract.sol", "--base-path=../code/a/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '/../../../work/a/b/c.sol'", {"x=contract.sol", "--base-path=../code/a/b/"})); + + BOOST_TEST(checkImport("import '/../../x/y/z.sol'", {"x=contract.sol/", "--base-path=../code/a/b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '/../../../work/a/b/c.sol'", {"x=contract.sol/", "--base-path=../code/a/b/"}) == ImportCheck::PathDisallowed()); + + // Adding a remapping with an empty target does not whitelist anything + BOOST_TEST(checkImport("import '" + m_portablePrefix + m_portablePrefix + "/a/b/c.sol'", {m_portablePrefix + "="}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"../code/="}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '/../work/a/b/c.sol'", {"../code/=", "--base-path", m_portablePrefix}) == ImportCheck::PathDisallowed()); + + // Adding a remapping that includes .. or . segments whitelists the parent dir and subdirectories + // of the resolved target + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=."}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=."}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=./"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=./"}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=.."})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=.."})); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../"})); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../code/a/b/./.."})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"x=../code/a/b/./.."})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../code/a/b/./.."}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../code/a/b/./../"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"x=../code/a/b/./../"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../code/a/b/./../"}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../code/a/b/./../b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"x=../code/a/b/./../b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../code/a/b/./../b"}) == ImportCheck::PathDisallowed()); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"x=../code/a/b/./../b/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/X/c.sol'", {"x=../code/a/b/./../b/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/X/b/c.sol'", {"x=../code/a/b/./../b/"}) == ImportCheck::PathDisallowed()); + + // If the target is just a file name, its parent dir path is empty. This should be equivalent to + // whitelisting the work dir. + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/contract.sol'", {"x=contract.sol"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import 'contract.sol'", {"x=contract.sol"})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_automatic_whitelisting_base_path, AllowPathsFixture) +{ + // Relative base path whitelists its content + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path=../code/a"})); + BOOST_TEST(checkImport("import 'b/c/d.sol'", {"--base-path=../code/a"})); + BOOST_TEST(checkImport("import 'b/X.sol'", {"--base-path=../code/a"})); + BOOST_TEST(checkImport("import 'X/c.sol'", {"--base-path=../code/a"})); + + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path=../code/a/"})); + BOOST_TEST(checkImport("import 'b/c/d.sol'", {"--base-path=../code/a/"})); + BOOST_TEST(checkImport("import 'b/X.sol'", {"--base-path=../code/a/"})); + BOOST_TEST(checkImport("import 'X/c.sol'", {"--base-path=../code/a/"})); + + BOOST_TEST(checkImport("import 'a/b/c.sol'", {"--base-path=../code/."})); + BOOST_TEST(checkImport("import 'a/b/c.sol'", {"--base-path=../code/./"})); + BOOST_TEST(checkImport("import 'code/a/b/c.sol'", {"--base-path=.."})); + BOOST_TEST(checkImport("import 'code/a/b/c.sol'", {"--base-path=../"})); + + // Absolute base path whitelists its content + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path", m_codeDir.string() + "/a"})); + BOOST_TEST(checkImport("import 'b/c/d.sol'", {"--base-path", m_codeDir.string() + "/a"})); + BOOST_TEST(checkImport("import 'b/X.sol'", {"--base-path", m_codeDir.string() + "/a"})); + BOOST_TEST(checkImport("import 'X/c.sol'", {"--base-path", m_codeDir.string() + "/a"})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_automatic_whitelisting_work_dir, AllowPathsFixture) +{ + // Work dir is only automatically whitelisted if it matches base path + BOOST_TEST(checkImport("import 'b/../../../work/a/b/c.sol'", {"--base-path=../code/a/"}) == ImportCheck::PathDisallowed()); + + // Compiling a file in the work dir whitelists it even if it's not in base path + BOOST_TEST(checkImport("import 'b/../../../work/a/b/c.sol'", {"--base-path", "../code/a/", "a/b/c.sol"})); + + // Work dir can also be whitelisted manually + BOOST_TEST(checkImport("import 'b/../../../work/a/b/c.sol'", {"--base-path", "../code/a/", "--allow-paths=."})); + + // Not setting base path whitelists the working directory + BOOST_TEST(checkImport("import 'a/b/c.sol'", {})); + BOOST_TEST(checkImport("import 'a/b/c/d.sol'", {})); + BOOST_TEST(checkImport("import 'a/b/X.sol'", {})); + BOOST_TEST(checkImport("import 'a/X/c.sol'", {})); + + // Setting base path to an empty value whitelists the working directory + BOOST_TEST(checkImport("import 'a/b/c.sol'", {"--base-path", ""})); + BOOST_TEST(checkImport("import 'a/b/c/d.sol'", {"--base-path", ""})); + BOOST_TEST(checkImport("import 'a/b/X.sol'", {"--base-path", ""})); + BOOST_TEST(checkImport("import 'a/X/c.sol'", {"--base-path", ""})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_automatic_whitelisting_include_paths, AllowPathsFixture) +{ + // Relative include path whitelists its content + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path=a/b/c", "--include-path=../code/a"})); + BOOST_TEST(checkImport("import 'b/c/d.sol'", {"--base-path=a/b/c", "--include-path=../code/a"})); + BOOST_TEST(checkImport("import 'b/X.sol'", {"--base-path=a/b/c", "--include-path=../code/a"})); + BOOST_TEST(checkImport("import 'X/c.sol'", {"--base-path=a/b/c", "--include-path=../code/a"})); + + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path=a/b/c", "--include-path=../code/a/"})); + BOOST_TEST(checkImport("import 'b/c/d.sol'", {"--base-path=a/b/c", "--include-path=../code/a/"})); + BOOST_TEST(checkImport("import 'b/X.sol'", {"--base-path=a/b/c", "--include-path=../code/a/"})); + BOOST_TEST(checkImport("import 'X/c.sol'", {"--base-path=a/b/c", "--include-path=../code/a/"})); + + BOOST_TEST(checkImport("import 'a/b/c.sol'", {"--base-path=a/b/c", "--include-path=../code/."})); + BOOST_TEST(checkImport("import 'a/b/c.sol'", {"--base-path=a/b/c", "--include-path=../code/./"})); + BOOST_TEST(checkImport("import 'code/a/b/c.sol'", {"--base-path=a/b/c", "--include-path=.."})); + BOOST_TEST(checkImport("import 'code/a/b/c.sol'", {"--base-path=a/b/c", "--include-path=../"})); + + // Absolute include path whitelists its content + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path=a/b/c", "--include-path", m_codeDir.string() + "/a"})); + BOOST_TEST(checkImport("import 'b/c/d.sol'", {"--base-path=a/b/c", "--include-path", m_codeDir.string() + "/a"})); + BOOST_TEST(checkImport("import 'b/X.sol'", {"--base-path=a/b/c", "--include-path", m_codeDir.string() + "/a"})); + BOOST_TEST(checkImport("import 'X/c.sol'", {"--base-path=a/b/c", "--include-path", m_codeDir.string() + "/a"})); + + // If there are multiple include paths, all of them get whitelisted + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path=a/b/c", "--include-path=../code/a", "--include-path=../code/1"})); + BOOST_TEST(checkImport("import '2/3.sol'", {"--base-path=a/b/c", "--include-path=../code/a", "--include-path=../code/1"})); + BOOST_TEST(checkImport("import 'b/c.sol'", {"--base-path=a/b/c", "--include-path=../code/1", "--include-path=../code/a"})); + BOOST_TEST(checkImport("import '2/3.sol'", {"--base-path=a/b/c", "--include-path=../code/1", "--include-path=../code/a"})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_symlinks_within_whitelisted_dir, AllowPathsFixture) +{ + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b_sym/c.sol'", {"--allow-paths=../code/a/b/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths=../code/a/b_sym/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b_sym/c.sol'", {"--allow-paths=../code/a/b_sym/"})); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b_sym/c.sol'", {"--allow-paths=../code/a/b"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths=../code/a/b_sym"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b_sym/c.sol'", {"--allow-paths=../code/a/b_sym"})); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c_sym.sol'", {"--allow-paths=../code/a/b/c.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c.sol'", {"--allow-paths=../code/a/b/c_sym.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/c_sym.sol'", {"--allow-paths=../code/a/b/c_sym.sol"})); +} + +BOOST_FIXTURE_TEST_CASE(allow_path_symlinks_outside_whitelisted_dir, AllowPathsFixture) +{ + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/y_sym/z.sol'", {"--allow-paths=../code/a/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/y_sym/z.sol'", {"--allow-paths=../code/x/"})); + + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/z_sym.sol'", {"--allow-paths=../code/a/"}) == ImportCheck::PathDisallowed()); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/z_sym.sol'", {"--allow-paths=../code/x/"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/z_sym.sol'", {"--allow-paths=../code/a/b/z_sym.sol"})); + BOOST_TEST(checkImport("import '" + m_portablePrefix + "/a/b/z_sym.sol'", {"--allow-paths=../code/x/y/z.sol"})); +} + +BOOST_AUTO_TEST_SUITE_END() + +} // namespace solidity::frontend::test diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 1e3a53162..7b1dd375b 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -117,6 +117,8 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "a:b=c/d", ":contract.sol=", "--base-path=/home/user/", + "--include-path=/usr/lib/include/", + "--include-path=/home/user/include", "--allow-paths=/tmp,/home,project,../contracts", "--ignore-missing", "--error-recovery", @@ -168,7 +170,9 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) expectedOptions.input.addStdin = true; expectedOptions.input.basePath = "/home/user/"; - expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "", "c", "/usr/lib"}; + expectedOptions.input.includePaths = {"/usr/lib/include/", "/home/user/include"}; + + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "c", "/usr/lib"}; expectedOptions.input.ignoreMissingFiles = true; expectedOptions.input.errorRecovery = (inputMode == InputMode::Compiler); expectedOptions.output.dir = "/tmp/out"; @@ -257,6 +261,8 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) "a:b=c/d", ":contract.yul=", "--base-path=/home/user/", + "--include-path=/usr/lib/include/", + "--include-path=/home/user/include", "--allow-paths=/tmp,/home,project,../contracts", "--ignore-missing", "--error-recovery", // Ignored in assembly mode @@ -307,7 +313,8 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) }; expectedOptions.input.addStdin = true; expectedOptions.input.basePath = "/home/user/"; - expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "", "c", "/usr/lib"}; + expectedOptions.input.includePaths = {"/usr/lib/include/", "/home/user/include"}; + expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts", "c", "/usr/lib"}; expectedOptions.input.ignoreMissingFiles = true; expectedOptions.output.overwriteFiles = true; expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); @@ -350,6 +357,8 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) "input.json", "--standard-json", "--base-path=/home/user/", + "--include-path=/usr/lib/include/", + "--include-path=/home/user/include", "--allow-paths=/tmp,/home,project,../contracts", "--ignore-missing", "--error-recovery", // Ignored in Standard JSON mode @@ -368,9 +377,6 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) "--combined-json=abi,bin", // Accepted but has no effect in Standard JSON mode "--metadata-hash=swarm", // Ignored in Standard JSON mode "--metadata-literal", // Ignored in Standard JSON mode - "--optimize", // Ignored in Standard JSON mode - "--optimize-runs=1000", // Ignored in Standard JSON mode - "--yul-optimizations=agf", "--model-checker-contracts=" // Ignored in Standard JSON mode "contract1.yul:A," "contract2.yul:B", @@ -393,6 +399,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) expectedOptions.input.mode = InputMode::StandardJson; expectedOptions.input.paths = {"input.json"}; expectedOptions.input.basePath = "/home/user/"; + expectedOptions.input.includePaths = {"/usr/lib/include/", "/home/user/include"}; expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts"}; expectedOptions.input.ignoreMissingFiles = true; expectedOptions.output.dir = "/tmp/out"; diff --git a/test/solc/Common.cpp b/test/solc/Common.cpp index 1bd209067..28263e21e 100644 --- a/test/solc/Common.cpp +++ b/test/solc/Common.cpp @@ -54,5 +54,22 @@ test::OptionsReaderAndMessages test::parseCommandLineAndReadInputFiles( if (success && _processInput) success = cli.processInput(); - return {success, cli.options(), cli.fileReader(), cli.standardJsonInput(), sout.str(), serr.str()}; + return { + success, + cli.options(), + cli.fileReader(), + cli.standardJsonInput(), + sout.str(), + stripPreReleaseWarning(serr.str()), + }; +} + +string test::stripPreReleaseWarning(string const& _stderrContent) +{ + static regex const preReleaseWarningRegex{ + R"(Warning( \(3805\))?: This is a pre-release compiler version, please do not use it in production\.\n)" + R"((\n)?)" + }; + + return regex_replace(_stderrContent, preReleaseWarningRegex, ""); } diff --git a/test/solc/Common.h b/test/solc/Common.h index 6d5e3875d..958d63054 100644 --- a/test/solc/Common.h +++ b/test/solc/Common.h @@ -50,4 +50,6 @@ OptionsReaderAndMessages parseCommandLineAndReadInputFiles( bool _processInput = false ); +std::string stripPreReleaseWarning(std::string const& _stderrContent); + } // namespace solidity::frontend::test diff --git a/test/stopAfterParseTests.sh b/test/stopAfterParseTests.sh index 8df15e842..a100146b0 100755 --- a/test/stopAfterParseTests.sh +++ b/test/stopAfterParseTests.sh @@ -18,7 +18,7 @@ cd "$FILETMP" || exit 1 function testFile() { set +e - ALLOUTPUT=$($SOLC --combined-json ast,compact-format --pretty-json "$@" --stop-after parsing 2>&1) + ALLOUTPUT=$($SOLC --combined-json ast --pretty-json "$@" --stop-after parsing 2>&1) local RESULT=$? set -e if test ${RESULT} -ne 0; then diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 1f62b57fc..18b04afe8 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -38,6 +38,7 @@ add_executable(isoltest ../libyul/FunctionSideEffects.cpp ../libyul/ObjectCompilerTest.cpp ../libyul/SyntaxTest.cpp + ../libyul/StackLayoutGeneratorTest.cpp ../libyul/YulOptimizerTest.cpp ../libyul/YulOptimizerTestCommon.cpp ../libyul/YulInterpreterTest.cpp diff --git a/test/tools/IsolTestOptions.cpp b/test/tools/IsolTestOptions.cpp index aaa087e47..7f00e88f6 100644 --- a/test/tools/IsolTestOptions.cpp +++ b/test/tools/IsolTestOptions.cpp @@ -56,14 +56,21 @@ std::string editorPath() } -IsolTestOptions::IsolTestOptions(std::string* _editor): +IsolTestOptions::IsolTestOptions(): CommonOptions(description) { + enforceViaYul = true; + enforceGasTest = (evmVersion() == langutil::EVMVersion{}); +} + +void IsolTestOptions::addOptions() +{ + CommonOptions::addOptions(); options.add_options() - ("editor", po::value(_editor)->default_value(editorPath()), "Path to editor for opening test files.") - ("help", po::bool_switch(&showHelp), "Show this help screen.") - ("no-color", po::bool_switch(&noColor), "Don't use colors.") - ("accept-updates", po::bool_switch(&acceptUpdates), "Automatically accept expectation updates.") + ("editor", po::value(&editor)->default_value(editorPath()), "Path to editor for opening test files.") + ("help", po::bool_switch(&showHelp)->default_value(showHelp), "Show this help screen.") + ("no-color", po::bool_switch(&noColor)->default_value(noColor), "Don't use colors.") + ("accept-updates", po::bool_switch(&acceptUpdates)->default_value(acceptUpdates), "Automatically accept expectation updates.") ("test,t", po::value(&testFilter)->default_value("*/*"), "Filters which test units to include."); } @@ -76,15 +83,13 @@ bool IsolTestOptions::parse(int _argc, char const* const* _argv) std::cout << options << std::endl; return false; } - enforceViaYul = true; - enforceGasTest = (evmVersion() == langutil::EVMVersion{}); - enforceGasTestMinValue = 100000; return res; } void IsolTestOptions::validate() const { + CommonOptions::validate(); static std::string filterString{"[a-zA-Z0-9_/*]*"}; static std::regex filterExpression{filterString}; assertThrow( diff --git a/test/tools/IsolTestOptions.h b/test/tools/IsolTestOptions.h index aa9eb2564..5bb02c2bc 100644 --- a/test/tools/IsolTestOptions.h +++ b/test/tools/IsolTestOptions.h @@ -33,8 +33,10 @@ struct IsolTestOptions: CommonOptions bool noColor = false; bool acceptUpdates = false; std::string testFilter = std::string{}; + std::string editor = std::string{}; - IsolTestOptions(std::string* _editor); + explicit IsolTestOptions(); + void addOptions() override; bool parse(int _argc, char const* const* _argv) override; void validate() const override; }; diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index 5a1849ea6..332b9fc04 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -78,7 +78,7 @@ void FuzzerUtil::testCompilerJsonInterface(string const& _input, bool _optimize, void FuzzerUtil::forceSMT(StringMap& _input) { // Add SMT checker pragma if not already present in source - static const char* smtPragma = "pragma experimental SMTChecker;"; + static auto constexpr smtPragma = "pragma experimental SMTChecker;"; for (auto &sourceUnit: _input) if (sourceUnit.second.find(smtPragma) == string::npos) sourceUnit.second += smtPragma; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index fa7b914aa..73adbc62b 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -121,8 +121,6 @@ public: fs::path const& _basepath, fs::path const& _path ); - - static string editor; private: enum class Request { @@ -145,7 +143,6 @@ private: static bool m_exitRequested; }; -string TestTool::editor; bool TestTool::m_exitRequested = false; TestTool::Result TestTool::process() @@ -258,7 +255,7 @@ TestTool::Request TestTool::handleResponse(bool _exception) } case 'e': cout << endl << endl; - if (system((TestTool::editor + " \"" + m_path.string() + "\"").c_str())) + if (system((m_options.editor + " \"" + m_path.string() + "\"").c_str())) cerr << "Error running editor command." << endl << endl; return Request::Rerun; case 'q': @@ -425,7 +422,7 @@ int main(int argc, char const *argv[]) setupTerminal(); { - auto options = std::make_unique(&TestTool::editor); + auto options = std::make_unique(); if (!options->parse(argc, argv)) return -1; diff --git a/test/tools/ossfuzz/YulEvmoneInterface.cpp b/test/tools/ossfuzz/YulEvmoneInterface.cpp index fe737993d..e21a881c6 100644 --- a/test/tools/ossfuzz/YulEvmoneInterface.cpp +++ b/test/tools/ossfuzz/YulEvmoneInterface.cpp @@ -29,7 +29,7 @@ bytes YulAssembler::assemble() !m_stack.parseAndAnalyze("source", m_yulProgram) || !m_stack.parserResult()->code || !m_stack.parserResult()->analysisInfo || - !langutil::Error::containsOnlyWarnings(m_stack.errors()) + langutil::Error::containsErrors(m_stack.errors()) ) yulAssert(false, "Yul program could not be parsed successfully."); diff --git a/test/tools/ossfuzz/protoToAbiV2.h b/test/tools/ossfuzz/protoToAbiV2.h index a2be558e8..83246ac53 100644 --- a/test/tools/ossfuzz/protoToAbiV2.h +++ b/test/tools/ossfuzz/protoToAbiV2.h @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -505,7 +506,7 @@ public: // Note: Don't change HexPrefix::Add. See comment in fixedByteValueAsString(). static std::string maskUnsignedIntToHex(unsigned _counter, unsigned _numMaskNibbles) { - return toHex(maskUnsignedInt(_counter, _numMaskNibbles), util::HexPrefix::Add); + return "0x" + toHex(maskUnsignedInt(_counter, _numMaskNibbles)); } /// Dynamically sized arrays can have a length of at least zero diff --git a/test/tools/ossfuzz/yulProtoFuzzer.cpp b/test/tools/ossfuzz/yulProtoFuzzer.cpp index a3b4e14ee..7118a0596 100644 --- a/test/tools/ossfuzz/yulProtoFuzzer.cpp +++ b/test/tools/ossfuzz/yulProtoFuzzer.cpp @@ -72,7 +72,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) !stack.parseAndAnalyze("source", yul_source) || !stack.parserResult()->code || !stack.parserResult()->analysisInfo || - !Error::containsOnlyWarnings(stack.errors()) + Error::containsErrors(stack.errors()) ) yulAssert(false, "Proto fuzzer generated malformed program"); diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 1fe4e6eaa..abb0df82f 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -71,7 +71,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) !stack.parseAndAnalyze("source", yul_source) || !stack.parserResult()->code || !stack.parserResult()->analysisInfo || - !Error::containsOnlyWarnings(stack.errors()) + Error::containsErrors(stack.errors()) ) { SourceReferenceFormatter formatter(std::cout, stack, false, false); diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 476c95357..1e030d098 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -29,6 +29,9 @@ #include #include +#include + +#include using namespace std; using namespace solidity; @@ -511,7 +514,7 @@ void EVMInstructionInterpreter::logTrace(std::string const& _pseudoInstruction, { string message = _pseudoInstruction + "("; for (size_t i = 0; i < _arguments.size(); ++i) - message += (i > 0 ? ", " : "") + util::formatNumber(_arguments[i]); + message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]); message += ")"; if (!_data.empty()) message += " [" + util::toHex(_data) + "]"; diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index 25f9f2519..2da5b45e5 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -24,6 +24,7 @@ #include #include +#include #include diff --git a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp index 776c1c305..25349f5d1 100644 --- a/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp +++ b/test/tools/yulInterpreter/EwasmBuiltinInterpreter.cpp @@ -29,6 +29,9 @@ #include #include +#include + +#include using namespace std; using namespace solidity; @@ -601,7 +604,7 @@ void EwasmBuiltinInterpreter::logTrace(std::string const& _pseudoInstruction, st { string message = _pseudoInstruction + "("; for (size_t i = 0; i < _arguments.size(); ++i) - message += (i > 0 ? ", " : "") + util::formatNumber(_arguments[i]); + message += (i > 0 ? ", " : "") + formatNumber(_arguments[i]); message += ")"; if (!_data.empty()) message += " [" + util::toHex(_data) + "]"; diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 357311595..752aaafa6 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -118,7 +118,7 @@ public: size_t _columns ) { - yulAssert(_columns > 0, ""); + yulAssert(_columns > 0); auto hasShorterString = [](auto const& a, auto const& b) { return a.second.size() < b.second.size(); }; size_t longestDescriptionLength = std::max( @@ -151,7 +151,7 @@ public: ); }); - yulAssert(sortedOptions.size() > 0, ""); + yulAssert(sortedOptions.size() > 0); size_t rows = (sortedOptions.size() - 1) / _columns + 1; for (size_t row = 0; row < rows; ++row) { diff --git a/tools/solidityUpgrade/UpgradeChange.cpp b/tools/solidityUpgrade/UpgradeChange.cpp index 2976efe5d..ce1627d0a 100644 --- a/tools/solidityUpgrade/UpgradeChange.cpp +++ b/tools/solidityUpgrade/UpgradeChange.cpp @@ -19,6 +19,7 @@ #include #include +#include using namespace std; using namespace solidity;